Compare commits

..

25 Commits

Author SHA1 Message Date
lizzie 825a6b30ad prepare 2026-06-27 17:56:39 +00:00
lizzie 5e2c294d7b build adjustments 2026-06-27 14:38:33 +00:00
lizzie e2e2a4ee9c ok no broken make 2026-06-27 14:38:33 +00:00
lizzie dfd23821d7 boost asio fix in android 2026-06-27 14:38:25 +00:00
lizzie d1761a1331 it loads the fucker :) 2026-06-27 14:38:25 +00:00
lizzie 8005c11b40 BUT PROPERLY SHOW THE LOGS for fucks sake 2026-06-27 14:38:25 +00:00
lizzie bcfab23498 force services into guest process and singlecore option 2026-06-27 14:38:25 +00:00
lizzie 27ab01fd1d add filter options 2026-06-27 14:38:25 +00:00
lizzie de6e0ffd4c --null-renderer 2026-06-27 14:38:25 +00:00
lizzie 6e32151d9c emscripten friendly sdl loop 2026-06-27 14:38:25 +00:00
lizzie 518971f4eb fixup make program 2026-06-27 14:38:25 +00:00
lizzie 46433279af fixup caveats + add miniserver 2026-06-27 14:38:25 +00:00
lizzie 47822d1606 dont make the browser hang 2026-06-27 14:38:25 +00:00
lizzie 8462bd501e 10gb of ram 2026-06-27 14:38:25 +00:00
lizzie 11280e18d3 small writeup of wasm 2026-06-27 14:38:25 +00:00
lizzie e5fd0b8ef5 fix openssl 2026-06-27 14:38:25 +00:00
lizzie e8246f42b2 proper jthread support 2026-06-27 14:38:25 +00:00
lizzie d8cf5caeac fix wasm flags 2026-06-27 14:38:25 +00:00
lizzie 219920fbd0 disable JIT service 2026-06-27 14:38:25 +00:00
lizzie 5acadadaa2 emscripten can't memfd 2026-06-27 14:38:25 +00:00
lizzie 7bf1ed8ce6 emscripten doesnt have arc4random but wasi does? 2026-06-27 14:38:25 +00:00
lizzie 4b3b735486 fix non existant pthread funcs 2026-06-27 14:38:24 +00:00
lizzie 4a891bee92 initial wasm support 2026-06-27 14:38:22 +00:00
lizzie e7a9c4af3e ok fixup but better with bool returns 2026-06-27 14:37:17 +00:00
lizzie 936e5b56eb Use managarm special fastmem fallback 2026-06-27 14:37:17 +00:00
129 changed files with 1519 additions and 1049 deletions
+92
View File
@@ -0,0 +1,92 @@
#!/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
@@ -0,0 +1,40 @@
diff --git a/cmake/ConfigureOpenSSL.cmake b/cmake/ConfigureOpenSSL.cmake
index 3012e05..2ae23ff 100644
--- a/cmake/ConfigureOpenSSL.cmake
+++ b/cmake/ConfigureOpenSSL.cmake
@@ -108,7 +108,8 @@ function(configure_openssl)
)
if(NOT "${CONFIGURE_OPTIONS_OLD}" STREQUAL "")
- if(CONFIGURE_OPTIONS STREQUAL CONFIGURE_OPTIONS_OLD)
+ # TODO(lizzie): Emscripten has issues with rebuilding due to the wrapper it uses
+ if(CMAKE_SYSTEM_NAME MATCHES "Emscripten" OR CONFIGURE_OPTIONS STREQUAL CONFIGURE_OPTIONS_OLD)
message(STATUS "Found previous configure results. Don't perform configuration")
return()
endif()
@@ -134,10 +135,24 @@ function(configure_openssl)
set(VERBOSE_OPTION OUTPUT_QUIET)
endif()
+ if (CMAKE_SYSTEM_NAME MATCHES "Emscripten")
+ set(EMSCRIPTEN_CMAKE_WRAPPER "emcmake")
+ find_program(EMCC emcc REQUIRED)
+ set(EMSCRIPTEN_LINKER ${EMCC})
+ list(APPEND CONFIGURE_COMMAND wasm64)
+ else()
+ set(EMSCRIPTEN_CMAKE_WRAPPER "")
+ set(EMSCRIPTEN_LINKER ${CMAKE_LINKER})
+ endif ()
+
execute_process(
- COMMAND ${CMAKE_COMMAND} -E env
+ COMMAND ${EMSCRIPTEN_CMAKE_WRAPPER} ${CMAKE_COMMAND} -E env
"CFLAGS=${CMAKE_C_FLAGS}"
"CXXFLAGS=${CMAKE_CXX_FLAGS}"
+ "LDFLAGS=${CMAKE_CXX_LINK_FLAGS}"
+ "CC=${CMAKE_C_COMPILER}"
+ "CXX=${CMAKE_CXX_COMPILER}"
+ "LD=${EMSCRIPTEN_LINKER}"
${CONFIGURE_COMMAND}
WORKING_DIRECTORY ${CONFIGURE_BUILD_DIR}
${VERBOSE_OPTION}
+112
View File
@@ -0,0 +1,112 @@
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)
+22 -5
View File
@@ -79,6 +79,7 @@ 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))
@@ -380,6 +381,15 @@ 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)
@@ -408,7 +418,12 @@ set(BUILD_TESTING OFF)
set(ENABLE_TESTING OFF)
# boost
set(BOOST_INCLUDE_LIBRARIES algorithm icl pool container heap asio headers process filesystem crc variant)
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()
AddJsonPackage(boost)
@@ -429,10 +444,12 @@ 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>)
target_compile_options(boost_asio INTERFACE
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-conversion>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-implicit-fallthrough>
)
# 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()
endif()
endif()
+5 -3
View File
@@ -121,7 +121,7 @@
"find_args": "MODULE GLOBAL",
"hash": "159ed94965018f2a371d45a3bfc1961e5fb1549e501ded70a6b4532d7fe99d0579c18b5195aff6e35f96f399b426cea2650ec9fb75ef80d4c9edeccb51f2e6c9",
"options": [
"HTTPLIB_REQUIRE_OPENSSL ON",
"HTTPLIB_REQUIRE_OPENSSL OFF",
"HTTPLIB_DISABLE_MACOSX_AUTOMATIC_ROOT_CERTIFICATES ON"
],
"patches": [
@@ -207,7 +207,8 @@
"min_version": "3.0.0",
"package": "OpenSSL",
"patches": [
"0001-add-bundled-cert.patch"
"0001-add-bundled-cert.patch",
"0002-wasm-support.patch"
],
"repo": "openssl/openssl",
"tag": "openssl-%VERSION%",
@@ -231,7 +232,8 @@
"0001-cpmutil-compat.patch",
"0002-use-ccache.patch",
"0003-use-cmake-compiler-flags.patch",
"0004-use-shell-wrapper.patch"
"0004-use-shell-wrapper.patch",
"0005-wasm-support.patch"
],
"repo": "jimmy-park/openssl-cmake",
"tag": "%VERSION%",
+17
View File
@@ -12,6 +12,7 @@
- [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)
@@ -245,6 +246,22 @@ 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
+8
View File
@@ -359,6 +359,14 @@ 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>
+3
View File
@@ -27,3 +27,6 @@ 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.
+9 -5
View File
@@ -94,6 +94,10 @@ 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
@@ -101,6 +105,11 @@ 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
@@ -203,11 +212,6 @@ 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
+4 -2
View File
@@ -33,6 +33,9 @@ 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
@@ -151,6 +154,5 @@ 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()
+58 -26
View File
@@ -37,26 +37,39 @@ 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)
if (FFmpeg_SYSTEM_NAME STREQUAL "openorbis" OR FFmpeg_SYSTEM_NAME STREQUAL "managarm")
# 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))
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}"
--sysroot="${CMAKE_SYSROOT}"
)
--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()
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)
if (PLATFORM_PS4 OR PLATFORM_MANAGARM OR PLATFORM_EMSCRIPTEN)
# 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)
@@ -159,20 +172,28 @@ 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}"
)
--extra-libs="${FFmpeg_CROSS_COMPILE_LIBS}")
endif()
# Usually used by Emscripten
if (DEFINED CMAKE_AR)
list(APPEND FFmpeg_CROSS_COMPILE_FLAGS --ar=${CMAKE_AR})
endif()
if (DEFINED CMAKE_NM)
list(APPEND FFmpeg_CROSS_COMPILE_FLAGS --nm=${CMAKE_NM})
endif()
if (DEFINED CMAKE_RANLIB)
list(APPEND FFmpeg_CROSS_COMPILE_FLAGS --ranlib=${CMAKE_RANLIB})
endif()
if (YUZU_USE_BUNDLED_FFMPEG)
@@ -202,6 +223,7 @@ 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.
@@ -210,6 +232,12 @@ 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")
@@ -243,7 +271,13 @@ else()
CACHE PATH "Paths to FFmpeg libraries" FORCE)
endforeach()
find_program(BASH_PROGRAM bash REQUIRED)
# 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()
# `configure` parameters builds only exactly what yuzu needs from FFmpeg
# `--disable-vdpau` is needed to avoid linking issues
@@ -253,7 +287,7 @@ else()
OUTPUT
${FFmpeg_MAKEFILE}
COMMAND
${BASH_PROGRAM} ${FFmpeg_PREFIX}/configure
${FFmpeg_CONFIGURE_WRAPPER} ${FFmpeg_PREFIX}/configure
--disable-avdevice
--disable-avformat
--disable-doc
@@ -262,6 +296,10 @@ 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
@@ -269,7 +307,7 @@ else()
--enable-pic
--cc=${FFmpeg_CC}
--cxx=${FFmpeg_CXX}
--ld=${CMAKE_LINKER}
--ld=${FFmpeg_LD}
--extra-cflags=${CMAKE_C_FLAGS}
--extra-cxxflags=${CMAKE_CXX_FLAGS}
--extra-ldflags=${CMAKE_C_LINK_FLAGS}
@@ -285,26 +323,20 @@ 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.
execute_process(
COMMAND
nproc
OUTPUT_VARIABLE
SYSTEM_THREADS)
cmake_host_system_information(RESULT SYSTEM_THREADS QUERY NUMBER_OF_LOGICAL_CORES)
set(FFmpeg_BUILD_LIBRARIES ${FFmpeg_LIBRARIES})
# 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 "")
find_program(MAKE NAMES gmake make REQUIRED)
if (SYSTEM_THREADS GREATER 1)
set(FFmpeg_MAKE_ARGS -j ${SYSTEM_THREADS})
endif()
add_custom_command(
OUTPUT
${FFmpeg_BUILD_LIBRARIES}
COMMAND
gmake ${FFmpeg_MAKE_ARGS}
${MAKE} ${FFmpeg_MAKE_ARGS}
WORKING_DIRECTORY
${FFmpeg_BUILD_DIR}
)
+2
View File
@@ -14,7 +14,9 @@
namespace Tz {
namespace {
#ifndef EINVAL
#define EINVAL 22
#endif
static Rule gmtmem{};
static Rule* const gmtptr = &gmtmem;
+16 -10
View File
@@ -155,23 +155,25 @@ 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>:-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>:-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>:-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>:-Werror=shadow-uncaptured-local>
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=implicit-fallthrough>
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=type-limits>)
$<$<COMPILE_LANGUAGE:C,CXX>:-Wshadow-uncaptured-local>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wimplicit-fallthrough>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wtype-limits>)
endif()
add_compile_options(
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-braced-scalar-init>
@@ -179,7 +181,11 @@ else()
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-nullability-completeness>)
endif()
if (ARCHITECTURE_x86_64)
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)
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:-mcx16>)
if (PLATFORM_LINUX OR PLATFORM_FREEBSD)
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:-mtls-dialect=gnu2>)
@@ -31,7 +31,6 @@ 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"),
@@ -806,13 +806,6 @@ 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,
@@ -292,7 +292,6 @@ 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,8 +503,6 @@
<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>
+1 -1
View File
@@ -222,7 +222,7 @@ if (MSVC)
)
else()
target_compile_options(audio_core PRIVATE
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=conversion>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wconversion>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-sign-conversion>)
endif()
+6 -2
View File
@@ -220,7 +220,7 @@ endif()
if(CXX_CLANG)
target_compile_options(common PRIVATE
$<$<COMPILE_LANGUAGE:C,CXX>:-fsized-deallocation>
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=unreachable-code-aggressive>)
$<$<COMPILE_LANGUAGE:C,CXX>:-Wunreachable-code-aggressive>)
target_compile_definitions(
common
PRIVATE
@@ -235,7 +235,11 @@ else()
target_link_libraries(common PUBLIC Boost::headers)
endif()
target_link_libraries(common PRIVATE OpenSSL::SSL)
target_link_libraries(common PUBLIC Boost::filesystem Boost::context httplib::httplib nlohmann_json::nlohmann_json)
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()
if (lz4_ADDED)
target_include_directories(common PRIVATE ${lz4_SOURCE_DIR}/lib)
+4
View File
@@ -5,7 +5,11 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <string>
#ifndef _WIN32
#include <cstring>
#endif
#include <utility>
#include <fmt/ranges.h>
+85 -12
View File
@@ -11,7 +11,12 @@
#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 {
@@ -22,36 +27,103 @@ 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> rewind_stack{};
std::array<u8, DEFAULT_STACK_SIZE> astack{};
u32 canary_2 = CANARY_VALUE;
boost::context::detail::fcontext_t context{};
boost::context::detail::fcontext_t rewind_context{};
emscripten_fiber_t* context{nullptr};
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);
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{};
u32 canary_2 = CANARY_VALUE;
boost::context::detail::fcontext_t context{};
std::mutex guard;
std::function<void()> entry_point;
std::shared_ptr<Fiber> previous_fiber;
u8* stack_limit = nullptr;
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);
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);
@@ -72,7 +144,7 @@ Fiber::~Fiber() {
if (!impl->released) {
// Make sure the Fiber is not being used
const bool locked = impl->guard.try_lock();
ASSERT_MSG(locked, "Destroying a fiber that's still running");
ASSERT(locked && "Destroying a fiber that's still running");
if (locked) {
impl->guard.unlock();
}
@@ -80,7 +152,7 @@ Fiber::~Fiber() {
}
void Fiber::Exit() {
ASSERT_MSG(impl->is_thread_fiber, "Exiting non main thread fiber");
ASSERT(impl->is_thread_fiber && "Exiting non main thread fiber");
if (impl->is_thread_fiber) {
impl->guard.unlock();
impl->released = true;
@@ -110,5 +182,6 @@ std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
fiber->impl->is_thread_fiber = true;
return fiber;
}
#endif
} // namespace Common
+1 -2
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
@@ -45,7 +45,6 @@ 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:
+5
View File
@@ -136,6 +136,11 @@ 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;
+6 -6
View File
@@ -394,7 +394,7 @@ private:
ankerl::unordered_dense::map<size_t, size_t> placeholder_host_pointers; ///< Placeholder backing offset
};
#elif defined(__OPENORBIS__) || defined(__managarm__)
#elif defined(__OPENORBIS__) || defined(__managarm__) || defined(__wasi__) || defined(__EMSCRIPTEN__)
// 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__)
#if defined(__OPENORBIS__) || defined(__managarm__) || defined(__wasi__) || defined(__EMSCRIPTEN__)
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__))
#if !(defined(__OPENORBIS__) || defined(__managarm__) || defined(__wasi__) || defined(__EMSCRIPTEN__))
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__))
#if !(defined(__OPENORBIS__) || defined(__managarm__) || defined(__wasi__) || defined(__EMSCRIPTEN__))
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__))
#if !(defined(__OPENORBIS__) || defined(__managarm__) || defined(__wasi__) || defined(__EMSCRIPTEN__))
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__))
#if !(defined(__OPENORBIS__) || defined(__managarm__) || defined(__wasi__) || defined(__EMSCRIPTEN__))
if (impl) {
impl->EnableDirectMappedAddress();
virtual_size += reinterpret_cast<uintptr_t>(virtual_base);
+1 -1
View File
@@ -77,7 +77,7 @@ private:
size_t backing_size{};
size_t virtual_size{};
#if !(defined(__OPENORBIS__) || defined(__managarm__))
#if !(defined(__OPENORBIS__) || defined(__managarm__) || defined(__wasi__) || defined(__EMSCRIPTEN__))
// Low level handler for the platform dependent memory routines
class Impl;
std::unique_ptr<Impl> impl;
+4
View File
@@ -78,6 +78,8 @@ 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;
@@ -127,6 +129,8 @@ 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
+13 -6
View File
@@ -38,10 +38,6 @@ 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
@@ -1170,6 +1166,14 @@ 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)
@@ -1199,7 +1203,7 @@ if (MSVC)
)
else()
target_compile_options(core PRIVATE
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=conversion>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wconversion>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-sign-conversion>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-cast-function-type>
$<$<CXX_COMPILER_ID:Clang>:-fsized-deallocation>)
@@ -1213,7 +1217,10 @@ 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::asio Boost::process Boost::crc)
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()
else()
target_link_libraries(core PUBLIC Boost::headers)
endif()
+4 -2
View File
@@ -6,19 +6,20 @@
#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));
@@ -28,5 +29,6 @@ constexpr HaltReason TranslateHaltReason(Dynarmic::HaltReason hr) {
static_assert(u64(HaltReason::PrefetchAbort) == u64(PrefetchAbort));
return HaltReason(hr);
}
#endif
} // namespace Core
+1 -1
View File
@@ -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::move(process), params);
applet_manager.CreateAndInsertByFrontendAppletParameters(std::make_unique<Service::Process>(*std::move(process)), params);
if (Settings::values.gamecard_inserted) {
if (Settings::values.gamecard_current_game) {
+27 -2
View File
@@ -6,30 +6,54 @@
#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>);
@@ -400,3 +424,4 @@ void Debugger::NotifyShutdown() {
}
} // namespace Core
#endif
+10 -3
View File
@@ -7,8 +7,14 @@
#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"
@@ -18,8 +24,6 @@
#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
@@ -1300,9 +1304,11 @@ 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++)
@@ -1322,6 +1328,7 @@ 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) {
+6 -6
View File
@@ -729,7 +729,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface(ctx, ensure_token_id);
rb.PushIpcInterface(ensure_token_id);
}
void LoadIdTokenCacheDeprecated(HLERequestContext& ctx) {
@@ -921,7 +921,7 @@ void Module::Interface::GetProfile(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IProfile>(ctx, system, user_id, *profile_manager);
rb.PushIpcInterface<IProfile>(system, user_id, *profile_manager);
}
void Module::Interface::IsUserRegistrationRequestPermitted(HLERequestContext& ctx) {
@@ -993,7 +993,7 @@ void Module::Interface::GetBaasAccountManagerForApplication(HLERequestContext& c
LOG_DEBUG(Service_ACC, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IManagerForApplication>(ctx, system, profile_manager);
rb.PushIpcInterface<IManagerForApplication>(system, profile_manager);
}
void Module::Interface::IsUserAccountSwitchLocked(HLERequestContext& ctx) {
@@ -1089,7 +1089,7 @@ void Module::Interface::GetProfileEditor(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IProfileEditor>(ctx, system, user_id, *profile_manager);
rb.PushIpcInterface<IProfileEditor>(system, user_id, *profile_manager);
}
void Module::Interface::GetBaasAccountAdministrator(HLERequestContext &ctx) {
@@ -1100,7 +1100,7 @@ void Module::Interface::GetBaasAccountAdministrator(HLERequestContext &ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IAdministrator>(ctx, system, uuid);
rb.PushIpcInterface<IAdministrator>(system, uuid);
}
void Module::Interface::ListQualifiedUsers(HLERequestContext& ctx) {
@@ -1143,7 +1143,7 @@ void Module::Interface::GetBaasAccountManagerForSystemService(HLERequestContext&
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IManagerForSystemService>(ctx, system, uuid);
rb.PushIpcInterface<IManagerForSystemService>(system, uuid);
}
void Module::Interface::StoreSaveDataThumbnailSystem(HLERequestContext& ctx) {
+1 -1
View File
@@ -35,7 +35,7 @@ void IAsyncContext::GetSystemEvent(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(ctx, completion_event->GetReadableEvent());
rb.PushCopyObjects(completion_event->GetReadableEvent());
}
void IAsyncContext::Cancel(HLERequestContext& ctx) {
-4
View File
@@ -109,10 +109,6 @@ 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,13 +25,6 @@ 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,7 +26,6 @@ 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();
+1 -1
View File
@@ -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::move(overlay_process), false);
auto overlay_applet = std::make_shared<Applet>(m_system, std::make_unique<Service::Process>(*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,6 +1,3 @@
// 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
@@ -15,10 +12,7 @@ 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,6 +1,3 @@
// 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
@@ -27,7 +24,6 @@ public:
explicit HidRegistration(Core::System& system, Process& process);
~HidRegistration();
void RegisterCurrentProcess();
void EnableAppletToGetInput(bool enable);
private:
@@ -171,18 +171,6 @@ 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,8 +138,6 @@ 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;
+8 -24
View File
@@ -40,23 +40,23 @@ namespace {
}
}
[[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) {
[[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) {
// 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_unique<Process>(system);
auto process = std::make_optional<Process>(system);
if (process->Initialize(*out_loader, out_load_result)) {
return process;
}
}
return nullptr;
return std::nullopt;
}
} // Anonymous namespace
std::unique_ptr<Process> CreateProcess(Core::System& system, u64 program_id, u8 minimum_key_generation, u8 maximum_key_generation) {
std::optional<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::unique_ptr<Process> CreateProcess(Core::System& system, u64 program_id, u8
// Ensure we retrieved a program NCA.
if (!nca_raw) {
return nullptr;
return std::nullopt;
}
// Ensure we have a suitable version.
@@ -76,7 +76,7 @@ std::unique_ptr<Process> CreateProcess(Core::System& system, u64 program_id, u8
(nca.GetKeyGeneration() < minimum_key_generation ||
nca.GetKeyGeneration() > maximum_key_generation)) {
LOG_WARNING(Service_LDR, "Skipping program {:016X} with generation {}", program_id, nca.GetKeyGeneration());
return nullptr;
return std::nullopt;
}
}
@@ -85,7 +85,7 @@ std::unique_ptr<Process> CreateProcess(Core::System& system, u64 program_id, u8
return CreateProcessImpl(loader, status, system, nca_raw, program_id, 0);
}
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) {
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) {
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,23 +110,7 @@ std::unique_ptr<Process> CreateApplicationProcess(std::vector<u8>& out_control,
system.GetARPManager().Register(launch.title_id, launch, out_control);
return process;
}
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);
return std::nullopt;
}
} // namespace Service::AM
+2 -4
View File
@@ -27,9 +27,7 @@ class Process;
namespace Service::AM {
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);
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);
} // 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 != nullptr, ResultUnknown);
R_UNLESS(process != std::nullopt, ResultUnknown);
const auto applet = std::make_shared<Applet>(system, std::move(process), true);
const auto applet = std::make_shared<Applet>(system, std::make_unique<Service::Process>(*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 != nullptr, ResultUnknown);
R_UNLESS(process != std::nullopt, ResultUnknown);
const auto applet = std::make_shared<Applet>(system, std::move(process), true);
const auto applet = std::make_shared<Applet>(system, std::make_unique<Service::Process>(*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, D<&ILibraryAppletAccessor::GetLibraryAppletInfo>, "GetLibraryAppletInfo"},
{120, nullptr, "GetLibraryAppletInfo"},
{150, nullptr, "RequestForAppletToGetForeground"},
{160, D<&ILibraryAppletAccessor::GetIndirectLayerConsumerHandle>, "GetIndirectLayerConsumerHandle"}, //2.0.0+
{170, D<&ILibraryAppletAccessor::Unknown170>, "Unknown170"}, //22.0.0+
@@ -218,16 +218,6 @@ 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,7 +6,6 @@
#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"
@@ -22,10 +21,6 @@ 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);
@@ -42,7 +37,6 @@ 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::move(process), false);
const auto applet = std::make_shared<Applet>(system, std::make_unique<Service::Process>(*std::move(process)), false);
applet->program_id = program_id;
applet->applet_id = applet_id;
applet->type = AppletType::LibraryApplet;
@@ -233,9 +233,8 @@ Result ILibraryAppletSelfAccessor::ReportVisibleErrorWithErrorContext(
R_SUCCEED();
}
Result ILibraryAppletSelfAccessor::UnpopInData(SharedPointer<IStorage> storage) {
LOG_INFO(Service_AM, "called");
m_broker->GetInData().Unpop(system.Kernel(), storage);
Result ILibraryAppletSelfAccessor::UnpopInData() {
LOG_WARNING(Service_AM, "(STUBBED) called");
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(SharedPointer<IStorage> storage);
Result UnpopInData();
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,11 +1,9 @@
// 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 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"
@@ -45,18 +43,6 @@ 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");
@@ -88,82 +74,22 @@ Result IProcessWindingController::PopContext(Out<SharedPointer<IStorage>> out_co
}
Result IProcessWindingController::CancelWindingReservation() {
LOG_INFO(Service_AM, "called");
std::scoped_lock lk{m_applet->lock};
m_applet->reserved_applet.reset();
m_applet->unwind_after_reserved = false;
LOG_WARNING(Service_AM, "STUBBED");
R_SUCCEED();
}
Result IProcessWindingController::WindAndDoReserved() {
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();
LOG_WARNING(Service_AM, "STUBBED");
R_SUCCEED();
}
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;
Result IProcessWindingController::ReserveToStartAndWaitAndUnwindThis() {
LOG_WARNING(Service_AM, "STUBBED");
R_SUCCEED();
}
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;
Result IProcessWindingController::ReserveToStartAndWait() {
LOG_WARNING(Service_AM, "STUBBED");
R_SUCCEED();
}
@@ -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 2024 yuzu Emulator Project
@@ -29,9 +29,8 @@ private:
Result PopContext(Out<SharedPointer<IStorage>> out_context);
Result CancelWindingReservation();
Result WindAndDoReserved();
Result ReserveToStartAndWaitAndUnwindThis(
SharedPointer<ILibraryAppletAccessor> reserved_applet_accessor);
Result ReserveToStartAndWait(SharedPointer<ILibraryAppletAccessor> reserved_applet_accessor);
Result ReserveToStartAndWaitAndUnwindThis();
Result ReserveToStartAndWait();
const std::shared_ptr<Applet> m_applet;
};
-57
View File
@@ -9,7 +9,6 @@
#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 {
@@ -241,37 +240,6 @@ 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());
@@ -333,28 +301,6 @@ 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) {
@@ -406,9 +352,6 @@ 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 ||
-1
View File
@@ -61,7 +61,6 @@ 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);
+2 -2
View File
@@ -82,7 +82,7 @@ void APM::OpenSession(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<ISession>(ctx, system, controller);
rb.PushIpcInterface<ISession>(system, controller);
}
void APM::GetPerformanceMode(HLERequestContext& ctx) {
@@ -125,7 +125,7 @@ void APM_Sys::GetPerformanceEvent(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<ISession>(ctx, system, controller);
rb.PushIpcInterface<ISession>(system, controller);
}
void APM_Sys::SetCpuBoostMode(HLERequestContext& ctx) {
+1 -4
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -48,7 +45,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IRequest>(ctx, system);
rb.PushIpcInterface<IRequest>(system);
}
};
+7 -7
View File
@@ -184,7 +184,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(readable_event.Signal(system.Kernel()));
rb.PushCopyObjects(ctx, readable_event);
rb.PushCopyObjects(readable_event);
}
void Cancel(HLERequestContext& ctx) {
@@ -400,7 +400,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(ctx, notification_event->GetReadableEvent());
rb.PushCopyObjects(notification_event->GetReadableEvent());
}
void Clear(HLERequestContext& ctx) {
@@ -476,7 +476,7 @@ private:
void Module::Interface::CreateFriendService(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IFriendService>(ctx, system);
rb.PushIpcInterface<IFriendService>(system);
LOG_DEBUG(Service_Friend, "called");
}
@@ -488,12 +488,12 @@ void Module::Interface::CreateNotificationService(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<INotificationService>(ctx, system, uuid);
rb.PushIpcInterface<INotificationService>(system, uuid);
}
Module::Interface::Interface(std::shared_ptr<Module> module_, Core::System& system_, const char* name)
: ServiceFramework{system_, name}, module{std::move(module_)}
{}
Module::Interface::Interface(std::shared_ptr<Module> module_, Core::System& system_,
const char* name)
: ServiceFramework{system_, name}, module{std::move(module_)} {}
Module::Interface::~Interface() = default;
+1 -1
View File
@@ -279,7 +279,7 @@ void ARP_W::AcquireRegistrar(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface(ctx, registrar);
rb.PushIpcInterface(registrar);
}
void ARP_W::UnregisterApplicationInstance(HLERequestContext& ctx) {
+1 -1
View File
@@ -28,7 +28,7 @@ void BGTC_T::OpenTaskService(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<ITaskService>(ctx, system);
rb.PushIpcInterface<ITaskService>(system);
}
ITaskService::ITaskService(Core::System& system_) : ServiceFramework{system_, "ITaskService"} {
+1 -4
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -59,7 +56,7 @@ ECTX_AW::~ECTX_AW() = default;
void ECTX_AW::CreateContextRegistrar(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IContextRegistrar>(ctx, system);
rb.PushIpcInterface<IContextRegistrar>(std::make_shared<IContextRegistrar>(system));
}
} // namespace Service::Glue
@@ -726,7 +726,7 @@ void IHidSystemServer::AcquireConnectionTriggerTimeoutEvent(HLERequestContext& c
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(ctx, acquire_device_registered_event->GetReadableEvent());
rb.PushCopyObjects(acquire_device_registered_event->GetReadableEvent());
}
void IHidSystemServer::AcquireDeviceRegisteredEventForControllerSupport(HLERequestContext& ctx) {
@@ -734,7 +734,7 @@ void IHidSystemServer::AcquireDeviceRegisteredEventForControllerSupport(HLEReque
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(ctx, acquire_device_registered_event->GetReadableEvent());
rb.PushCopyObjects(acquire_device_registered_event->GetReadableEvent());
}
void IHidSystemServer::GetRegisteredDevices(HLERequestContext& ctx) {
@@ -759,7 +759,7 @@ void IHidSystemServer::AcquireUniquePadConnectionEventHandle(HLERequestContext&
LOG_WARNING(Service_HID, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.PushCopyObjects(ctx, unique_pad_connection_event->GetReadableEvent());
rb.PushCopyObjects(unique_pad_connection_event->GetReadableEvent());
rb.Push(ResultSuccess);
}
@@ -776,7 +776,7 @@ void IHidSystemServer::AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestConte
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(ctx, joy_detach_event->GetReadableEvent());
rb.PushCopyObjects(joy_detach_event->GetReadableEvent());
}
void IHidSystemServer::IsUsbFullKeyControllerEnabled(HLERequestContext& ctx) {
+2 -2
View File
@@ -31,7 +31,7 @@ class Memory;
}
namespace IPC {
struct ResponseBuilder;
class ResponseBuilder;
}
namespace Service {
@@ -392,7 +392,7 @@ public:
}
private:
friend struct IPC::ResponseBuilder;
friend class IPC::ResponseBuilder;
void ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming);
+153 -91
View File
@@ -24,7 +24,45 @@ namespace IPC {
constexpr Result ResultSessionClosed{ErrorModule::HIPC, 301};
struct ResponseBuilder {
class RequestHelperBase {
protected:
Service::HLERequestContext* context = nullptr;
u32* cmdbuf;
u32 index = 0;
public:
explicit RequestHelperBase(u32* command_buffer) : cmdbuf(command_buffer) {}
explicit RequestHelperBase(Service::HLERequestContext& ctx)
: context(&ctx), cmdbuf(ctx.CommandBuffer()) {}
void Skip(u32 size_in_words, bool set_to_null) {
if (set_to_null) {
memset(cmdbuf + index, 0, size_in_words * sizeof(u32));
}
index += size_in_words;
}
/**
* Aligns the current position forward to a 16-byte boundary, padding with zeros.
*/
void AlignWithPadding() {
if (index & 3) {
Skip(static_cast<u32>(4 - (index & 3)), true);
}
}
u32 GetCurrentOffset() const {
return index;
}
void SetCurrentOffset(u32 offset) {
index = offset;
}
};
class ResponseBuilder : public RequestHelperBase {
public:
/// Flags used for customizing the behavior of ResponseBuilder
enum class Flags : u32 {
None = 0,
@@ -33,13 +71,14 @@ struct ResponseBuilder {
AlwaysMoveHandles = 1,
};
inline explicit ResponseBuilder(Service::HLERequestContext& ctx, u32 normal_params_size_, u32 num_handles_to_copy_ = 0, u32 num_objects_to_move_ = 0, Flags flags = Flags::None)
: cmdbuf(ctx.CommandBuffer())
, normal_params_size(normal_params_size_)
, num_handles_to_copy(num_handles_to_copy_)
, num_objects_to_move(num_objects_to_move_)
{
std::memset(cmdbuf, 0, sizeof(u32) * IPC::COMMAND_BUFFER_LENGTH);
explicit ResponseBuilder(Service::HLERequestContext& ctx, u32 normal_params_size_,
u32 num_handles_to_copy_ = 0, u32 num_objects_to_move_ = 0,
Flags flags = Flags::None)
: RequestHelperBase(ctx), normal_params_size(normal_params_size_),
num_handles_to_copy(num_handles_to_copy_),
num_objects_to_move(num_objects_to_move_), kernel{ctx.kernel} {
memset(cmdbuf, 0, sizeof(u32) * IPC::COMMAND_BUFFER_LENGTH);
IPC::CommandHeader header{};
auto const mgr = ctx.GetManager().get();
@@ -78,7 +117,9 @@ struct ResponseBuilder {
handle_descriptor_header.num_handles_to_copy.Assign(num_handles_to_copy_);
handle_descriptor_header.num_handles_to_move.Assign(num_handles_to_move);
PushRaw(handle_descriptor_header);
ctx.handles_offset = index;
Skip(num_handles_to_copy + num_handles_to_move, true);
}
@@ -90,6 +131,7 @@ struct ResponseBuilder {
domain_header.num_objects = num_domain_objects;
PushRaw(domain_header);
}
IPC::DataPayloadHeader data_payload_header{};
data_payload_header.magic = Common::MakeMagic('S', 'F', 'C', 'O');
PushRaw(data_payload_header);
@@ -99,39 +141,34 @@ struct ResponseBuilder {
ctx.data_payload_offset = index;
ctx.write_size += index;
ctx.domain_offset = u32(index + raw_data_size / sizeof(u32));
ctx.domain_offset = static_cast<u32>(index + raw_data_size / sizeof(u32));
}
inline void Skip(u32 size_in_words, bool set_to_null) {
if (set_to_null) std::memset(cmdbuf + index, 0, size_in_words * sizeof(u32));
index += size_in_words;
}
/// @brief Aligns the current position forward to a 16-byte boundary, padding with zeros.
inline void AlignWithPadding() { if (index & 3) Skip(u32(4 - (index & 3)), true); }
inline u32 GetCurrentOffset() const { return index; }
inline void SetCurrentOffset(u32 offset) { index = offset; }
template <class T>
void PushIpcInterface(std::shared_ptr<T> iface) {
auto manager{context->GetManager()};
template <class T> inline void PushIpcInterface(Service::HLERequestContext& ctx, std::shared_ptr<T> iface) {
auto manager = ctx.GetManager();
if (manager->IsDomain()) {
ctx.AddDomainObject(std::move(iface));
context->AddDomainObject(std::move(iface));
} else {
ASSERT(Kernel::GetCurrentProcess(ctx.kernel).GetResourceLimit()->Reserve(ctx.kernel, Kernel::LimitableResource::SessionCountMax, 1));
ASSERT(Kernel::GetCurrentProcess(kernel).GetResourceLimit()->Reserve(kernel, Kernel::LimitableResource::SessionCountMax, 1));
auto* session = Kernel::KSession::Create(ctx.kernel);
session->Initialize(ctx.kernel, nullptr, 0);
Kernel::KSession::Register(ctx.kernel, session);
auto* session = Kernel::KSession::Create(kernel);
session->Initialize(kernel, nullptr, 0);
Kernel::KSession::Register(kernel, session);
auto next_manager = std::make_shared<Service::SessionRequestManager>(ctx.kernel, manager->GetServerManager());
auto next_manager = std::make_shared<Service::SessionRequestManager>(
kernel, manager->GetServerManager());
next_manager->SetSessionHandler(iface);
manager->GetServerManager().RegisterSession(&session->GetServerSession(), next_manager);
ctx.AddMoveObject(&session->GetClientSession());
context->AddMoveObject(&session->GetClientSession());
}
}
template <class T, class... Args> inline void PushIpcInterface(Service::HLERequestContext& ctx, Args&&... args) {
PushIpcInterface<T>(ctx, std::make_shared<T>(std::forward<Args>(args)...));
template <class T, class... Args>
void PushIpcInterface(Args&&... args) {
PushIpcInterface<T>(std::make_shared<T>(std::forward<Args>(args)...));
}
void PushImpl(s8 value);
@@ -147,38 +184,59 @@ struct ResponseBuilder {
void PushImpl(bool value);
void PushImpl(Result value);
template <typename T> inline void Push(T value) {
template <typename T>
void Push(T value) {
return PushImpl(value);
}
template <typename First, typename... Other>
void Push(const First& first_value, const Other&... other_values);
/// @brief Helper function for pushing strongly-typed enumeration values.
/// @tparam Enum The enumeration type to be pushed
/// @param value The value to push.
/// @note The underlying size of the enumeration type is the size of the data that gets pushed.
/// e.g. "enum class SomeEnum : u16" will push a u16-sized amount of data.
template <typename Enum> inline void PushEnum(Enum value) {
/**
* Helper function for pushing strongly-typed enumeration values.
*
* @tparam Enum The enumeration type to be pushed
*
* @param value The value to push.
*
* @note The underlying size of the enumeration type is the size of the
* data that gets pushed. e.g. "enum class SomeEnum : u16" will
* push a u16-sized amount of data.
*/
template <typename Enum>
void PushEnum(Enum value) {
static_assert(std::is_enum_v<Enum>, "T must be an enum type within a PushEnum call.");
static_assert(!std::is_convertible_v<Enum, int>, "enum type in PushEnum must be a strongly typed enum.");
static_assert(!std::is_convertible_v<Enum, int>,
"enum type in PushEnum must be a strongly typed enum.");
Push(static_cast<std::underlying_type_t<Enum>>(value));
}
/// @brief Copies the content of the given trivially copyable class to the buffer as a normal param
/// @note: The input class must be correctly packed/padded to fit hardware layout.
template <typename T> void PushRaw(const T& value);
template <typename... O> void PushMoveObjects(Service::HLERequestContext& ctx, O*... pointers);
template <typename... O> void PushMoveObjects(Service::HLERequestContext& ctx, O&... pointers);
template <typename... O> void PushCopyObjects(Service::HLERequestContext& ctx, O*... pointers);
template <typename... O> void PushCopyObjects(Service::HLERequestContext& ctx, O&... pointers);
/**
* @brief Copies the content of the given trivially copyable class to the buffer as a normal
* param
* @note: The input class must be correctly packed/padded to fit hardware layout.
*/
template <typename T>
void PushRaw(const T& value);
u32* cmdbuf;
u32 index = 0;
template <typename... O>
void PushMoveObjects(O*... pointers);
template <typename... O>
void PushMoveObjects(O&... pointers);
template <typename... O>
void PushCopyObjects(O*... pointers);
template <typename... O>
void PushCopyObjects(O&... pointers);
private:
u32 normal_params_size{};
u32 num_handles_to_copy{};
u32 num_objects_to_move{}; ///< Domain objects or move handles, context dependent
u32 data_payload_index{};
Kernel::KernelCore& kernel;
};
/// Push ///
@@ -193,7 +251,8 @@ inline void ResponseBuilder::PushImpl(u32 value) {
template <typename T>
void ResponseBuilder::PushRaw(const T& value) {
static_assert(std::is_trivially_copyable_v<T>, "It's undefined behavior to use memcpy with non-trivially copyable objects");
static_assert(std::is_trivially_copyable_v<T>,
"It's undefined behavior to use memcpy with non-trivially copyable objects");
std::memcpy(cmdbuf + index, &value, sizeof(T));
index += (sizeof(T) + 3) / 4; // round up to word length
}
@@ -213,8 +272,8 @@ inline void ResponseBuilder::PushImpl(s16 value) {
}
inline void ResponseBuilder::PushImpl(s64 value) {
PushImpl(u32(value));
PushImpl(u32(value >> 32));
PushImpl(static_cast<u32>(value));
PushImpl(static_cast<u32>(value >> 32));
}
inline void ResponseBuilder::PushImpl(u8 value) {
@@ -226,8 +285,8 @@ inline void ResponseBuilder::PushImpl(u16 value) {
}
inline void ResponseBuilder::PushImpl(u64 value) {
PushImpl(u32(value));
PushImpl(u32(value >> 32));
PushImpl(static_cast<u32>(value));
PushImpl(static_cast<u32>(value >> 32));
}
inline void ResponseBuilder::PushImpl(float value) {
@@ -253,88 +312,90 @@ void ResponseBuilder::Push(const First& first_value, const Other&... other_value
}
template <typename... O>
inline void ResponseBuilder::PushCopyObjects(Service::HLERequestContext& ctx, O*... pointers) {
inline void ResponseBuilder::PushCopyObjects(O*... pointers) {
auto objects = {pointers...};
for (auto& object : objects) {
ctx.AddCopyObject(object);
context->AddCopyObject(object);
}
}
template <typename... O>
inline void ResponseBuilder::PushCopyObjects(Service::HLERequestContext& ctx, O&... pointers) {
inline void ResponseBuilder::PushCopyObjects(O&... pointers) {
auto objects = {&pointers...};
for (auto& object : objects) {
ctx.AddCopyObject(object);
context->AddCopyObject(object);
}
}
template <typename... O>
inline void ResponseBuilder::PushMoveObjects(Service::HLERequestContext& ctx, O*... pointers) {
inline void ResponseBuilder::PushMoveObjects(O*... pointers) {
auto objects = {pointers...};
for (auto& object : objects) {
ctx.AddMoveObject(object);
context->AddMoveObject(object);
}
}
template <typename... O>
inline void ResponseBuilder::PushMoveObjects(Service::HLERequestContext& ctx, O&... pointers) {
inline void ResponseBuilder::PushMoveObjects(O&... pointers) {
auto objects = {&pointers...};
for (auto& object : objects) {
ctx.AddMoveObject(object);
context->AddMoveObject(object);
}
}
struct RequestParser {
inline explicit RequestParser(u32* command_buffer) : cmdbuf(command_buffer) {}
inline explicit RequestParser(Service::HLERequestContext& ctx)
: cmdbuf(ctx.CommandBuffer())
{
class RequestParser : public RequestHelperBase {
public:
explicit RequestParser(u32* command_buffer) : RequestHelperBase(command_buffer) {}
explicit RequestParser(Service::HLERequestContext& ctx) : RequestHelperBase(ctx) {
// TIPC does not have data payload offset
if (!ctx.IsTipc()) {
ASSERT_MSG(ctx.GetDataPayloadOffset(), "context is incomplete");
Skip(ctx.GetDataPayloadOffset(), false);
}
// Skip the u64 command id, it's already stored in the context
static constexpr u32 CommandIdSize = 2;
Skip(CommandIdSize, false);
}
inline void Skip(u32 size_in_words, bool set_to_null) {
if (set_to_null) std::memset(cmdbuf + index, 0, size_in_words * sizeof(u32));
index += size_in_words;
}
/// @brief Aligns the current position forward to a 16-byte boundary, padding with zeros.
inline void AlignWithPadding() { if (index & 3) Skip(u32(4 - (index & 3)), true); }
inline u32 GetCurrentOffset() const { return index; }
inline void SetCurrentOffset(u32 offset) { index = offset; }
template <typename T>
T Pop();
template <typename T> T Pop();
template <typename T> void Pop(T& value);
template <typename First, typename... Other> void Pop(First& first_value, Other&... other_values);
template <typename T>
void Pop(T& value);
template <typename First, typename... Other>
void Pop(First& first_value, Other&... other_values);
template <typename T>
T PopEnum() {
static_assert(std::is_enum_v<T>, "T must be an enum type within a PopEnum call.");
static_assert(!std::is_convertible_v<T, int>, "enum type in PopEnum must be a strongly typed enum.");
return T(Pop<std::underlying_type_t<T>>());
static_assert(!std::is_convertible_v<T, int>,
"enum type in PopEnum must be a strongly typed enum.");
return static_cast<T>(Pop<std::underlying_type_t<T>>());
}
/// @brief Reads the next normal parameters as a struct, by copying it
/// @note: The output class must be correctly packed/padded to fit hardware layout.
template <typename T> void PopRaw(T& value);
/**
* @brief Reads the next normal parameters as a struct, by copying it
* @note: The output class must be correctly packed/padded to fit hardware layout.
*/
template <typename T>
void PopRaw(T& value);
/// @brief Reads the next normal parameters as a struct, by copying it into a new value
/// @note: The output class must be correctly packed/padded to fit hardware layout.
template <typename T> T PopRaw();
/**
* @brief Reads the next normal parameters as a struct, by copying it into a new value
* @note: The output class must be correctly packed/padded to fit hardware layout.
*/
template <typename T>
T PopRaw();
template <class T> [[nodiscard]] std::weak_ptr<T> PopIpcInterface(Service::HLERequestContext& ctx) {
ASSERT(ctx.GetManager()->IsDomain());
ASSERT(ctx.GetDomainMessageHeader().input_object_count > 0);
return ctx.GetDomainHandler<T>(Pop<u32>() - 1);
template <class T>
std::weak_ptr<T> PopIpcInterface() {
ASSERT(context->GetManager()->IsDomain());
ASSERT(context->GetDomainMessageHeader().input_object_count > 0);
return context->GetDomainHandler<T>(Pop<u32>() - 1);
}
u32* cmdbuf;
u32 index = 0;
};
/// Pop ///
@@ -346,7 +407,7 @@ inline u32 RequestParser::Pop() {
template <>
inline s32 RequestParser::Pop() {
return s32(Pop<u32>());
return static_cast<s32>(Pop<u32>());
}
// Ignore the -Wclass-memaccess warning on memcpy for non-trivially default constructible objects.
@@ -356,7 +417,8 @@ inline s32 RequestParser::Pop() {
#endif
template <typename T>
void RequestParser::PopRaw(T& value) {
static_assert(std::is_trivially_copyable_v<T>, "It's undefined behavior to use memcpy with non-trivially copyable objects");
static_assert(std::is_trivially_copyable_v<T>,
"It's undefined behavior to use memcpy with non-trivially copyable objects");
std::memcpy(&value, cmdbuf + index, sizeof(T));
index += (sizeof(T) + 3) / 4; // round up to word length
}
+1 -1
View File
@@ -353,7 +353,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<ILogger>(ctx, system);
rb.PushIpcInterface<ILogger>(system);
}
};
+4 -4
View File
@@ -151,7 +151,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IAm>(ctx, system);
rb.PushIpcInterface<IAm>(system);
}
};
@@ -173,7 +173,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<MFIUser>(ctx, system);
rb.PushIpcInterface<MFIUser>(system);
}
};
@@ -195,7 +195,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IUser>(ctx, system);
rb.PushIpcInterface<IUser>(system);
}
};
@@ -217,7 +217,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<ISystem>(ctx, system);
rb.PushIpcInterface<ISystem>(system);
}
};
+3 -3
View File
@@ -143,7 +143,7 @@ void NfcInterface::AttachAvailabilityChangeEvent(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(ctx, GetManager()->AttachAvailabilityChangeEvent());
rb.PushCopyObjects(GetManager()->AttachAvailabilityChangeEvent());
}
void NfcInterface::StartDetection(HLERequestContext& ctx) {
@@ -203,7 +203,7 @@ void NfcInterface::AttachActivateEvent(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(result);
rb.PushCopyObjects(ctx, out_event);
rb.PushCopyObjects(out_event);
}
void NfcInterface::AttachDeactivateEvent(HLERequestContext& ctx) {
@@ -217,7 +217,7 @@ void NfcInterface::AttachDeactivateEvent(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(result);
rb.PushCopyObjects(ctx, out_event);
rb.PushCopyObjects(out_event);
}
void NfcInterface::SetNfcEnabled(HLERequestContext& ctx) {
+3 -3
View File
@@ -157,7 +157,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IUser>(ctx, system);
rb.PushIpcInterface<IUser>(system);
}
};
@@ -179,7 +179,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<ISystem>(ctx, system);
rb.PushIpcInterface<ISystem>(system);
}
};
@@ -201,7 +201,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IDebug>(ctx, system);
rb.PushIpcInterface<IDebug>(system);
}
};
+8 -8
View File
@@ -307,7 +307,7 @@ private:
void GetSystemEventReadableHandle(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 2};
rb.Push(ResultSuccess);
rb.PushCopyObjects(ctx, evt_scan_complete->GetReadableEvent(),
rb.PushCopyObjects(evt_scan_complete->GetReadableEvent(),
evt_processing->GetReadableEvent());
}
@@ -452,7 +452,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 2};
rb.Push(ResultSuccess);
rb.PushCopyObjects(ctx, event1->GetReadableEvent(), event2->GetReadableEvent());
rb.PushCopyObjects(event1->GetReadableEvent(), event2->GetReadableEvent());
}
void Cancel(HLERequestContext& ctx) {
@@ -528,7 +528,7 @@ void IGeneralService::CreateScanRequest(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IScanRequest>(ctx, system);
rb.PushIpcInterface<IScanRequest>(system);
}
void IGeneralService::CreateRequest(HLERequestContext& ctx) {
@@ -537,7 +537,7 @@ void IGeneralService::CreateRequest(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IRequest>(ctx, system);
rb.PushIpcInterface<IRequest>(system);
}
void IGeneralService::GetCurrentNetworkProfile(HLERequestContext& ctx) {
@@ -716,7 +716,7 @@ void IGeneralService::GetNetworkProfile(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
rb.PushIpcInterface<INetworkProfile>(ctx, system);
rb.PushIpcInterface<INetworkProfile>(system);
}
void IGeneralService::SetNetworkProfile(HLERequestContext& ctx) {
@@ -869,7 +869,7 @@ void IGeneralService::CreateTemporaryNetworkProfile(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 6, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<INetworkProfile>(ctx, system);
rb.PushIpcInterface<INetworkProfile>(system);
rb.PushRaw<u128>(uuid);
}
@@ -1124,7 +1124,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IGeneralService>(ctx, system);
rb.PushIpcInterface<IGeneralService>(system);
}
void CreateGeneralService(HLERequestContext& ctx) {
@@ -1132,7 +1132,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IGeneralService>(ctx, system);
rb.PushIpcInterface<IGeneralService>(system);
}
};
+6 -6
View File
@@ -53,7 +53,7 @@ private:
LOG_WARNING(Service_NIM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IShopServiceAsync>(ctx, system);
rb.PushIpcInterface<IShopServiceAsync>(system);
}
};
@@ -75,7 +75,7 @@ private:
LOG_WARNING(Service_NIM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IShopServiceAccessor>(ctx, system);
rb.PushIpcInterface<IShopServiceAccessor>(system);
}
};
@@ -336,7 +336,7 @@ private:
LOG_DEBUG(Service_NIM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IShopServiceAccessServer>(ctx, system);
rb.PushIpcInterface<IShopServiceAccessServer>(system);
}
void IsLargeResourceAvailable(HLERequestContext& ctx) {
@@ -356,7 +356,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IShopServiceAccessServer>(ctx, system);
rb.PushIpcInterface<IShopServiceAccessServer>(system);
}
};
@@ -439,7 +439,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(ctx, finished_event->GetReadableEvent());
rb.PushCopyObjects(finished_event->GetReadableEvent());
}
void GetResult(HLERequestContext& ctx) {
@@ -500,7 +500,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IEnsureNetworkClockAvailabilityService>(ctx, system);
rb.PushIpcInterface<IEnsureNetworkClockAvailabilityService>(system);
}
// TODO(ogniK): Do we need these?
@@ -359,8 +359,8 @@ void IReadOnlyApplicationControlDataInterface::ListApplicationTitle(HLERequestCo
IPC::ResponseBuilder rb{ctx, 2, 1, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(ctx, async_value->ReadableEvent());
rb.PushIpcInterface(ctx, std::move(async_value));
rb.PushCopyObjects(async_value->ReadableEvent());
rb.PushIpcInterface(std::move(async_value));
}
Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData3(
@@ -199,7 +199,7 @@ void NVDRV::QueryEvent(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3, 1};
rb.Push(ResultSuccess);
auto& readable_event = event->GetReadableEvent();
rb.PushCopyObjects(ctx, readable_event);
rb.PushCopyObjects(readable_event);
rb.PushEnum(NvResult::Success);
} else {
LOG_ERROR(Service_NVDRV, "Invalid event request!");
-11
View File
@@ -6,8 +6,6 @@
#pragma once
#include <utility>
#include "common/common_types.h"
namespace Core {
@@ -30,15 +28,6 @@ public:
inline explicit Process(Core::System& system) noexcept : m_system(system) {}
inline ~Process() { this->Finalize(); }
Process(const Process&) = delete;
Process& operator=(const Process&) = delete;
Process& operator=(Process&&) = delete;
inline Process(Process&& other) noexcept
: m_system(other.m_system), m_process(std::exchange(other.m_process, nullptr)),
m_main_thread_stack_size(std::exchange(other.m_main_thread_stack_size, 0)),
m_main_thread_priority(std::exchange(other.m_main_thread_priority, 0)),
m_process_started(std::exchange(other.m_process_started, false)) {}
bool Initialize(Loader::AppLoader& loader, Loader::ResultStatus& out_load_result);
void Finalize();
+1 -4
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -127,7 +124,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IClkrstSession>(ctx, system, device_code);
rb.PushIpcInterface<IClkrstSession>(system, device_code);
}
};
+1 -1
View File
@@ -161,7 +161,7 @@ private:
IPC::ResponseBuilder rb{ctx, 10, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(ctx, *process);
rb.PushCopyObjects(*process);
rb.PushRaw(program_location);
rb.PushRaw(override_status);
}
+3 -3
View File
@@ -147,7 +147,7 @@ void IAlarmService::CreateWakeupAlarm(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<ISteadyClockAlarm>(ctx, system, m_alarms, AlarmType::WakeupAlarm);
rb.PushIpcInterface<ISteadyClockAlarm>(system, m_alarms, AlarmType::WakeupAlarm);
}
void IAlarmService::CreateBackgroundTaskAlarm(HLERequestContext& ctx) {
@@ -155,7 +155,7 @@ void IAlarmService::CreateBackgroundTaskAlarm(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<ISteadyClockAlarm>(ctx, system, m_alarms, AlarmType::BackgroundTaskAlarm);
rb.PushIpcInterface<ISteadyClockAlarm>(system, m_alarms, AlarmType::BackgroundTaskAlarm);
}
ISteadyClockAlarm::ISteadyClockAlarm(Core::System& system_, Alarms& alarms, AlarmType type)
@@ -179,7 +179,7 @@ void ISteadyClockAlarm::GetAlarmEvent(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(ctx, m_alarm.GetEventHandle());
rb.PushCopyObjects(m_alarm.GetEventHandle());
}
void ISteadyClockAlarm::Enable(HLERequestContext& ctx) {
+2 -2
View File
@@ -67,7 +67,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(ctx, state_change_event->GetReadableEvent());
rb.PushCopyObjects(state_change_event->GetReadableEvent());
}
void UnbindStateChangeEvent(HLERequestContext& ctx) {
@@ -186,7 +186,7 @@ void PSM::OpenSession(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IPsmSession>(ctx, system);
rb.PushIpcInterface<IPsmSession>(system);
}
void PSM::GetBatteryVoltageState(HLERequestContext& ctx) {
+1 -4
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
@@ -85,7 +82,7 @@ void TS::OpenSession(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<ISession>(ctx, system);
rb.PushIpcInterface<ISession>(system);
}
} // namespace Service::PTM
+13 -3
View File
@@ -86,17 +86,27 @@ 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
for (auto const& e : std::vector<std::pair<std::string_view, void (*)(Core::System&)>>{
std::vector<std::pair<std::string_view, void (*)(Core::System&)>> rt_services{
{"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();
kernel.RunOnHostCoreProcess("vi", [&, token] { VI::LoopProcess(system, token); }).detach();
#endif
// Avoid cold clones of lambdas -- succintly
for (auto const& e : std::vector<std::pair<std::string_view, void (*)(Core::System&)>>{
{"sm", &SM::LoopProcess},
+5 -4
View File
@@ -140,7 +140,7 @@ void SM::GetServiceCmif(HLERequestContext& ctx) {
if (result == ResultSuccess) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
rb.Push(result);
rb.PushMoveObjects(ctx, client_session);
rb.PushMoveObjects(client_session);
} else {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
@@ -157,7 +157,7 @@ void SM::GetServiceTipc(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
rb.Push(result);
rb.PushMoveObjects(ctx, result == ResultSuccess ? client_session : nullptr);
rb.PushMoveObjects(result == ResultSuccess ? client_session : nullptr);
}
static std::string PopServiceName(IPC::RequestParser& rp) {
@@ -230,7 +230,8 @@ void SM::RegisterServiceImpl(HLERequestContext& ctx, std::string name, u32 max_s
max_session_count, is_light);
Kernel::KServerPort* server_port{};
if (const auto result = service_manager.RegisterService(std::addressof(server_port), name, max_session_count, nullptr);
if (const auto result = service_manager.RegisterService(std::addressof(server_port), name,
max_session_count, nullptr);
result.IsError()) {
LOG_ERROR(Service_SM, "failed to register service with error_code={:08X}", result.raw);
IPC::ResponseBuilder rb{ctx, 2};
@@ -240,7 +241,7 @@ void SM::RegisterServiceImpl(HLERequestContext& ctx, std::string name, u32 max_s
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
rb.Push(ResultSuccess);
rb.PushMoveObjects(ctx, server_port);
rb.PushMoveObjects(server_port);
}
void SM::UnregisterService(HLERequestContext& ctx) {
+1 -1
View File
@@ -60,7 +60,7 @@ void Controller::CloneCurrentObject(HLERequestContext& ctx) {
// We succeeded.
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
rb.Push(ResultSuccess);
rb.PushMoveObjects(ctx, session->GetClientSession());
rb.PushMoveObjects(session->GetClientSession());
}
void Controller::CloneCurrentObjectEx(HLERequestContext& ctx) {
+5 -3
View File
@@ -543,7 +543,8 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(res);
if (res == ResultSuccess) {
rb.PushIpcInterface<ISslConnection>(ctx, system, ssl_version, shared_data, std::move(backend));
rb.PushIpcInterface<ISslConnection>(system, ssl_version, shared_data,
std::move(backend));
}
}
@@ -623,11 +624,12 @@ private:
IPC::RequestParser rp{ctx};
const auto parameters = rp.PopRaw<Parameters>();
LOG_WARNING(Service_SSL, "(STUBBED) called, api_version={}, pid_placeholder={}", parameters.ssl_version.api_version, parameters.pid_placeholder);
LOG_WARNING(Service_SSL, "(STUBBED) called, api_version={}, pid_placeholder={}",
parameters.ssl_version.api_version, parameters.pid_placeholder);
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<ISslContext>(ctx, system, parameters.ssl_version);
rb.PushIpcInterface<ISslContext>(system, parameters.ssl_version);
}
void SetInterfaceVersion(HLERequestContext& ctx) {
+2 -2
View File
@@ -155,7 +155,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IPdSession>(ctx, system);
rb.PushIpcInterface<IPdSession>(system);
}
};
@@ -199,7 +199,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IPdCradleSession>(ctx, system);
rb.PushIpcInterface<IPdCradleSession>(system);
}
};
+1 -1
View File
@@ -151,7 +151,7 @@ if (MSVC)
)
else()
target_compile_options(hid_core PRIVATE
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=conversion>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wconversion>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-sign-conversion>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-cast-function-type>
$<$<CXX_COMPILER_ID:Clang>:-fsized-deallocation>)
@@ -28,7 +28,10 @@ enum class NpadMcuState : u32 {
struct NpadMcuHolder {
NpadMcuState state;
INSERT_PADDING_BYTES(0x4);
IAbstractedPad* abstracted_pad;
union {
IAbstractedPad* abstracted_pad;
u64 abstracted_pad_raw;
};
};
static_assert(sizeof(NpadMcuHolder) == 0x10, "NpadMcuHolder is an invalid size");
+2 -2
View File
@@ -50,8 +50,8 @@ NPad::NPad(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service
auto& controller = controller_data[aruid_index][i];
controller.device = hid_core.GetEmulatedControllerByIndex(i);
Core::HID::ControllerUpdateCallback engine_callback{
.on_change = [this, i, kernel = &hid_core.kernel](Core::HID::ControllerTriggerType type) {
ControllerUpdate(*kernel, type, i);
.on_change = [this, i](Core::HID::ControllerTriggerType type) {
ControllerUpdate(hid_core.kernel, type, i);
},
.is_npad_service = true,
};
@@ -33,9 +33,15 @@ public:
bool is_created{};
bool is_mapped{};
INSERT_PADDING_BYTES(0x5);
Kernel::KSharedMemory* shared_memory;
union {
Kernel::KSharedMemory* shared_memory = nullptr;
u64 shared_memory_raw;
};
INSERT_PADDING_BYTES(0x38);
SharedMemoryFormat* address = nullptr;
union {
SharedMemoryFormat* address = nullptr;
u64 address_raw;
};
};
// Correct size is 0x50 bytes
static_assert(sizeof(SharedMemoryHolder) == 0x50, "SharedMemoryHolder is an invalid size");
+7 -5
View File
@@ -15,8 +15,6 @@ 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
@@ -34,8 +32,12 @@ add_library(input_common STATIC
input_poller.cpp
input_poller.h
main.cpp
main.h
)
main.h)
if (NOT PLATFORM_EMSCRIPTEN)
target_sources(input_common PRIVATE
drivers/udp_client.cpp
drivers/udp_client.h)
endif()
if (MSVC)
target_compile_options(input_common PRIVATE
@@ -44,7 +46,7 @@ if (MSVC)
/we4800 # Implicit conversion from 'type' to bool. Possible information loss
)
else()
target_compile_options(input_common PRIVATE $<$<COMPILE_LANGUAGE:C,CXX>:-Werror=conversion>)
target_compile_options(input_common PRIVATE $<$<COMPILE_LANGUAGE:C,CXX>:-Wconversion>)
endif()
if (ANDROID)
+21 -1
View File
@@ -12,7 +12,6 @@
#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"
@@ -21,6 +20,9 @@
#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"
@@ -82,7 +84,9 @@ 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__
@@ -116,7 +120,9 @@ struct InputSubsystem::Impl {
#ifdef ENABLE_LIBUSB
UnregisterEngine(gcadapter);
#endif
#ifndef __EMSCRIPTEN__
UnregisterEngine(udp_client);
#endif
UnregisterEngine(tas_input);
UnregisterEngine(camera);
#ifdef __ANDROID__
@@ -152,8 +158,10 @@ 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());
@@ -186,9 +194,11 @@ 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;
@@ -271,9 +281,11 @@ struct InputSubsystem::Impl {
return true;
}
#endif
#ifndef __EMSCRIPTEN__
if (engine == udp_client->GetEngineName()) {
return true;
}
#endif
if (engine == tas_input->GetEngineName()) {
return true;
}
@@ -300,7 +312,9 @@ struct InputSubsystem::Impl {
#ifdef ENABLE_LIBUSB
gcadapter->BeginConfiguration();
#endif
#ifndef __EMSCRIPTEN__
udp_client->BeginConfiguration();
#endif
#ifdef HAVE_SDL3
sdl->BeginConfiguration();
joycon->BeginConfiguration();
@@ -316,7 +330,9 @@ struct InputSubsystem::Impl {
#ifdef ENABLE_LIBUSB
gcadapter->EndConfiguration();
#endif
#ifndef __EMSCRIPTEN__
udp_client->EndConfiguration();
#endif
#ifdef HAVE_SDL3
sdl->EndConfiguration();
joycon->EndConfiguration();
@@ -341,7 +357,9 @@ 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;
@@ -470,7 +488,9 @@ 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) {
+2 -3
View File
@@ -253,12 +253,11 @@ if (MSVC)
)
else()
target_compile_options(shader_recompiler PRIVATE
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=conversion>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wconversion>
# 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)
+6 -6
View File
@@ -31,7 +31,7 @@ struct CbufWordKey {
struct CbufWordKeyHash {
constexpr size_t operator()(const CbufWordKey& k) const noexcept {
return (size_t(k.index) << 32) ^ k.offset;
return size_t((u64(k.index) << 32) ^ u64(k.offset));
}
};
@@ -46,12 +46,12 @@ struct HandleKey {
};
struct HandleKeyHash {
constexpr size_t operator()(const HandleKey& k) const noexcept {
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);
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);
h ^= k.has_secondary ? 0x9e3779b97f4a7c15ULL : 0ULL;
return h;
return size_t(h);
}
};
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -157,16 +160,14 @@ IR::Value Sample(TranslatorVisitor& v, u64 insn) {
unsigned Swizzle(u64 insn) {
const Encoding texs{insn};
const size_t encoding{texs.swizzle};
u8 const encoding = u8(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,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -142,16 +145,14 @@ IR::Value Sample(TranslatorVisitor& v, u64 insn) {
unsigned Swizzle(u64 insn) {
const Encoding tlds{insn};
const size_t encoding{tlds.swizzle};
u8 const encoding = u8(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];
}
}
@@ -236,11 +236,8 @@ void LowerGeometryPassthrough(const IR::Program& program, const HostTranslateInf
IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool,
Environment& env, Flow::CFG& cfg, const HostTranslateInfo& host_info) {
HostTranslateInfo normalized_host_info{host_info};
normalized_host_info.ApplyDescriptorLimitPolicy();
IR::Program program;
program.syntax_list = BuildASL(inst_pool, block_pool, env, cfg, normalized_host_info);
program.syntax_list = BuildASL(inst_pool, block_pool, env, cfg, host_info);
program.blocks = GenerateBlocks(program.syntax_list);
program.post_order_blocks = PostOrder(program.syntax_list.front());
program.stage = env.ShaderStage();
@@ -263,9 +260,9 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
program.info.passthrough.mask[i] = ((mask[i / 32] >> (i % 32)) & 1) == 0;
}
if (!normalized_host_info.support_geometry_shader_passthrough) {
if (!host_info.support_geometry_shader_passthrough) {
program.output_vertices = GetOutputTopologyVertices(program.output_topology);
LowerGeometryPassthrough(program, normalized_host_info);
LowerGeometryPassthrough(program, host_info);
}
}
break;
@@ -280,16 +277,16 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
RemoveUnreachableBlocks(program);
// Replace instructions before the SSA rewrite
if (!normalized_host_info.support_float64) {
if (!host_info.support_float64) {
Optimization::LowerFp64ToFp32(program);
}
if (!normalized_host_info.support_float16) {
if (!host_info.support_float16) {
Optimization::LowerFp16ToFp32(program);
}
if (!normalized_host_info.support_int64) {
if (!host_info.support_int64) {
Optimization::LowerInt64ToInt32(program);
}
if (!normalized_host_info.support_conditional_barrier) {
if (!host_info.support_conditional_barrier) {
Optimization::ConditionalBarrierPass(program);
}
Optimization::SsaRewritePass(program);
@@ -298,8 +295,8 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
Optimization::PositionPass(env, program);
Optimization::GlobalMemoryToStorageBufferPass(program, normalized_host_info);
Optimization::TexturePass(env, program, normalized_host_info);
Optimization::GlobalMemoryToStorageBufferPass(program, host_info);
Optimization::TexturePass(env, program, host_info);
if (Settings::values.resolution_info.active || Settings::values.rescale_hack.GetValue()) {
Optimization::RescalingPass(program);
@@ -309,7 +306,7 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
Optimization::VerificationPass(program);
}
Optimization::CollectShaderInfoPass(env, program);
Optimization::LayerPass(program, normalized_host_info);
Optimization::LayerPass(program, host_info);
Optimization::VendorWorkaroundPass(program);
CollectInterpolationInfo(env, program);
+4 -38
View File
@@ -6,8 +6,6 @@
#pragma once
#include "common/common_types.h"
namespace Shader {
// Try to keep entries here to a minimum
@@ -15,52 +13,20 @@ namespace Shader {
/// Misc information about the host
struct HostTranslateInfo {
static constexpr u32 DEFAULT_DESCRIPTOR_LIMIT = 1024;
u64 min_ssbo_alignment{}; ///< Minimum alignment supported by the device for SSBOs
u32 max_per_stage_descriptor_sampled_images{}; ///< maximum sampled descriptors per stage
u32 max_per_stage_resources{}; ///< maximum resources per stage
u32 max_descriptor_set_samplers{};
u32 max_descriptor_set_uniform_buffers{};
u32 max_descriptor_set_uniform_buffers_dynamic{};
u32 max_descriptor_set_storage_buffers{};
u32 max_descriptor_set_storage_buffers_dynamic{};
u32 max_descriptor_set_sampled_images{};
u32 max_descriptor_set_storage_images{};
u32 max_descriptor_set_input_attachements{};
bool support_float64{}; ///< True when the device supports 64-bit floats
bool support_float16{}; ///< True when the device supports 16-bit floats
bool support_int64{}; ///< True when the device supports 64-bit integers
bool needs_demote_reorder{}; ///< True when the device needs DemoteToHelperInvocation reordered
bool support_snorm_render_buffer{}; ///< True when the device supports SNORM render buffers
bool support_viewport_index_layer{}; ///< True when the device supports gl_Layer in VS
u32 min_ssbo_alignment{}; ///< Minimum alignment supported by the device for SSBOs
u32 max_per_stage_descriptor_sampled_images{1024}; ///< maximum sampled descriptors per stage
u32 max_per_stage_resources{4096}; ///< maximum resources per stage
u32 max_descriptor_set_sampled_images{1024}; ///< maximum sampled descriptors per set
bool support_geometry_shader_passthrough{}; ///< True when the device supports geometry
///< passthrough shaders
bool support_conditional_barrier{}; ///< True when the device supports barriers in conditional
///< control flow
void ApplyDescriptorLimitPolicy() noexcept {
if (min_ssbo_alignment == 0) {
min_ssbo_alignment = 1;
}
ApplyDescriptorLimitFallback(max_per_stage_descriptor_sampled_images);
ApplyDescriptorLimitFallback(max_per_stage_resources);
ApplyDescriptorLimitFallback(max_descriptor_set_samplers);
ApplyDescriptorLimitFallback(max_descriptor_set_uniform_buffers);
ApplyDescriptorLimitFallback(max_descriptor_set_uniform_buffers_dynamic);
ApplyDescriptorLimitFallback(max_descriptor_set_storage_buffers);
ApplyDescriptorLimitFallback(max_descriptor_set_storage_buffers_dynamic);
ApplyDescriptorLimitFallback(max_descriptor_set_sampled_images);
ApplyDescriptorLimitFallback(max_descriptor_set_storage_images);
ApplyDescriptorLimitFallback(max_descriptor_set_input_attachements);
}
private:
static void ApplyDescriptorLimitFallback(u32& limit) noexcept {
if (limit == 0) {
limit = DEFAULT_DESCRIPTOR_LIMIT;
}
}
};
} // namespace Shader
@@ -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
@@ -548,7 +545,7 @@ void GlobalMemoryToStorageBufferPass(IR::Program& program, const HostTranslateIn
IR::Block* const block{storage_inst.block};
IR::Inst* const inst{storage_inst.inst};
const IR::U32 offset{
StorageOffset(*block, *inst, storage_buffer, u32(host_info.min_ssbo_alignment))};
StorageOffset(*block, *inst, storage_buffer, host_info.min_ssbo_alignment)};
Replace(*block, *inst, index, offset);
}
}
+71 -65
View File
@@ -32,62 +32,71 @@ struct TextureInst {
using TextureInstVector = boost::container::small_vector<TextureInst, 24>;
constexpr u32 DESCRIPTOR_SIZE = 8;
constexpr u32 DESCRIPTOR_SIZE_SHIFT = u32(std::countr_zero(DESCRIPTOR_SIZE));
constexpr u32 DESCRIPTOR_MAX_COUNT = 1024;
constexpr u32 DESCRIPTOR_SIZE_SHIFT = static_cast<u32>(std::countr_zero(DESCRIPTOR_SIZE));
constexpr u32 DYNAMIC_DESCRIPTOR_CBUF_BYTES = 16 * 1024;
constexpr u32 MAX_DYNAMIC_DESCRIPTOR_COUNT = 1024;
u32 DynamicDescriptorSizeShift(const IR::U32& dynamic_offset) {
const IR::Inst* const inst = dynamic_offset.InstRecursive();
if (!inst || inst->GetOpcode() != IR::Opcode::ShiftLeftLogical32)
const IR::Inst* const inst{dynamic_offset.InstRecursive()};
if (!inst || inst->GetOpcode() != IR::Opcode::ShiftLeftLogical32) {
return DESCRIPTOR_SIZE_SHIFT;
const IR::Value shift = inst->Arg(1);
if (!shift.IsImmediate())
}
const IR::Value shift{inst->Arg(1)};
if (!shift.IsImmediate()) {
return DESCRIPTOR_SIZE_SHIFT;
const u32 size_shift = shift.U32();
return size_shift >= DESCRIPTOR_SIZE_SHIFT && size_shift < 31 ? size_shift : DESCRIPTOR_SIZE_SHIFT;
}
const u32 size_shift{shift.U32()};
return size_shift >= DESCRIPTOR_SIZE_SHIFT && size_shift < 31 ? size_shift
: DESCRIPTOR_SIZE_SHIFT;
}
u32 DynamicDescriptorCount(u32 base_offset, u32 size_shift, u32 max_descriptors) {
auto const descriptor_limit = (std::max)(1U, max_descriptors);
auto const max_cbuf_bytes = 16 * descriptor_limit;
if (size_shift >= 31 || base_offset >= max_cbuf_bytes)
u32 DynamicDescriptorCount(u32 base_offset, u32 size_shift) {
if (size_shift >= 31 || base_offset >= DYNAMIC_DESCRIPTOR_CBUF_BYTES) {
return 1;
auto const stride = 1U << size_shift;
auto const available = max_cbuf_bytes - base_offset;
if (available < DESCRIPTOR_SIZE)
}
const u32 stride{1U << size_shift};
const u32 available{DYNAMIC_DESCRIPTOR_CBUF_BYTES - base_offset};
if (available < DESCRIPTOR_SIZE) {
return 1;
auto const available_count = 1U + (available - DESCRIPTOR_SIZE) / stride;
return std::min(descriptor_limit, available_count);
}
const u32 available_count{1U + (available - DESCRIPTOR_SIZE) / stride};
return std::min(MAX_DYNAMIC_DESCRIPTOR_COUNT, available_count);
}
u32 SaturatingSub(u32 lhs, u32 rhs) {
return lhs > rhs ? lhs - rhs : 0;
}
template <typename T>
[[nodiscard]] u32 StaticDescriptorCount(T const& descriptors) noexcept {
return std::accumulate(descriptors.cbegin(), descriptors.cend(), 0U, [](auto const& acc, auto const& e) {
return acc + (e.count <= 1 ? e.count : 0);
});
template <typename Descriptors>
u32 StaticDescriptorCount(const Descriptors& descriptors) {
u32 count{};
for (const auto& desc : descriptors) {
if (desc.count <= 1) {
count += desc.count;
}
}
return count;
}
u32 DynamicSampledTextureCap(const Info& info, const HostTranslateInfo& host_info, u32 dynamic_arrays) {
auto const sampled_limit = (std::max)(1U, std::min(host_info.max_per_stage_descriptor_sampled_images,
host_info.max_descriptor_set_sampled_images));
auto const resource_limit = (std::max)(1U, host_info.max_per_stage_resources);
if (dynamic_arrays > 0) {
auto const sampled_static_count = StaticDescriptorCount(info.texture_buffer_descriptors) + StaticDescriptorCount(info.texture_descriptors);
auto const resource_static_count =
NumDescriptors(info.constant_buffer_descriptors)
+ NumDescriptors(info.storage_buffers_descriptors)
+ sampled_static_count + NumDescriptors(info.image_buffer_descriptors)
+ NumDescriptors(info.image_descriptors);
auto const sampled_budget = SaturatingSub(sampled_limit, sampled_static_count);
auto const resource_budget = SaturatingSub(resource_limit, resource_static_count);
auto const sampled_cap = sampled_budget / dynamic_arrays;
auto const resource_cap = resource_budget / dynamic_arrays;
return (std::max)(1U, (std::min)(sampled_cap, resource_cap));
u32 DynamicSampledTextureCap(const Info& info, const HostTranslateInfo& host_info,
u32 dynamic_arrays) {
if (dynamic_arrays == 0) {
return MAX_DYNAMIC_DESCRIPTOR_COUNT;
}
return (std::min)({DESCRIPTOR_MAX_COUNT, sampled_limit, resource_limit});
const u32 sampled_static_count{StaticDescriptorCount(info.texture_buffer_descriptors) +
StaticDescriptorCount(info.texture_descriptors)};
const u32 resource_static_count{
NumDescriptors(info.constant_buffer_descriptors) +
NumDescriptors(info.storage_buffers_descriptors) + sampled_static_count +
NumDescriptors(info.image_buffer_descriptors) + NumDescriptors(info.image_descriptors)};
const u32 sampled_limit{std::min(host_info.max_per_stage_descriptor_sampled_images,
host_info.max_descriptor_set_sampled_images)};
const u32 sampled_budget{SaturatingSub(sampled_limit, sampled_static_count)};
const u32 resource_budget{SaturatingSub(host_info.max_per_stage_resources,
resource_static_count)};
const u32 sampled_cap{sampled_budget / dynamic_arrays};
const u32 resource_cap{resource_budget / dynamic_arrays};
return std::max(1U, std::min({MAX_DYNAMIC_DESCRIPTOR_COUNT, sampled_cap, resource_cap}));
}
IR::Opcode IndexedInstruction(const IR::Inst& inst) {
@@ -295,23 +304,21 @@ static inline bool IsTexturePixelFormatIntegerCached(Environment& env,
}
std::optional<ConstBufferAddr> Track(const IR::Value& value, Environment& env, const HostTranslateInfo& host_info);
static inline std::optional<ConstBufferAddr> TrackCached(const IR::Value& v, Environment& env, const HostTranslateInfo& host_info) {
std::optional<ConstBufferAddr> Track(const IR::Value& value, Environment& env);
static inline std::optional<ConstBufferAddr> TrackCached(const IR::Value& v, Environment& env) {
if (const IR::Inst* key = v.InstRecursive()) {
if (auto it = env.track_cache.find(key); it != env.track_cache.end()) return it->second;
auto found = Track(v, env, host_info);
auto found = Track(v, env);
if (found) env.track_cache.emplace(key, *found);
return found;
}
return Track(v, env, host_info);
return Track(v, env);
}
std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environment& env, const HostTranslateInfo& host_info);
std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environment& env);
std::optional<ConstBufferAddr> Track(const IR::Value& value, Environment& env, const HostTranslateInfo& host_info) {
return IR::BreadthFirstSearch(value, [&env, &host_info](const IR::Inst* inst) {
return TryGetConstBuffer(inst, env, host_info);
});
std::optional<ConstBufferAddr> Track(const IR::Value& value, Environment& env) {
return IR::BreadthFirstSearch(value, [&env](const IR::Inst* inst) { return TryGetConstBuffer(inst, env); });
}
std::optional<u32> TryGetConstant(IR::Value& value, Environment& env) {
@@ -335,13 +342,13 @@ std::optional<u32> TryGetConstant(IR::Value& value, Environment& env) {
return ReadCbufCached(env, index_number, offset_number);
}
std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environment& env, const HostTranslateInfo& host_info) {
std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environment& env) {
switch (inst->GetOpcode()) {
default:
return std::nullopt;
case IR::Opcode::BitwiseOr32: {
std::optional lhs{TrackCached(inst->Arg(0), env, host_info)};
std::optional rhs{TrackCached(inst->Arg(1), env, host_info)};
std::optional lhs{TrackCached(inst->Arg(0), env)};
std::optional rhs{TrackCached(inst->Arg(1), env)};
if (!lhs || !rhs) {
return std::nullopt;
}
@@ -371,11 +378,12 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environme
if (!shift.IsImmediate()) {
return std::nullopt;
}
std::optional lhs{TrackCached(inst->Arg(0), env, host_info)};
std::optional lhs{TrackCached(inst->Arg(0), env)};
if (lhs) {
lhs->shift_left = shift.U32();
}
return lhs;
break;
}
case IR::Opcode::BitwiseAnd32: {
IR::Value op1{inst->Arg(0)};
@@ -399,7 +407,7 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environme
return std::nullopt;
} while (false);
}
std::optional lhs{TrackCached(op1, env, host_info)};
std::optional lhs{TrackCached(op1, env)};
if (lhs) {
lhs->shift_left = static_cast<u32>(std::countr_zero(op2.U32()));
}
@@ -445,10 +453,7 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environme
} else {
return std::nullopt;
}
auto const size_shift = DynamicDescriptorSizeShift(dynamic_offset);
auto const sampled_limit = (std::max)(1U, (std::min)(host_info.max_per_stage_descriptor_sampled_images,
host_info.max_descriptor_set_sampled_images));
auto const resource_limit = (std::max)(1U, host_info.max_per_stage_resources);
const u32 size_shift{DynamicDescriptorSizeShift(dynamic_offset)};
return ConstBufferAddr{
.index = index.U32(),
.offset = base_offset,
@@ -457,15 +462,15 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environme
.secondary_offset = 0,
.secondary_shift_left = 0,
.dynamic_offset = dynamic_offset,
.count = DynamicDescriptorCount(base_offset, size_shift, (std::min)({DESCRIPTOR_MAX_COUNT, sampled_limit, resource_limit})),
.count = DynamicDescriptorCount(base_offset, size_shift),
.has_secondary = false,
};
}
TextureInst MakeInst(Environment& env, IR::Block* block, IR::Inst& inst, const HostTranslateInfo& host_info) {
TextureInst MakeInst(Environment& env, IR::Block* block, IR::Inst& inst) {
ConstBufferAddr addr;
if (IsBindless(inst)) {
const std::optional<ConstBufferAddr> track_addr{TrackCached(inst.Arg(0), env, host_info)};
const std::optional<ConstBufferAddr> track_addr{TrackCached(inst.Arg(0), env)};
if (!track_addr) {
throw NotImplementedException("Failed to track bindless texture constant buffer");
@@ -501,15 +506,15 @@ u32 GetTextureHandle(Environment& env, const ConstBufferAddr& cbuf) {
return lhs_raw | rhs_raw;
}
[[maybe_unused]] TextureType ReadTextureType(Environment& env, const ConstBufferAddr& cbuf) {
[[maybe_unused]]TextureType ReadTextureType(Environment& env, const ConstBufferAddr& cbuf) {
return env.ReadTextureType(GetTextureHandle(env, cbuf));
}
[[maybe_unused]] TexturePixelFormat ReadTexturePixelFormat(Environment& env, const ConstBufferAddr& cbuf) {
[[maybe_unused]]TexturePixelFormat ReadTexturePixelFormat(Environment& env, const ConstBufferAddr& cbuf) {
return env.ReadTexturePixelFormat(GetTextureHandle(env, cbuf));
}
[[maybe_unused]] bool IsTexturePixelFormatInteger(Environment& env, const ConstBufferAddr& cbuf) {
[[maybe_unused]]bool IsTexturePixelFormatInteger(Environment& env, const ConstBufferAddr& cbuf) {
return env.IsTexturePixelFormatInteger(GetTextureHandle(env, cbuf));
}
@@ -670,7 +675,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
if (!IsTextureInstruction(inst)) {
continue;
}
to_replace.push_back(MakeInst(env, block, inst, host_info));
to_replace.push_back(MakeInst(env, block, inst));
}
}
// Sort instructions to visit textures by constant buffer index, then by offset
@@ -684,7 +689,8 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
program.info.texture_descriptors,
program.info.image_descriptors,
};
const u32 sampled_dynamic_cap = DynamicSampledTextureCap(program.info, host_info, DynamicSampledTextureArrayCount(to_replace));
const u32 sampled_dynamic_cap{
DynamicSampledTextureCap(program.info, host_info, DynamicSampledTextureArrayCount(to_replace))};
for (TextureInst& texture_inst : to_replace) {
// TODO: Handle arrays
IR::Inst* const inst{texture_inst.inst};
+1
View File
@@ -92,6 +92,7 @@ struct Profile {
bool has_broken_robust{};
u64 min_ssbo_alignment{};
u32 max_user_clip_distances{};
};
+2 -27
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -18,19 +15,12 @@ static constexpr auto PERMS = Common::MemoryPermission::ReadWrite;
static constexpr auto HEAP = false;
TEST_CASE("HostMemory: Initialize and deinitialize", "[common]") {
{
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
REQUIRE(mem.BackingBasePointer() != nullptr);
}
{
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
REQUIRE(mem.BackingBasePointer() != nullptr);
}
{ HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); }
{ HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); }
}
TEST_CASE("HostMemory: Simple map", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
REQUIRE(mem.BackingBasePointer() != nullptr);
mem.Map(0x5000, 0x8000, 0x1000, PERMS, HEAP);
volatile u8* const data = mem.VirtualBasePointer() + 0x5000;
@@ -40,7 +30,6 @@ TEST_CASE("HostMemory: Simple map", "[common]") {
TEST_CASE("HostMemory: Simple mirror map", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
REQUIRE(mem.BackingBasePointer() != nullptr);
mem.Map(0x5000, 0x3000, 0x2000, PERMS, HEAP);
mem.Map(0x8000, 0x4000, 0x1000, PERMS, HEAP);
@@ -52,7 +41,6 @@ TEST_CASE("HostMemory: Simple mirror map", "[common]") {
TEST_CASE("HostMemory: Simple unmap", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
REQUIRE(mem.BackingBasePointer() != nullptr);
mem.Map(0x5000, 0x3000, 0x2000, PERMS, HEAP);
volatile u8* const data = mem.VirtualBasePointer() + 0x5000;
@@ -64,7 +52,6 @@ TEST_CASE("HostMemory: Simple unmap", "[common]") {
TEST_CASE("HostMemory: Simple unmap and remap", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
REQUIRE(mem.BackingBasePointer() != nullptr);
mem.Map(0x5000, 0x3000, 0x2000, PERMS, HEAP);
volatile u8* const data = mem.VirtualBasePointer() + 0x5000;
@@ -82,7 +69,6 @@ TEST_CASE("HostMemory: Simple unmap and remap", "[common]") {
TEST_CASE("HostMemory: Nieche allocation", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
REQUIRE(mem.BackingBasePointer() != nullptr);
mem.Map(0x0000, 0, 0x20000, PERMS, HEAP);
mem.Unmap(0x0000, 0x4000, HEAP);
mem.Map(0x1000, 0, 0x2000, PERMS, HEAP);
@@ -92,7 +78,6 @@ TEST_CASE("HostMemory: Nieche allocation", "[common]") {
TEST_CASE("HostMemory: Full unmap", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
REQUIRE(mem.BackingBasePointer() != nullptr);
mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
mem.Unmap(0x8000, 0x4000, HEAP);
mem.Map(0x6000, 0, 0x16000, PERMS, HEAP);
@@ -100,7 +85,6 @@ TEST_CASE("HostMemory: Full unmap", "[common]") {
TEST_CASE("HostMemory: Right out of bounds unmap", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
REQUIRE(mem.BackingBasePointer() != nullptr);
mem.Map(0x0000, 0, 0x4000, PERMS, HEAP);
mem.Unmap(0x2000, 0x4000, HEAP);
mem.Map(0x2000, 0x80000, 0x4000, PERMS, HEAP);
@@ -108,7 +92,6 @@ TEST_CASE("HostMemory: Right out of bounds unmap", "[common]") {
TEST_CASE("HostMemory: Left out of bounds unmap", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
mem.Unmap(0x6000, 0x4000, HEAP);
mem.Map(0x8000, 0, 0x2000, PERMS, HEAP);
@@ -116,7 +99,6 @@ TEST_CASE("HostMemory: Left out of bounds unmap", "[common]") {
TEST_CASE("HostMemory: Multiple placeholder unmap", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
REQUIRE(mem.BackingBasePointer() != nullptr);
mem.Map(0x0000, 0, 0x4000, PERMS, HEAP);
mem.Map(0x4000, 0, 0x1b000, PERMS, HEAP);
mem.Unmap(0x3000, 0x1c000, HEAP);
@@ -125,7 +107,6 @@ TEST_CASE("HostMemory: Multiple placeholder unmap", "[common]") {
TEST_CASE("HostMemory: Unmap between placeholders", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
REQUIRE(mem.BackingBasePointer() != nullptr);
mem.Map(0x0000, 0, 0x4000, PERMS, HEAP);
mem.Map(0x4000, 0, 0x4000, PERMS, HEAP);
mem.Unmap(0x2000, 0x4000, HEAP);
@@ -134,7 +115,6 @@ TEST_CASE("HostMemory: Unmap between placeholders", "[common]") {
TEST_CASE("HostMemory: Unmap to origin", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
REQUIRE(mem.BackingBasePointer() != nullptr);
mem.Map(0x4000, 0, 0x4000, PERMS, HEAP);
mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
mem.Unmap(0x4000, 0x4000, HEAP);
@@ -144,7 +124,6 @@ TEST_CASE("HostMemory: Unmap to origin", "[common]") {
TEST_CASE("HostMemory: Unmap to right", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
REQUIRE(mem.BackingBasePointer() != nullptr);
mem.Map(0x4000, 0, 0x4000, PERMS, HEAP);
mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
mem.Unmap(0x8000, 0x4000, HEAP);
@@ -153,7 +132,6 @@ TEST_CASE("HostMemory: Unmap to right", "[common]") {
TEST_CASE("HostMemory: Partial right unmap check bindings", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
REQUIRE(mem.BackingBasePointer() != nullptr);
mem.Map(0x4000, 0x10000, 0x4000, PERMS, HEAP);
volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000;
@@ -166,7 +144,6 @@ TEST_CASE("HostMemory: Partial right unmap check bindings", "[common]") {
TEST_CASE("HostMemory: Partial left unmap check bindings", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
REQUIRE(mem.BackingBasePointer() != nullptr);
mem.Map(0x4000, 0x10000, 0x4000, PERMS, HEAP);
volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000;
@@ -181,7 +158,6 @@ TEST_CASE("HostMemory: Partial left unmap check bindings", "[common]") {
TEST_CASE("HostMemory: Partial middle unmap check bindings", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
REQUIRE(mem.BackingBasePointer() != nullptr);
mem.Map(0x4000, 0x10000, 0x4000, PERMS, HEAP);
volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000;
@@ -196,7 +172,6 @@ TEST_CASE("HostMemory: Partial middle unmap check bindings", "[common]") {
TEST_CASE("HostMemory: Partial sparse middle unmap and check bindings", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
REQUIRE(mem.BackingBasePointer() != nullptr);
mem.Map(0x4000, 0x10000, 0x2000, PERMS, HEAP);
mem.Map(0x6000, 0x20000, 0x2000, PERMS, HEAP);
+1 -1
View File
@@ -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>:-Werror=conversion>)
target_compile_options(video_core PRIVATE $<$<COMPILE_LANGUAGE:C,CXX>:-Wconversion>)
endif()
target_compile_options(video_core PRIVATE $<$<COMPILE_LANGUAGE:C,CXX>:-Wno-sign-conversion>)
@@ -11,8 +11,9 @@
#define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants {
#define END_PUSH_CONSTANTS };
#define UNIFORM(n)
#define BINDING_INPUT_BUFFER 0
#define BINDING_OUTPUT_IMAGE 1
#define BINDING_SWIZZLE_BUFFER 0
#define BINDING_INPUT_BUFFER 1
#define BINDING_OUTPUT_IMAGE 2
#else // ^^^ Vulkan ^^^ // vvv OpenGL vvv
@@ -25,7 +26,8 @@
#define BEGIN_PUSH_CONSTANTS
#define END_PUSH_CONSTANTS
#define UNIFORM(n) layout (location = n) uniform
#define BINDING_INPUT_BUFFER 0
#define BINDING_SWIZZLE_BUFFER 0
#define BINDING_INPUT_BUFFER 1
#define BINDING_OUTPUT_IMAGE 0
#endif
@@ -41,6 +43,10 @@ UNIFORM(6) uint block_height;
UNIFORM(7) uint block_height_mask;
END_PUSH_CONSTANTS
layout(binding = BINDING_SWIZZLE_BUFFER, std430) readonly buffer SwizzleTable {
uint swizzle_table[];
};
#if HAS_EXTENDED_TYPES
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU8 { uint8_t u8data[]; };
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU16 { uint16_t u16data[]; };
@@ -65,19 +71,9 @@ const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT + GOB_SIZE_Z_SHI
const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1, GOB_SIZE_Y - 1);
uint SwizzleTable(uint pos) {
const uint t[8] = uint[](
0x12100200, 0x13110301, 0x16140604, 0x17150705,
0x1a180a08, 0x1b190b09, 0x1e1c0e0c, 0x1f1d0f0d
);
const uint i = pos >> 4;
const uint h = (t[i / 4] >> ((i % 4) * 8)) & 0xff;
return (h << 4) | (pos & 0xf);
}
uint SwizzleOffset(uvec2 pos) {
pos = pos & SWIZZLE_MASK;
return SwizzleTable(pos.y * 64 + pos.x);
return swizzle_table[pos.y * 64 + pos.x];
}
uvec4 ReadTexel(uint offset) {
@@ -11,8 +11,9 @@
#define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants {
#define END_PUSH_CONSTANTS };
#define UNIFORM(n)
#define BINDING_INPUT_BUFFER 0
#define BINDING_OUTPUT_IMAGE 1
#define BINDING_SWIZZLE_BUFFER 0
#define BINDING_INPUT_BUFFER 1
#define BINDING_OUTPUT_IMAGE 2
#else // ^^^ Vulkan ^^^ // vvv OpenGL vvv
@@ -25,7 +26,8 @@
#define BEGIN_PUSH_CONSTANTS
#define END_PUSH_CONSTANTS
#define UNIFORM(n) layout (location = n) uniform
#define BINDING_INPUT_BUFFER 0
#define BINDING_SWIZZLE_BUFFER 0
#define BINDING_INPUT_BUFFER 1
#define BINDING_OUTPUT_IMAGE 0
#endif
@@ -43,6 +45,10 @@ UNIFORM(8) uint block_depth;
UNIFORM(9) uint block_depth_mask;
END_PUSH_CONSTANTS
layout(binding = BINDING_SWIZZLE_BUFFER, std430) readonly buffer SwizzleTable {
uint swizzle_table[];
};
#if HAS_EXTENDED_TYPES
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU8 { uint8_t u8data[]; };
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU16 { uint16_t u16data[]; };
@@ -67,19 +73,9 @@ const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT + GOB_SIZE_Z_SHI
const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1, GOB_SIZE_Y - 1);
uint SwizzleTable(uint pos) {
const uint t[8] = uint[](
0x12100200, 0x13110301, 0x16140604, 0x17150705,
0x1a180a08, 0x1b190b09, 0x1e1c0e0c, 0x1f1d0f0d
);
const uint i = pos >> 4;
const uint h = (t[i / 4] >> ((i % 4) * 8)) & 0xff;
return (h << 4) | (pos & 0xf);
}
uint SwizzleOffset(uvec2 pos) {
pos = pos & SWIZZLE_MASK;
return SwizzleTable(pos.y * 64 + pos.x);
return swizzle_table[pos.y * 64 + pos.x];
}
uvec4 ReadTexel(uint offset) {

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