mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-10-04 05:18:20 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3de6cfbe4a |
@@ -1,92 +0,0 @@
|
||||
#!/bin/sh -ex
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
NUM_JOBS=$(nproc 2>/dev/null || getconf _NPROCESSORS_ONLN 2>/dev/null || echo 2)
|
||||
|
||||
: "${CCACHE:=false}"
|
||||
RETURN=0
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: $0 [-b|--build-type BUILD_TYPE] [-o|--outdir OUTPUT_DIRECTORY]
|
||||
|
||||
Build script for Emscripten (using wasm64).
|
||||
|
||||
Options:
|
||||
--build-type Set the CMake build type (Release|RelWithDebInfo|MinSizeRel|Debug)
|
||||
Default: Release
|
||||
--outdir Set the output directory
|
||||
Default: build
|
||||
|
||||
EOF
|
||||
exit "$RETURN"
|
||||
}
|
||||
|
||||
die() {
|
||||
echo "-- ! $*" >&2
|
||||
RETURN=1 usage
|
||||
}
|
||||
|
||||
type() {
|
||||
[ -z "$1" ] && die "You must specify a valid type."
|
||||
TYPE="$1"
|
||||
}
|
||||
|
||||
outdir() {
|
||||
[ -z "$1" ] && die "You must specify a valid output directory."
|
||||
OUTDIR="$1"
|
||||
}
|
||||
|
||||
while true; do
|
||||
case "$1" in
|
||||
-r|--release) DEVEL=false ;;
|
||||
-b|--build-type) type "$2"; shift ;;
|
||||
-o|--outdir) outdir "$2"; shift ;;
|
||||
-h|--help) usage ;;
|
||||
*) break ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
: "${TYPE:=Release}"
|
||||
: "${DEVEL:=true}"
|
||||
: "${OUTDIR:=build}"
|
||||
|
||||
# -sMEMORY64 must be specified twice, see below
|
||||
# The CMake toolchain file will match against MEMORY64 but will fail to match if:
|
||||
# - it's either -sMEMORY64
|
||||
# - or it's either -sMEMORY64=1
|
||||
# The line in question:
|
||||
# if (CMAKE_C_FLAGS MATCHES "MEMORY64")
|
||||
# However why need to specify -sMEMORY64=1 then? Oh that's because if you didn't set
|
||||
# the =1, it would assume you meant =0, which equates to not specifying it at all
|
||||
# This seems to be fixed in later versions but occurs atleast on 4.0.3-git and below.
|
||||
emcmake cmake -B "$OUTDIR" -G "Unix Makefiles" \
|
||||
-DCMAKE_BUILD_TYPE=${TYPE} \
|
||||
-DENABLE_OPENGL=OFF \
|
||||
-DENABLE_LTO=OFF \
|
||||
-DENABLE_QT=OFF \
|
||||
-DENABLE_UNITY_BUILD=OFF \
|
||||
-DENABLE_QT_TRANSLATION=OFF \
|
||||
-DENABLE_CUBEB=OFF \
|
||||
-DENABLE_LIBUSB=OFF \
|
||||
-DENABLE_UPDATE_CHECKER=OFF \
|
||||
-DENABLE_WEB_SERVICE=OFF \
|
||||
-DUSE_DISCORD_PRESENCE=OFF \
|
||||
-DENABLE_WIFI_SCAN=OFF \
|
||||
-DUSE_FASTER_LINKER=ON \
|
||||
-DYUZU_STATIC_BUILD=ON \
|
||||
-DYUZU_USE_BUNDLED_OPENSSL=OFF \
|
||||
-DYUZU_USE_EXTERNAL_FFMPEG=ON \
|
||||
-Dzstd_FORCE_BUNDLED=ON \
|
||||
-DOpenSSL_FORCE_BUNDLED=ON \
|
||||
-DEMSCRIPTEN_SYSTEM_PROCESSOR=wasm \
|
||||
-DCMAKE_C_FLAGS="-s MEMORY64 -m64 -pipe -sMEMORY64=1" \
|
||||
-DCMAKE_CXX_FLAGS="-s MEMORY64 -m64 -pipe -sMEMORY64=1" \
|
||||
-DCMAKE_EXE_LINKER_FLAGS="-sMEMORY64=1 -m64 -Wl,-mwasm64 -sASYNCIFY=1" \
|
||||
-DCMAKE_C_LINK_FLAGS="-sMEMORY64=1 -m64 -Wl,-mwasm64 -sASYNCIFY=1" \
|
||||
-DCMAKE_CXX_LINK_FLAGS="-sMEMORY64=1 -m64 -Wl,-mwasm64 -sASYNCIFY=1"
|
||||
|
||||
cmake --build "$OUTDIR" -- -j$NUM_JOBS
|
||||
@@ -1,40 +0,0 @@
|
||||
diff --git a/cmake/ConfigureOpenSSL.cmake b/cmake/ConfigureOpenSSL.cmake
|
||||
index 3012e05..2ae23ff 100644
|
||||
--- a/cmake/ConfigureOpenSSL.cmake
|
||||
+++ b/cmake/ConfigureOpenSSL.cmake
|
||||
@@ -108,7 +108,8 @@ function(configure_openssl)
|
||||
)
|
||||
|
||||
if(NOT "${CONFIGURE_OPTIONS_OLD}" STREQUAL "")
|
||||
- if(CONFIGURE_OPTIONS STREQUAL CONFIGURE_OPTIONS_OLD)
|
||||
+ # TODO(lizzie): Emscripten has issues with rebuilding due to the wrapper it uses
|
||||
+ if(CMAKE_SYSTEM_NAME MATCHES "Emscripten" OR CONFIGURE_OPTIONS STREQUAL CONFIGURE_OPTIONS_OLD)
|
||||
message(STATUS "Found previous configure results. Don't perform configuration")
|
||||
return()
|
||||
endif()
|
||||
@@ -134,10 +135,24 @@ function(configure_openssl)
|
||||
set(VERBOSE_OPTION OUTPUT_QUIET)
|
||||
endif()
|
||||
|
||||
+ if (CMAKE_SYSTEM_NAME MATCHES "Emscripten")
|
||||
+ set(EMSCRIPTEN_CMAKE_WRAPPER "emcmake")
|
||||
+ find_program(EMCC emcc REQUIRED)
|
||||
+ set(EMSCRIPTEN_LINKER ${EMCC})
|
||||
+ list(APPEND CONFIGURE_COMMAND wasm64)
|
||||
+ else()
|
||||
+ set(EMSCRIPTEN_CMAKE_WRAPPER "")
|
||||
+ set(EMSCRIPTEN_LINKER ${CMAKE_LINKER})
|
||||
+ endif ()
|
||||
+
|
||||
execute_process(
|
||||
- COMMAND ${CMAKE_COMMAND} -E env
|
||||
+ COMMAND ${EMSCRIPTEN_CMAKE_WRAPPER} ${CMAKE_COMMAND} -E env
|
||||
"CFLAGS=${CMAKE_C_FLAGS}"
|
||||
"CXXFLAGS=${CMAKE_CXX_FLAGS}"
|
||||
+ "LDFLAGS=${CMAKE_CXX_LINK_FLAGS}"
|
||||
+ "CC=${CMAKE_C_COMPILER}"
|
||||
+ "CXX=${CMAKE_CXX_COMPILER}"
|
||||
+ "LD=${EMSCRIPTEN_LINKER}"
|
||||
${CONFIGURE_COMMAND}
|
||||
WORKING_DIRECTORY ${CONFIGURE_BUILD_DIR}
|
||||
${VERBOSE_OPTION}
|
||||
@@ -1,112 +0,0 @@
|
||||
diff --git a/Configurations/10-main.conf b/Configurations/10-main.conf
|
||||
index e62721e..243feb4 100644
|
||||
--- a/Configurations/10-main.conf
|
||||
+++ b/Configurations/10-main.conf
|
||||
@@ -1970,6 +1970,26 @@ my %targets = (
|
||||
multilib => "64",
|
||||
},
|
||||
|
||||
+ "wasm32" => {
|
||||
+ inherit_from => [ "BASE_unix" ],
|
||||
+ CC => "emcc",
|
||||
+ CXX => "emc++",
|
||||
+ cflags => combine("--target=wasm32-unknown-emscripten", threads("-pthread")),
|
||||
+ cxxflags => combine("--target=wasm32-unknown-emscripten", threads("-pthread")),
|
||||
+ lib_cppflags => add("-DL_ENDIAN"),
|
||||
+ bn_ops => "THIRTY_TWO_BIT",
|
||||
+ },
|
||||
+ "wasm64" => {
|
||||
+ inherit_from => [ "BASE_unix" ],
|
||||
+ CC => "emcc",
|
||||
+ CXX => "emc++",
|
||||
+ cflags => combine("--target=wasm64-unknown-emscripten", threads("-pthread")),
|
||||
+ cxxflags => combine("--target=wasm64-unknown-emscripten", threads("-pthread")),
|
||||
+ lib_cppflags => add("-DL_ENDIAN"),
|
||||
+ bn_ops => "SIXTY_FOUR_BIT_LONG",
|
||||
+ },
|
||||
+
|
||||
+
|
||||
#### uClinux
|
||||
"uClinux-dist" => {
|
||||
inherit_from => [ "BASE_unix" ],
|
||||
diff --git a/crypto/rand/rand_lib.c b/crypto/rand/rand_lib.c
|
||||
index d9e8f02..7faf347 100644
|
||||
--- a/crypto/rand/rand_lib.c
|
||||
+++ b/crypto/rand/rand_lib.c
|
||||
@@ -379,17 +379,27 @@ void RAND_add(const void *buf, int num, double randomness)
|
||||
#if !defined(OPENSSL_NO_DEPRECATED_1_1_0)
|
||||
int RAND_pseudo_bytes(unsigned char *buf, int num)
|
||||
{
|
||||
+#if defined(__wasi__)
|
||||
+ arc4random_buf(buf, num);
|
||||
+ return 1;
|
||||
+#elif defined(__EMSCRIPTEN__)
|
||||
+ return 1;
|
||||
+#else
|
||||
const RAND_METHOD *meth = RAND_get_rand_method();
|
||||
|
||||
if (meth != NULL && meth->pseudorand != NULL)
|
||||
return meth->pseudorand(buf, num);
|
||||
ERR_raise(ERR_LIB_RAND, RAND_R_FUNC_NOT_IMPLEMENTED);
|
||||
return -1;
|
||||
+#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
int RAND_status(void)
|
||||
{
|
||||
+#if defined(__EMSCRIPTEN__) || defined(__wasi__)
|
||||
+ return 1;
|
||||
+#else
|
||||
EVP_RAND_CTX *rand;
|
||||
#ifndef OPENSSL_NO_DEPRECATED_3_0
|
||||
const RAND_METHOD *meth = RAND_get_rand_method();
|
||||
@@ -401,6 +411,7 @@ int RAND_status(void)
|
||||
if ((rand = RAND_get0_primary(NULL)) == NULL)
|
||||
return 0;
|
||||
return EVP_RAND_get_state(rand) == EVP_RAND_STATE_READY;
|
||||
+#endif
|
||||
}
|
||||
#else /* !FIPS_MODULE */
|
||||
|
||||
@@ -420,6 +431,12 @@ const RAND_METHOD *RAND_get_rand_method(void)
|
||||
int RAND_priv_bytes_ex(OSSL_LIB_CTX *ctx, unsigned char *buf, size_t num,
|
||||
unsigned int strength)
|
||||
{
|
||||
+#if defined(__wasi__)
|
||||
+ arc4random_buf(buf, num);
|
||||
+ return 1;
|
||||
+#elif defined(__EMSCRIPTEN__)
|
||||
+ return 1;
|
||||
+#else
|
||||
RAND_GLOBAL *dgbl;
|
||||
EVP_RAND_CTX *rand;
|
||||
#if !defined(OPENSSL_NO_DEPRECATED_3_0) && !defined(FIPS_MODULE)
|
||||
@@ -451,6 +468,7 @@ int RAND_priv_bytes_ex(OSSL_LIB_CTX *ctx, unsigned char *buf, size_t num,
|
||||
return EVP_RAND_generate(rand, buf, num, strength, 0, NULL, 0);
|
||||
|
||||
return 0;
|
||||
+#endif
|
||||
}
|
||||
|
||||
int RAND_priv_bytes(unsigned char *buf, int num)
|
||||
diff --git a/ssl/ssl_cert.c b/ssl/ssl_cert.c
|
||||
index 3d21801..e026f8f 100644
|
||||
--- a/ssl/ssl_cert.c
|
||||
+++ b/ssl/ssl_cert.c
|
||||
@@ -941,6 +941,7 @@ done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
+#ifndef OPENSSL_NO_POSIX_IO
|
||||
int SSL_add_dir_cert_subjects_to_stack(STACK_OF(X509_NAME) *stack,
|
||||
const char *dir)
|
||||
{
|
||||
@@ -1016,6 +1017,7 @@ err:
|
||||
|
||||
return ret;
|
||||
}
|
||||
+#endif
|
||||
|
||||
static int add_uris_recursive(STACK_OF(X509_NAME) *stack,
|
||||
const char *uri, int depth)
|
||||
+5
-21
@@ -377,15 +377,6 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
|
||||
# Prefer the -pthread flag on Linux.
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
# It is absolutely promordial to enable on Emscripten
|
||||
# Not only this allows to use std::thread and std::jthread without exceptions
|
||||
# but it also fixes several issues related to MT operations.
|
||||
# ...and CMake doesn't include it by default even when we specify
|
||||
# that we prefer the pthread flag; why is that? I don't know.
|
||||
if (EMSCRIPTEN)
|
||||
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:-pthread>)
|
||||
add_link_options($<$<COMPILE_LANGUAGE:C,CXX>:-pthread>)
|
||||
endif()
|
||||
|
||||
find_package(RenderDoc MODULE)
|
||||
|
||||
@@ -414,12 +405,7 @@ set(BUILD_TESTING OFF)
|
||||
set(ENABLE_TESTING OFF)
|
||||
|
||||
# boost
|
||||
if (EMSCRIPTEN)
|
||||
set(BOOST_INCLUDE_LIBRARIES algorithm icl pool container heap headers filesystem crc variant)
|
||||
set(BOOST_CONTAINER_HEADER_ONLY ON)
|
||||
else()
|
||||
set(BOOST_INCLUDE_LIBRARIES algorithm icl pool container heap asio headers process filesystem crc variant)
|
||||
endif()
|
||||
set(BOOST_INCLUDE_LIBRARIES algorithm icl pool container heap asio headers process filesystem crc variant)
|
||||
|
||||
AddJsonPackage(boost)
|
||||
|
||||
@@ -440,12 +426,10 @@ if (Boost_ADDED)
|
||||
|
||||
target_compile_options(boost_heap INTERFACE $<$<COMPILE_LANGUAGE:C,CXX>:-Wno-shadow>)
|
||||
target_compile_options(boost_icl INTERFACE $<$<COMPILE_LANGUAGE:C,CXX>:-Wno-shadow>)
|
||||
# May not exist (i.e emscripten)
|
||||
if (TARGET boost_asio)
|
||||
target_compile_options(boost_asio INTERFACE
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-conversion>
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-implicit-fallthrough>)
|
||||
endif()
|
||||
target_compile_options(boost_asio INTERFACE
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-conversion>
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-implicit-fallthrough>
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
+3
-5
@@ -115,7 +115,7 @@
|
||||
"find_args": "MODULE GLOBAL",
|
||||
"hash": "159ed94965018f2a371d45a3bfc1961e5fb1549e501ded70a6b4532d7fe99d0579c18b5195aff6e35f96f399b426cea2650ec9fb75ef80d4c9edeccb51f2e6c9",
|
||||
"options": [
|
||||
"HTTPLIB_REQUIRE_OPENSSL OFF",
|
||||
"HTTPLIB_REQUIRE_OPENSSL ON",
|
||||
"HTTPLIB_DISABLE_MACOSX_AUTOMATIC_ROOT_CERTIFICATES ON"
|
||||
],
|
||||
"patches": [
|
||||
@@ -197,8 +197,7 @@
|
||||
"min_version": "3.0.0",
|
||||
"package": "OpenSSL",
|
||||
"patches": [
|
||||
"0001-add-bundled-cert.patch",
|
||||
"0002-wasm-support.patch"
|
||||
"0001-add-bundled-cert.patch"
|
||||
],
|
||||
"repo": "openssl/openssl",
|
||||
"version": "openssl-3.6.2"
|
||||
@@ -220,8 +219,7 @@
|
||||
"0001-cpmutil-compat.patch",
|
||||
"0002-use-ccache.patch",
|
||||
"0003-use-cmake-compiler-flags.patch",
|
||||
"0004-use-shell-wrapper.patch",
|
||||
"0005-wasm-support.patch"
|
||||
"0004-use-shell-wrapper.patch"
|
||||
],
|
||||
"repo": "jimmy-park/openssl-cmake",
|
||||
"version": "3.6.2"
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
- [NetBSD](#netbsd)
|
||||
- [MSYS2](#msys2)
|
||||
- [RedoxOS](#redoxos)
|
||||
- [WebAssembly](#webassembly)
|
||||
<!-- /TOC -->
|
||||
|
||||
## Arch Linux
|
||||
@@ -243,19 +242,3 @@ find ./*/ -name "*.dll" | while read -r dll; do deps "$dll"; done
|
||||
The package install may randomly hang at times, in which case it has to be restarted. ALWAYS do a `sudo pkg update` or the chances of it hanging will be close to 90%. If "multiple" installs fail at once, try installing 1 by 1 the packages.
|
||||
|
||||
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.
|
||||
|
||||
@@ -361,14 +361,6 @@ pkgman install git cmake patch libfmt_devel nlohmann_json lz4_devel opus_devel b
|
||||
|
||||
[Caveats](./Caveats.md#haikuos).
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>WebAssembly</summary>
|
||||
|
||||
Emscripten: The default installation should provide enough.
|
||||
|
||||
[Caveats](./Caveats.md#wasm).
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>RedoxOS</summary>
|
||||
|
||||
+5
-9
@@ -94,10 +94,6 @@ function(detect_architecture_symbols)
|
||||
endfunction()
|
||||
|
||||
# arches here are put in a sane default order of importance
|
||||
# EXCEPT FOR WASM, which must be probed for FIRST, because some genius
|
||||
# decided to also allow the host architecture to be defined when building
|
||||
# for the emscripten target, absolutely lovely detail.
|
||||
#
|
||||
# notably, amd64, arm64, and riscv (in order) are BY FAR the most common
|
||||
# mips is pretty popular in embedded
|
||||
# ppc64 is pretty popular in supercomputing
|
||||
@@ -105,11 +101,6 @@ endfunction()
|
||||
# ia64 exists
|
||||
# the rest exist, but are probably less popular than ia64
|
||||
|
||||
detect_architecture_symbols(
|
||||
ARCH wasm
|
||||
SYMBOLS
|
||||
"__EMSCRIPTEN__")
|
||||
|
||||
detect_architecture_symbols(
|
||||
ARCH arm64
|
||||
SYMBOLS
|
||||
@@ -212,6 +203,11 @@ detect_architecture_symbols(
|
||||
"__loongarch__"
|
||||
"__loongarch64")
|
||||
|
||||
detect_architecture_symbols(
|
||||
ARCH wasm
|
||||
SYMBOLS
|
||||
"__EMSCRIPTEN__")
|
||||
|
||||
# "generic" target
|
||||
# If you have reached this point, you're on some as-of-yet unsupported architecture.
|
||||
# See the docs up above for known unsupported architectures
|
||||
|
||||
-25
@@ -20,9 +20,6 @@ elseif (${CMAKE_SYSTEM_NAME} STREQUAL "managarm")
|
||||
set(MANAGARM ON)
|
||||
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Haiku")
|
||||
set(HAIKUOS ON)
|
||||
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Emscripten")
|
||||
set(EMSCRIPTEN ON)
|
||||
message(WARNING "${CMAKE_LIBRARY_ARCHITECTURE} support is highly experimental!!!")
|
||||
endif()
|
||||
|
||||
# BSD
|
||||
@@ -100,25 +97,3 @@ if (MINGW)
|
||||
set(CMAKE_EXE_LINKER_FLAGS_RELEASE
|
||||
"${CMAKE_EXE_LINKER_FLAGS_RELEASE} ${MINGW_FLAGS}")
|
||||
endif()
|
||||
|
||||
if (EMSCRIPTEN)
|
||||
set(EMSCRIPTEN_C_FLAGS "-s MEMORY64 -m64 -pipe -sMEMORY64=1")
|
||||
set(EMSCRIPTEN_LINK_FLAGS "-sMEMORY64=1 -m64 -Wl,-mwasm64 -sASYNCIFY=1")
|
||||
|
||||
# This prevents FFmpeg and other libraries from assuming it's the host's CPU
|
||||
# Additionally some Emscripten installs may not be very good... generally
|
||||
set(EMSCRIPTEN_SYSTEM_PROCESSOR wasm)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EMSCRIPTEN_C_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} ${EMSCRIPTEN_C_FLAGS}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${EMSCRIPTEN_LINK_FLAGS}")
|
||||
set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} ${EMSCRIPTEN_LINK_FLAGS}")
|
||||
set(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} ${EMSCRIPTEN_LINK_FLAGS}")
|
||||
|
||||
unset(EMSCRIPTEN_C_FLAGS)
|
||||
unset(EMSCRIPTEN_LINK_FLAGS)
|
||||
endif()
|
||||
|
||||
# awesome
|
||||
if (FREEBSD OR DRAGONFLYBSD)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L${CMAKE_SYSROOT}/usr/local/lib")
|
||||
endif()
|
||||
|
||||
Vendored
+19
-54
@@ -35,37 +35,26 @@ if (NOT YUZU_USE_BUNDLED_FFMPEG)
|
||||
elseif (NOT (CMAKE_HOST_SYSTEM_PROCESSOR MATCHES CMAKE_SYSTEM_PROCESSOR
|
||||
AND CMAKE_HOST_SYSTEM_NAME MATCHES CMAKE_SYSTEM_NAME))
|
||||
string(TOLOWER "${CMAKE_SYSTEM_NAME}" FFmpeg_SYSTEM_NAME)
|
||||
# All of these platforms are supported by ffmpeg as native build OSes
|
||||
# anything else (like Redox or Managarm or PS4) is NOT natively supported
|
||||
# hence, assume the "unix like" is just "none" for the sake of OUR sanity.
|
||||
# If YOUR OS/platform has actual native support:
|
||||
# 1. fucking congrats
|
||||
# 2. feel free to add it on the condition below
|
||||
if (NOT (NETBSD OR SOLARIS OR FREEBSD OR OPENBSD OR DRAGONFLYBSD OR HAIKU
|
||||
OR LINUX OR MSYS OR WIN32 OR ANDROID OR APPLE))
|
||||
if (FFmpeg_SYSTEM_NAME STREQUAL "openorbis" OR FFmpeg_SYSTEM_NAME STREQUAL "managarm")
|
||||
set(FFmpeg_SYSTEM_NAME "none")
|
||||
endif()
|
||||
# TODO: Can we really do better? Auto-detection? Something clever?
|
||||
list(APPEND FFmpeg_CROSS_COMPILE_FLAGS
|
||||
--enable-cross-compile
|
||||
--arch="${CMAKE_SYSTEM_PROCESSOR}"
|
||||
--target-os="${FFmpeg_SYSTEM_NAME}")
|
||||
# Emscripten doesn't use CMAKE_SYSROOT I'm afraid, but it does well use EMSCRIPTEN_SYSROOT
|
||||
if (EMSCRIPTEN)
|
||||
list(APPEND FFmpeg_CROSS_COMPILE_FLAGS --sysroot="${EMSCRIPTEN_SYSROOT}")
|
||||
else()
|
||||
list(APPEND FFmpeg_CROSS_COMPILE_FLAGS --sysroot="${CMAKE_SYSROOT}")
|
||||
endif()
|
||||
--target-os="${FFmpeg_SYSTEM_NAME}"
|
||||
--sysroot="${CMAKE_SYSROOT}"
|
||||
)
|
||||
if (DEFINED FFmpeg_CROSS_PREFIX)
|
||||
list(APPEND FFmpeg_CROSS_COMPILE_FLAGS --cross-prefix="${FFmpeg_CROSS_PREFIX}")
|
||||
else()
|
||||
message(WARNING "Please set FFmpeg_CROSS_PREFIX to your cross toolchain prefix, for example: ${CMAKE_STAGING_PREFIX}/bin/${CMAKE_SYSTEM_PROCESSOR}-${CMAKE_SYSTEM_NAME}")
|
||||
message(WARNING "Please set FFmpeg_CROSS_PREFIX to your cross toolchain prefix, for example: \${CMAKE_STAGING_PREFIX}/bin/${CMAKE_SYSTEM_PROCESSOR}-${CMAKE_SYSTEM_NAME}-")
|
||||
endif()
|
||||
set(FFmpeg_IS_CROSS_COMPILING TRUE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (OPENORBIS OR MANAGARM OR EMSCRIPTEN)
|
||||
if (OPENORBIS OR MANAGARM)
|
||||
# Doesn't support VA-API, don't go thru the embarrassment of trying to enable it
|
||||
list(APPEND FFmpeg_HWACCEL_FLAGS --disable-vaapi)
|
||||
elseif (ANDROID)
|
||||
@@ -173,7 +162,8 @@ if (OPENORBIS)
|
||||
-lSceUserService
|
||||
-lSceSysmodule
|
||||
-lSceNet
|
||||
-lSceLibcInternal)
|
||||
-lSceLibcInternal
|
||||
)
|
||||
list(APPEND FFmpeg_CROSS_COMPILE_FLAGS
|
||||
--disable-pthreads
|
||||
--extra-cflags=${CMAKE_SYSROOT}/usr/include
|
||||
@@ -184,18 +174,8 @@ elseif (MANAGARM)
|
||||
# Required for proper stuff
|
||||
list(APPEND FFmpeg_CROSS_COMPILE_FLAGS
|
||||
--disable-pthreads
|
||||
--extra-libs="${FFmpeg_CROSS_COMPILE_LIBS}")
|
||||
endif()
|
||||
|
||||
# Usually used by Emscripten
|
||||
if (DEFINED CMAKE_AR)
|
||||
list(APPEND FFmpeg_CROSS_COMPILE_FLAGS --ar=${CMAKE_AR})
|
||||
endif()
|
||||
if (DEFINED CMAKE_NM)
|
||||
list(APPEND FFmpeg_CROSS_COMPILE_FLAGS --nm=${CMAKE_NM})
|
||||
endif()
|
||||
if (DEFINED CMAKE_RANLIB)
|
||||
list(APPEND FFmpeg_CROSS_COMPILE_FLAGS --ranlib=${CMAKE_RANLIB})
|
||||
--extra-libs="${FFmpeg_CROSS_COMPILE_LIBS}"
|
||||
)
|
||||
endif()
|
||||
|
||||
if (YUZU_USE_BUNDLED_FFMPEG)
|
||||
@@ -225,7 +205,6 @@ else()
|
||||
# Build FFmpeg from externals
|
||||
message(STATUS "Using FFmpeg from externals")
|
||||
|
||||
set(FFmpeg_LD ${CMAKE_LINKER})
|
||||
if (CMAKE_SYSTEM_PROCESSOR MATCHES "(x86_64|amd64)")
|
||||
# FFmpeg has source that requires one of nasm or yasm to assemble it.
|
||||
# REQUIRED throws an error if not found here during configuration rather than during compilation.
|
||||
@@ -234,12 +213,6 @@ else()
|
||||
message(FATAL_ERROR "One of either `nasm` or `yasm` not found but is required.")
|
||||
endif()
|
||||
endif()
|
||||
if (CMAKE_SYSTEM_NAME STREQUAL "Emscripten")
|
||||
# TODO: this is 100% redundant but we need it because CMAKE_LINKER can resolve to ld.lld
|
||||
# which is NOT compatible
|
||||
find_program(EMCC NAMES emcc REQUIRED)
|
||||
set(FFmpeg_LD ${EMCC})
|
||||
endif()
|
||||
|
||||
find_program(AUTOCONF autoconf)
|
||||
if ("${AUTOCONF}" STREQUAL "AUTOCONF-NOTFOUND")
|
||||
@@ -273,13 +246,7 @@ else()
|
||||
CACHE PATH "Paths to FFmpeg libraries" FORCE)
|
||||
endforeach()
|
||||
|
||||
# Some SDKs (especially wasm) require us, actually, WANT us to use their "emconfigure"
|
||||
# otherwise they will cry a billion tears
|
||||
if (EMSCRIPTEN)
|
||||
find_program(FFmpeg_CONFIGURE_WRAPPER emconfigure REQUIRED)
|
||||
else()
|
||||
find_program(FFmpeg_CONFIGURE_WRAPPER bash REQUIRED)
|
||||
endif()
|
||||
find_program(BASH_PROGRAM bash REQUIRED)
|
||||
|
||||
# `configure` parameters builds only exactly what yuzu needs from FFmpeg
|
||||
# `--disable-vdpau` is needed to avoid linking issues
|
||||
@@ -289,7 +256,7 @@ else()
|
||||
OUTPUT
|
||||
${FFmpeg_MAKEFILE}
|
||||
COMMAND
|
||||
${FFmpeg_CONFIGURE_WRAPPER} ${FFmpeg_PREFIX}/configure
|
||||
${BASH_PROGRAM} ${FFmpeg_PREFIX}/configure
|
||||
--disable-avdevice
|
||||
--disable-avformat
|
||||
--disable-doc
|
||||
@@ -298,10 +265,6 @@ else()
|
||||
--disable-ffprobe
|
||||
--disable-network
|
||||
--disable-swresample
|
||||
--disable-autodetect
|
||||
--disable-runtime-cpudetect
|
||||
--disable-debug
|
||||
--disable-programs
|
||||
--enable-decoder=h264
|
||||
--enable-decoder=vp8
|
||||
--enable-decoder=vp9
|
||||
@@ -309,7 +272,7 @@ else()
|
||||
--enable-pic
|
||||
--cc=${FFmpeg_CC}
|
||||
--cxx=${FFmpeg_CXX}
|
||||
--ld=${FFmpeg_LD}
|
||||
--ld=${CMAKE_LINKER}
|
||||
--extra-cflags=${CMAKE_C_FLAGS}
|
||||
--extra-cxxflags=${CMAKE_CXX_FLAGS}
|
||||
--extra-ldflags=${CMAKE_C_LINK_FLAGS}
|
||||
@@ -325,7 +288,11 @@ else()
|
||||
|
||||
# Workaround for Ubuntu 18.04's older version of make not being able to call make as a child
|
||||
# with context of the jobserver. Also helps ninja users.
|
||||
cmake_host_system_information(RESULT SYSTEM_THREADS QUERY NUMBER_OF_LOGICAL_CORES)
|
||||
execute_process(
|
||||
COMMAND
|
||||
nproc
|
||||
OUTPUT_VARIABLE
|
||||
SYSTEM_THREADS)
|
||||
|
||||
set(FFmpeg_BUILD_LIBRARIES ${FFmpeg_LIBRARIES})
|
||||
|
||||
@@ -333,8 +300,6 @@ else()
|
||||
if (LINUX OR ANDROID OR APPLE OR WIN32 OR FREEBSD)
|
||||
set(FFmpeg_MAKE_ARGS -j${SYSTEM_THREADS})
|
||||
else()
|
||||
# No GNU make implies that this system may be highly non-GNU
|
||||
find_program(MAKE make required)
|
||||
set(FFmpeg_MAKE_ARGS "")
|
||||
endif()
|
||||
|
||||
@@ -342,7 +307,7 @@ else()
|
||||
OUTPUT
|
||||
${FFmpeg_BUILD_LIBRARIES}
|
||||
COMMAND
|
||||
${MAKE} ${FFmpeg_MAKE_ARGS}
|
||||
gmake ${FFmpeg_MAKE_ARGS}
|
||||
WORKING_DIRECTORY
|
||||
${FFmpeg_BUILD_DIR}
|
||||
)
|
||||
|
||||
Vendored
-2
@@ -14,9 +14,7 @@
|
||||
namespace Tz {
|
||||
|
||||
namespace {
|
||||
#ifndef EINVAL
|
||||
#define EINVAL 22
|
||||
#endif
|
||||
|
||||
static Rule gmtmem{};
|
||||
static Rule* const gmtptr = &gmtmem;
|
||||
|
||||
+2
-5
@@ -177,6 +177,7 @@ else()
|
||||
$<$<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(
|
||||
@@ -190,11 +191,7 @@ else()
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-nullability-completeness>)
|
||||
endif()
|
||||
|
||||
if (ARCHITECTURE_wasm)
|
||||
# we are evil but fmt is even more evil
|
||||
add_compile_options(
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-shorten-64-to-32>)
|
||||
elseif (ARCHITECTURE_x86_64)
|
||||
if (ARCHITECTURE_x86_64)
|
||||
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:-mcx16>)
|
||||
if (LINUX OR FREEBSD)
|
||||
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:-mtls-dialect=gnu2>)
|
||||
|
||||
@@ -236,11 +236,7 @@ else()
|
||||
target_link_libraries(common PUBLIC Boost::headers)
|
||||
endif()
|
||||
target_link_libraries(common PRIVATE OpenSSL::SSL)
|
||||
target_link_libraries(common PUBLIC Boost::filesystem httplib::httplib nlohmann_json::nlohmann_json)
|
||||
if (NOT EMSCRIPTEN)
|
||||
# Emscripten is: "bring your own implementation", boost doesn't add upstream support sadly
|
||||
target_link_libraries(common PRIVATE Boost::context)
|
||||
endif()
|
||||
target_link_libraries(common PUBLIC Boost::filesystem Boost::context httplib::httplib nlohmann_json::nlohmann_json)
|
||||
|
||||
if (lz4_ADDED)
|
||||
target_include_directories(common PRIVATE ${lz4_SOURCE_DIR}/lib)
|
||||
|
||||
+11
-84
@@ -11,12 +11,7 @@
|
||||
#include "common/fiber.h"
|
||||
#include "common/virtual_buffer.h"
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten.h>
|
||||
#include <emscripten/fiber.h>
|
||||
#else
|
||||
#include <boost/context/detail/fcontext.hpp>
|
||||
#endif
|
||||
|
||||
namespace Common {
|
||||
|
||||
@@ -27,103 +22,36 @@ constexpr size_t DEFAULT_STACK_SIZE = 512 * 4096;
|
||||
#endif
|
||||
constexpr u32 CANARY_VALUE = 0xDEADBEEF;
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
struct Fiber::FiberImpl {
|
||||
FiberImpl() {}
|
||||
|
||||
u32 canary_1 = CANARY_VALUE;
|
||||
std::array<u8, DEFAULT_STACK_SIZE> stack{};
|
||||
std::array<u8, DEFAULT_STACK_SIZE> astack{};
|
||||
u32 canary_2 = CANARY_VALUE;
|
||||
|
||||
emscripten_fiber_t* context{nullptr};
|
||||
|
||||
std::mutex guard;
|
||||
std::function<void()> entry_point;
|
||||
std::shared_ptr<Fiber> previous_fiber;
|
||||
bool is_thread_fiber = false;
|
||||
bool released = false;
|
||||
};
|
||||
|
||||
Fiber::Fiber(std::function<void()>&& entry_point_func) : impl{std::make_unique<FiberImpl>()} {
|
||||
impl->entry_point = std::move(entry_point_func);
|
||||
emscripten_fiber_init(impl->context, [](void *user_data) -> void {
|
||||
auto* fiber = static_cast<Fiber*>(user_data);
|
||||
ASSERT(fiber && fiber->impl && fiber->impl->previous_fiber && fiber->impl->previous_fiber->impl);
|
||||
ASSERT(fiber->impl->canary_1 == CANARY_VALUE);
|
||||
ASSERT(fiber->impl->canary_2 == CANARY_VALUE);
|
||||
fiber->impl->previous_fiber->impl->context = fiber->impl->context;
|
||||
fiber->impl->previous_fiber->impl->guard.unlock();
|
||||
fiber->impl->previous_fiber.reset();
|
||||
fiber->impl->entry_point();
|
||||
UNREACHABLE();
|
||||
}, impl.get(), impl->stack.data(), impl->stack.size(), impl->astack.data(), impl->astack.size());
|
||||
}
|
||||
|
||||
Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {}
|
||||
|
||||
Fiber::~Fiber() {
|
||||
if (!impl->released) {
|
||||
// Make sure the Fiber is not being used
|
||||
const bool locked = impl->guard.try_lock();
|
||||
ASSERT(locked && "Destroying a fiber that's still running");
|
||||
if (locked) {
|
||||
impl->guard.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Fiber::Exit() {
|
||||
ASSERT(impl->is_thread_fiber && "Exiting non main thread fiber");
|
||||
if (impl->is_thread_fiber) {
|
||||
impl->guard.unlock();
|
||||
impl->released = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Fiber::YieldTo(std::weak_ptr<Fiber> weak_from, Fiber& to) {
|
||||
to.impl->guard.lock();
|
||||
to.impl->previous_fiber = weak_from.lock();
|
||||
emscripten_fiber_swap(to.impl->context, to.impl->previous_fiber->impl->context);
|
||||
// "from" might no longer be valid if the thread was killed
|
||||
if (auto from = weak_from.lock()) {
|
||||
if (from->impl->previous_fiber == nullptr) {
|
||||
ASSERT(false && "previous_fiber is nullptr!");
|
||||
} else {
|
||||
from->impl->previous_fiber->impl->guard.unlock();
|
||||
from->impl->previous_fiber.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
|
||||
std::shared_ptr<Fiber> fiber = std::shared_ptr<Fiber>{new Fiber()};
|
||||
fiber->impl->guard.lock();
|
||||
fiber->impl->is_thread_fiber = true;
|
||||
return fiber;
|
||||
}
|
||||
#else
|
||||
struct Fiber::FiberImpl {
|
||||
FiberImpl() {}
|
||||
|
||||
u32 canary_1 = CANARY_VALUE;
|
||||
std::array<u8, DEFAULT_STACK_SIZE> stack{};
|
||||
std::array<u8, DEFAULT_STACK_SIZE> rewind_stack{};
|
||||
u32 canary_2 = CANARY_VALUE;
|
||||
|
||||
boost::context::detail::fcontext_t context{};
|
||||
boost::context::detail::fcontext_t rewind_context{};
|
||||
|
||||
std::mutex guard;
|
||||
std::function<void()> entry_point;
|
||||
std::function<void()> rewind_point;
|
||||
std::shared_ptr<Fiber> previous_fiber;
|
||||
|
||||
u8* stack_limit = nullptr;
|
||||
u8* rewind_stack_limit = nullptr;
|
||||
bool is_thread_fiber = false;
|
||||
bool released = false;
|
||||
};
|
||||
|
||||
void Fiber::SetRewindPoint(std::function<void()>&& rewind_func) {
|
||||
impl->rewind_point = std::move(rewind_func);
|
||||
}
|
||||
|
||||
Fiber::Fiber(std::function<void()>&& entry_point_func) : impl{std::make_unique<FiberImpl>()} {
|
||||
impl->entry_point = std::move(entry_point_func);
|
||||
impl->stack_limit = impl->stack.data();
|
||||
impl->rewind_stack_limit = impl->rewind_stack.data();
|
||||
u8* stack_base = impl->stack_limit + DEFAULT_STACK_SIZE;
|
||||
impl->context = boost::context::detail::make_fcontext(stack_base, impl->stack.size(), [](boost::context::detail::transfer_t transfer) -> void {
|
||||
auto* fiber = static_cast<Fiber*>(transfer.data);
|
||||
@@ -144,7 +72,7 @@ Fiber::~Fiber() {
|
||||
if (!impl->released) {
|
||||
// Make sure the Fiber is not being used
|
||||
const bool locked = impl->guard.try_lock();
|
||||
ASSERT(locked && "Destroying a fiber that's still running");
|
||||
ASSERT_MSG(locked, "Destroying a fiber that's still running");
|
||||
if (locked) {
|
||||
impl->guard.unlock();
|
||||
}
|
||||
@@ -152,7 +80,7 @@ Fiber::~Fiber() {
|
||||
}
|
||||
|
||||
void Fiber::Exit() {
|
||||
ASSERT(impl->is_thread_fiber && "Exiting non main thread fiber");
|
||||
ASSERT_MSG(impl->is_thread_fiber, "Exiting non main thread fiber");
|
||||
if (impl->is_thread_fiber) {
|
||||
impl->guard.unlock();
|
||||
impl->released = true;
|
||||
@@ -182,6 +110,5 @@ std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
|
||||
fiber->impl->is_thread_fiber = true;
|
||||
return fiber;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace Common
|
||||
|
||||
+2
-1
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
@@ -45,6 +45,7 @@ public:
|
||||
/// Fiber 'from' must be the currently running fiber.
|
||||
static void YieldTo(std::weak_ptr<Fiber> weak_from, Fiber& to);
|
||||
[[nodiscard]] static std::shared_ptr<Fiber> ThreadToFiber();
|
||||
void SetRewindPoint(std::function<void()>&& rewind_func);
|
||||
/// Only call from main thread's fiber
|
||||
void Exit();
|
||||
private:
|
||||
|
||||
@@ -136,11 +136,6 @@ public:
|
||||
eden_path = GetDataDirectory("XDG_DATA_HOME") / EDEN_DIR;
|
||||
eden_path_cache = GetDataDirectory("XDG_CACHE_HOME") / EDEN_DIR;
|
||||
eden_path_config = GetDataDirectory("XDG_CONFIG_HOME") / EDEN_DIR;
|
||||
#if defined(__EMSCRIPTEN__) || defined(__wasi__) || defined(__managarm__)
|
||||
// folders MAY not exist in this distrobution/OS
|
||||
CreateParentDir(GetDataDirectory("XDG_CONFIG_HOME"));
|
||||
CreateParentDir(GetDataDirectory("XDG_CACHE_HOME"));
|
||||
#endif
|
||||
} else {
|
||||
eden_path_cache = eden_path / CACHE_DIR;
|
||||
eden_path_config = eden_path / CONFIG_DIR;
|
||||
|
||||
@@ -394,7 +394,7 @@ private:
|
||||
::Common::unordered_map<size_t, size_t> placeholder_host_pointers; ///< Placeholder backing offset
|
||||
};
|
||||
|
||||
#elif defined(__OPENORBIS__) || defined(__managarm__) || defined(__wasi__) || defined(__EMSCRIPTEN__)
|
||||
#elif defined(__OPENORBIS__) || defined(__managarm__)
|
||||
// None of the luxuries of POSIX, all of the suffering
|
||||
// For managarm: see https://github.com/managarm/managarm/issues/1370
|
||||
#else // ^^^ Windows ^^^ vvv POSIX vvv
|
||||
@@ -689,7 +689,7 @@ HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_)
|
||||
: backing_size(backing_size_)
|
||||
, virtual_size(virtual_size_)
|
||||
{
|
||||
#if defined(__OPENORBIS__) || defined(__managarm__) || defined(__wasi__) || defined(__EMSCRIPTEN__)
|
||||
#if defined(__OPENORBIS__) || defined(__managarm__)
|
||||
LOG_WARNING(HW_Memory, "Platform doesn't support fastmem");
|
||||
fallback_buffer.emplace(backing_size);
|
||||
backing_base = fallback_buffer->data();
|
||||
@@ -723,7 +723,7 @@ HostMemory::HostMemory(HostMemory&&) noexcept = default;
|
||||
HostMemory& HostMemory::operator=(HostMemory&&) noexcept = default;
|
||||
|
||||
void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms, bool separate_heap) {
|
||||
#if !(defined(__OPENORBIS__) || defined(__managarm__) || defined(__wasi__) || defined(__EMSCRIPTEN__))
|
||||
#if !(defined(__OPENORBIS__) || defined(__managarm__))
|
||||
ASSERT(virtual_offset % PageAlignment == 0);
|
||||
ASSERT(host_offset % PageAlignment == 0);
|
||||
ASSERT(length % PageAlignment == 0);
|
||||
@@ -737,7 +737,7 @@ void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length, M
|
||||
}
|
||||
|
||||
void HostMemory::Unmap(size_t virtual_offset, size_t length, bool separate_heap) {
|
||||
#if !(defined(__OPENORBIS__) || defined(__managarm__) || defined(__wasi__) || defined(__EMSCRIPTEN__))
|
||||
#if !(defined(__OPENORBIS__) || defined(__managarm__))
|
||||
ASSERT(virtual_offset % PageAlignment == 0);
|
||||
ASSERT(length % PageAlignment == 0);
|
||||
ASSERT(virtual_offset + length <= virtual_size);
|
||||
@@ -749,7 +749,7 @@ void HostMemory::Unmap(size_t virtual_offset, size_t length, bool separate_heap)
|
||||
}
|
||||
|
||||
void HostMemory::Protect(size_t virtual_offset, size_t length, MemoryPermission perm) {
|
||||
#if !(defined(__OPENORBIS__) || defined(__managarm__) || defined(__wasi__) || defined(__EMSCRIPTEN__))
|
||||
#if !(defined(__OPENORBIS__) || defined(__managarm__))
|
||||
ASSERT(virtual_offset % PageAlignment == 0);
|
||||
ASSERT(length % PageAlignment == 0);
|
||||
ASSERT(virtual_offset + length <= virtual_size);
|
||||
@@ -768,7 +768,7 @@ void HostMemory::ClearBackingRegion(size_t physical_offset, size_t length, u32 f
|
||||
}
|
||||
|
||||
void HostMemory::EnableDirectMappedAddress() {
|
||||
#if !(defined(__OPENORBIS__) || defined(__managarm__) || defined(__wasi__) || defined(__EMSCRIPTEN__))
|
||||
#if !(defined(__OPENORBIS__) || defined(__managarm__))
|
||||
if (impl) {
|
||||
impl->EnableDirectMappedAddress();
|
||||
virtual_size += reinterpret_cast<uintptr_t>(virtual_base);
|
||||
|
||||
@@ -77,7 +77,7 @@ private:
|
||||
size_t backing_size{};
|
||||
size_t virtual_size{};
|
||||
|
||||
#if !(defined(__OPENORBIS__) || defined(__managarm__) || defined(__wasi__) || defined(__EMSCRIPTEN__))
|
||||
#if !(defined(__OPENORBIS__) || defined(__managarm__))
|
||||
// Low level handler for the platform dependent memory routines
|
||||
class Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
|
||||
@@ -490,8 +490,6 @@ void SetCurrentThreadPriority(ThreadPriority new_priority) {
|
||||
LOG_DEBUG(Common, "Could not set thread nice value to {}: {}", nice_value,
|
||||
GetLastErrorMsg());
|
||||
}
|
||||
#elif defined(__EMSCRIPTEN__)
|
||||
// TODO: set priority?
|
||||
#else
|
||||
const s32 max_prio = sched_get_priority_max(SCHED_OTHER);
|
||||
const s32 min_prio = sched_get_priority_min(SCHED_OTHER);
|
||||
@@ -536,8 +534,6 @@ void SetCurrentThreadName(const char* name) {
|
||||
// See for reference
|
||||
// https://gitlab.freedesktop.org/mesa/mesa/-/blame/main/src/util/u_thread.c?ref_type=heads#L75
|
||||
(void)name;
|
||||
#elif defined(__EMSCRIPTEN__)
|
||||
// TODO: set thread name?
|
||||
#else
|
||||
pthread_setname_np(pthread_self(), name);
|
||||
#endif
|
||||
|
||||
+5
-12
@@ -38,6 +38,10 @@ add_library(core STATIC
|
||||
debugger/debugger.cpp
|
||||
debugger/debugger.h
|
||||
debugger/debugger_interface.h
|
||||
debugger/gdbstub.cpp
|
||||
debugger/gdbstub.h
|
||||
debugger/gdbstub_arch.cpp
|
||||
debugger/gdbstub_arch.h
|
||||
device_memory.cpp
|
||||
device_memory.h
|
||||
device_memory_manager.h
|
||||
@@ -1162,14 +1166,6 @@ add_library(core STATIC
|
||||
tools/freezer.h
|
||||
tools/renderdoc.cpp
|
||||
tools/renderdoc.h)
|
||||
if (NOT EMSCRIPTEN)
|
||||
# incompatible with wasm's async model
|
||||
target_sources(core PRIVATE
|
||||
debugger/gdbstub.cpp
|
||||
debugger/gdbstub.h
|
||||
debugger/gdbstub_arch.cpp
|
||||
debugger/gdbstub_arch.h)
|
||||
endif()
|
||||
|
||||
if (ENABLE_WIFI_SCAN)
|
||||
target_sources(core PRIVATE internal_network/wifi_scanner.cpp)
|
||||
@@ -1213,10 +1209,7 @@ target_include_directories(core PRIVATE ${OPUS_INCLUDE_DIRS})
|
||||
target_link_libraries(core PUBLIC common PRIVATE audio_core hid_core network video_core nx_tzdb tz)
|
||||
|
||||
if (BOOST_NO_HEADERS)
|
||||
target_link_libraries(core PUBLIC Boost::container Boost::heap Boost::crc)
|
||||
if (NOT EMSCRIPTEN)
|
||||
target_link_libraries(core PUBLIC Boost::asio Boost::process)
|
||||
endif()
|
||||
target_link_libraries(core PUBLIC Boost::container Boost::heap Boost::asio Boost::process Boost::crc)
|
||||
else()
|
||||
target_link_libraries(core PUBLIC Boost::headers)
|
||||
endif()
|
||||
|
||||
@@ -6,20 +6,19 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef __EMSCRIPTEN__
|
||||
#include <dynarmic/interface/halt_reason.h>
|
||||
#endif
|
||||
|
||||
#include "core/arm/arm_interface.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
#ifndef __EMSCRIPTEN__
|
||||
constexpr Dynarmic::HaltReason StepThread = Dynarmic::HaltReason::Step;
|
||||
constexpr Dynarmic::HaltReason DataAbort = Dynarmic::HaltReason::MemoryAbort;
|
||||
constexpr Dynarmic::HaltReason BreakLoop = Dynarmic::HaltReason::UserDefined2;
|
||||
constexpr Dynarmic::HaltReason SupervisorCall = Dynarmic::HaltReason::UserDefined3;
|
||||
constexpr Dynarmic::HaltReason InstructionBreakpoint = Dynarmic::HaltReason::UserDefined4;
|
||||
constexpr Dynarmic::HaltReason PrefetchAbort = Dynarmic::HaltReason::UserDefined6;
|
||||
|
||||
constexpr HaltReason TranslateHaltReason(Dynarmic::HaltReason hr) {
|
||||
static_assert(u64(HaltReason::StepThread) == u64(StepThread));
|
||||
static_assert(u64(HaltReason::DataAbort) == u64(DataAbort));
|
||||
@@ -29,6 +28,5 @@ constexpr HaltReason TranslateHaltReason(Dynarmic::HaltReason hr) {
|
||||
static_assert(u64(HaltReason::PrefetchAbort) == u64(PrefetchAbort));
|
||||
return HaltReason(hr);
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -6,54 +6,30 @@
|
||||
|
||||
#include <mutex>
|
||||
#include <utility>
|
||||
|
||||
#if defined(__EMSCRIPTEN__) || defined(__wasi__)
|
||||
// TODO: gdb stub compat with emscripten?
|
||||
#else
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/version.hpp>
|
||||
|
||||
#if BOOST_VERSION > 108400 && (!defined(_WINDOWS) && !defined(__ANDROID__)) || defined(YUZU_BOOST_v1)
|
||||
#define USE_BOOST_v1
|
||||
#endif
|
||||
|
||||
#ifdef USE_BOOST_v1
|
||||
#include <boost/process/v1/async_pipe.hpp>
|
||||
#else
|
||||
#include <boost/process/async_pipe.hpp>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "common/logging.h"
|
||||
#include "common/polyfill_thread.h"
|
||||
#include "common/thread.h"
|
||||
#include "core/core.h"
|
||||
#include "core/debugger/debugger.h"
|
||||
#if defined(__EMSCRIPTEN__) || defined(__wasi__)
|
||||
// TODO: gdbstub with emscripten?
|
||||
#else
|
||||
#include "core/debugger/debugger_interface.h"
|
||||
#include "core/debugger/gdbstub.h"
|
||||
#endif
|
||||
#include "core/hle/kernel/global_scheduler_context.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
|
||||
#if defined(__EMSCRIPTEN__) || defined(__wasi__)
|
||||
namespace Core {
|
||||
// Dummy
|
||||
class DebuggerImpl {
|
||||
char pad;
|
||||
};
|
||||
Debugger::Debugger(Core::System& system, u16 port) {}
|
||||
Debugger::~Debugger() = default;
|
||||
bool Debugger::NotifyThreadStopped(Kernel::KThread* thread) {
|
||||
return false;
|
||||
}
|
||||
bool Debugger::NotifyThreadWatchpoint(Kernel::KThread* thread, const Kernel::DebugWatchpoint& watch) {
|
||||
return false;
|
||||
}
|
||||
void Debugger::NotifyShutdown() {}
|
||||
} // namespace Core
|
||||
#else
|
||||
template <typename Readable, typename Buffer, typename Callback>
|
||||
static void AsyncReceiveInto(Readable& r, Buffer& buffer, Callback&& c) {
|
||||
static_assert(std::is_trivial_v<Buffer>);
|
||||
@@ -424,4 +400,3 @@ void Debugger::NotifyShutdown() {
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
#endif
|
||||
|
||||
@@ -7,14 +7,8 @@
|
||||
#include <random>
|
||||
#include "common/scope_exit.h"
|
||||
#include "common/settings.h"
|
||||
|
||||
#include "core/arm/exclusive_monitor.h"
|
||||
#ifndef __EMSCRIPTEN__
|
||||
#include "core/arm/dynarmic/arm_dynarmic.h"
|
||||
#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
|
||||
#include "core/arm/dynarmic/arm_dynarmic_32.h"
|
||||
#include "core/arm/dynarmic/arm_dynarmic_64.h"
|
||||
#endif
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_scoped_resource_reservation.h"
|
||||
@@ -24,6 +18,8 @@
|
||||
#include "core/hle/kernel/k_thread_queue.h"
|
||||
#include "core/hle/kernel/k_worker_task_manager.h"
|
||||
|
||||
#include "core/arm/dynarmic/arm_dynarmic_32.h"
|
||||
#include "core/arm/dynarmic/arm_dynarmic_64.h"
|
||||
#ifdef HAS_NCE
|
||||
#include "core/arm/nce/arm_nce.h"
|
||||
#endif
|
||||
@@ -1304,11 +1300,9 @@ void KProcess::LoadModule(KernelCore& kernel, CodeSet code_set, KProcessAddress
|
||||
}
|
||||
|
||||
void KProcess::InitializeInterfaces(KernelCore& kernel) {
|
||||
#ifdef __EMSCRIPTEN__
|
||||
ASSERT(false && "unimplemented");
|
||||
#else
|
||||
m_exclusive_monitor =
|
||||
Core::MakeExclusiveMonitor(this->GetMemory(), Core::Hardware::NUM_CPU_CORES);
|
||||
|
||||
#ifdef HAS_NCE
|
||||
if (this->IsApplication() && Settings::IsNceEnabled()) {
|
||||
for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++)
|
||||
@@ -1328,7 +1322,6 @@ void KProcess::InitializeInterfaces(KernelCore& kernel) {
|
||||
static_cast<Core::DynarmicExclusiveMonitor&>(*m_exclusive_monitor), i);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool KProcess::InsertWatchpoint(KernelCore& kernel, KProcessAddress addr, u64 size, DebugWatchpointType type) {
|
||||
|
||||
@@ -88,22 +88,15 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
|
||||
|
||||
// BEGONE cold clones of lambdas, for I have merged you all into a SINGLE lambda instead of
|
||||
// spamming lambdas like it's some kind of lambda calculus class
|
||||
std::vector<std::pair<std::string_view, void (*)(Core::System&)>> rt_services{
|
||||
for (auto const& e : std::vector<std::pair<std::string_view, void (*)(Core::System&)>>{
|
||||
{"audio", &Audio::LoopProcess},
|
||||
{"FS", &FileSystem::LoopProcess},
|
||||
{"ldn", &LDN::LoopProcess},
|
||||
{"nvservices", &Nvidia::LoopProcess},
|
||||
{"bsdsocket", &Sockets::LoopProcess},
|
||||
};
|
||||
#if defined(__EMSCRIPTEN__) || defined(__wasi__) || defined(__OPENORBIS__)
|
||||
for (auto const& e : rt_services)
|
||||
kernel.RunOnGuestCoreProcess(std::string(e.first), [&system, f = e.second] { f(system); });
|
||||
kernel.RunOnGuestCoreProcess("vi", [&, token] { VI::LoopProcess(system, token); });
|
||||
#else
|
||||
for (auto const& e : rt_services)
|
||||
})
|
||||
kernel.RunOnHostCoreProcess(std::string(e.first), [&system, f = e.second] { f(system); }).detach();
|
||||
kernel.RunOnHostCoreProcess("vi", [&, token] { VI::LoopProcess(system, token); }).detach();
|
||||
#endif
|
||||
kernel.RunOnHostCoreProcess("vi", [&, token] { VI::LoopProcess(system, token); }).detach();
|
||||
// Avoid cold clones of lambdas -- succintly
|
||||
for (auto const& e : std::vector<std::pair<std::string_view, void (*)(Core::System&)>>{
|
||||
{"sm", &SM::LoopProcess},
|
||||
@@ -127,10 +120,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
|
||||
{"glue", &Glue::LoopProcess},
|
||||
{"grc", &GRC::LoopProcess},
|
||||
{"hid", &HID::LoopProcess},
|
||||
// Must match with src/core/CMakeLists.txt for target_source of jit.cpp
|
||||
#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64) || defined(ARCHITECTURE_riscv64) || defined(ARCHITECTURE_loongarch64)
|
||||
{"jit", &JIT::LoopProcess},
|
||||
#endif
|
||||
{"lbl", &LBL::LoopProcess},
|
||||
{"Loader", &LDR::LoopProcess},
|
||||
{"LogManager.Prod", &LM::LoopProcess},
|
||||
|
||||
@@ -28,10 +28,7 @@ enum class NpadMcuState : u32 {
|
||||
struct NpadMcuHolder {
|
||||
NpadMcuState state;
|
||||
INSERT_PADDING_BYTES(0x4);
|
||||
union {
|
||||
IAbstractedPad* abstracted_pad;
|
||||
u64 abstracted_pad_raw;
|
||||
};
|
||||
IAbstractedPad* abstracted_pad;
|
||||
};
|
||||
static_assert(sizeof(NpadMcuHolder) == 0x10, "NpadMcuHolder is an invalid size");
|
||||
|
||||
|
||||
@@ -33,15 +33,9 @@ public:
|
||||
bool is_created{};
|
||||
bool is_mapped{};
|
||||
INSERT_PADDING_BYTES(0x5);
|
||||
union {
|
||||
Kernel::KSharedMemory* shared_memory = nullptr;
|
||||
u64 shared_memory_raw;
|
||||
};
|
||||
Kernel::KSharedMemory* shared_memory;
|
||||
INSERT_PADDING_BYTES(0x38);
|
||||
union {
|
||||
SharedMemoryFormat* address = nullptr;
|
||||
u64 address_raw;
|
||||
};
|
||||
SharedMemoryFormat* address = nullptr;
|
||||
};
|
||||
// Correct size is 0x50 bytes
|
||||
static_assert(sizeof(SharedMemoryHolder) == 0x50, "SharedMemoryHolder is an invalid size");
|
||||
|
||||
@@ -15,6 +15,8 @@ add_library(input_common STATIC
|
||||
drivers/tas_input.h
|
||||
drivers/touch_screen.cpp
|
||||
drivers/touch_screen.h
|
||||
drivers/udp_client.cpp
|
||||
drivers/udp_client.h
|
||||
drivers/virtual_amiibo.cpp
|
||||
drivers/virtual_amiibo.h
|
||||
drivers/virtual_gamepad.cpp
|
||||
@@ -32,12 +34,8 @@ add_library(input_common STATIC
|
||||
input_poller.cpp
|
||||
input_poller.h
|
||||
main.cpp
|
||||
main.h)
|
||||
if (NOT EMSCRIPTEN)
|
||||
target_sources(input_common PRIVATE
|
||||
drivers/udp_client.cpp
|
||||
drivers/udp_client.h)
|
||||
endif()
|
||||
main.h
|
||||
)
|
||||
|
||||
if (MSVC)
|
||||
target_compile_options(input_common PRIVATE
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "input_common/drivers/mouse.h"
|
||||
#include "input_common/drivers/tas_input.h"
|
||||
#include "input_common/drivers/touch_screen.h"
|
||||
#include "input_common/drivers/udp_client.h"
|
||||
#include "input_common/drivers/virtual_amiibo.h"
|
||||
#include "input_common/drivers/virtual_gamepad.h"
|
||||
#include "input_common/helpers/stick_from_buttons.h"
|
||||
@@ -20,9 +21,6 @@
|
||||
#include "input_common/input_mapping.h"
|
||||
#include "input_common/input_poller.h"
|
||||
#include "input_common/main.h"
|
||||
#ifndef __EMSCRIPTEN__
|
||||
#include "input_common/drivers/udp_client.h"
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_LIBUSB
|
||||
#include "input_common/drivers/gc_adapter.h"
|
||||
@@ -84,9 +82,7 @@ struct InputSubsystem::Impl {
|
||||
#ifdef ENABLE_LIBUSB
|
||||
RegisterEngine("gcpad", gcadapter);
|
||||
#endif
|
||||
#ifndef __EMSCRIPTEN__
|
||||
RegisterEngine("cemuhookudp", udp_client);
|
||||
#endif
|
||||
RegisterEngine("tas", tas_input);
|
||||
RegisterEngine("camera", camera);
|
||||
#ifdef __ANDROID__
|
||||
@@ -120,9 +116,7 @@ struct InputSubsystem::Impl {
|
||||
#ifdef ENABLE_LIBUSB
|
||||
UnregisterEngine(gcadapter);
|
||||
#endif
|
||||
#ifndef __EMSCRIPTEN__
|
||||
UnregisterEngine(udp_client);
|
||||
#endif
|
||||
UnregisterEngine(tas_input);
|
||||
UnregisterEngine(camera);
|
||||
#ifdef __ANDROID__
|
||||
@@ -158,10 +152,8 @@ struct InputSubsystem::Impl {
|
||||
auto gcadapter_devices = gcadapter->GetInputDevices();
|
||||
devices.insert(devices.end(), gcadapter_devices.begin(), gcadapter_devices.end());
|
||||
#endif
|
||||
#ifndef __EMSCRIPTEN__
|
||||
auto udp_devices = udp_client->GetInputDevices();
|
||||
devices.insert(devices.end(), udp_devices.begin(), udp_devices.end());
|
||||
#endif
|
||||
#ifdef HAVE_SDL3
|
||||
auto joycon_devices = joycon->GetInputDevices();
|
||||
devices.insert(devices.end(), joycon_devices.begin(), joycon_devices.end());
|
||||
@@ -194,11 +186,9 @@ struct InputSubsystem::Impl {
|
||||
return gcadapter;
|
||||
}
|
||||
#endif
|
||||
#ifndef __EMSCRIPTEN__
|
||||
if (engine == udp_client->GetEngineName()) {
|
||||
return udp_client;
|
||||
}
|
||||
#endif
|
||||
#ifdef HAVE_SDL3
|
||||
if (engine == sdl->GetEngineName()) {
|
||||
return sdl;
|
||||
@@ -281,11 +271,9 @@ struct InputSubsystem::Impl {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
#ifndef __EMSCRIPTEN__
|
||||
if (engine == udp_client->GetEngineName()) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
if (engine == tas_input->GetEngineName()) {
|
||||
return true;
|
||||
}
|
||||
@@ -312,9 +300,7 @@ struct InputSubsystem::Impl {
|
||||
#ifdef ENABLE_LIBUSB
|
||||
gcadapter->BeginConfiguration();
|
||||
#endif
|
||||
#ifndef __EMSCRIPTEN__
|
||||
udp_client->BeginConfiguration();
|
||||
#endif
|
||||
#ifdef HAVE_SDL3
|
||||
sdl->BeginConfiguration();
|
||||
joycon->BeginConfiguration();
|
||||
@@ -330,9 +316,7 @@ struct InputSubsystem::Impl {
|
||||
#ifdef ENABLE_LIBUSB
|
||||
gcadapter->EndConfiguration();
|
||||
#endif
|
||||
#ifndef __EMSCRIPTEN__
|
||||
udp_client->EndConfiguration();
|
||||
#endif
|
||||
#ifdef HAVE_SDL3
|
||||
sdl->EndConfiguration();
|
||||
joycon->EndConfiguration();
|
||||
@@ -357,9 +341,7 @@ struct InputSubsystem::Impl {
|
||||
std::shared_ptr<Mouse> mouse;
|
||||
std::shared_ptr<TouchScreen> touch_screen;
|
||||
std::shared_ptr<TasInput::Tas> tas_input;
|
||||
#ifndef __EMSCRIPTEN__
|
||||
std::shared_ptr<CemuhookUDP::UDPClient> udp_client;
|
||||
#endif
|
||||
std::shared_ptr<Camera> camera;
|
||||
std::shared_ptr<VirtualAmiibo> virtual_amiibo;
|
||||
std::shared_ptr<VirtualGamepad> virtual_gamepad;
|
||||
@@ -488,9 +470,7 @@ bool InputSubsystem::IsStickInverted(const Common::ParamPackage& params) const {
|
||||
}
|
||||
|
||||
void InputSubsystem::ReloadInputDevices() {
|
||||
#ifndef __EMSCRIPTEN__
|
||||
impl->udp_client.get()->ReloadSockets();
|
||||
#endif
|
||||
}
|
||||
|
||||
void InputSubsystem::BeginMapping(Polling::InputType type) {
|
||||
|
||||
@@ -234,13 +234,12 @@ add_library(shader_recompiler STATIC
|
||||
ir_opt/texture_pass.cpp
|
||||
ir_opt/vendor_workaround_pass.cpp
|
||||
ir_opt/verification_pass.cpp
|
||||
object_pool.h
|
||||
profile.h
|
||||
program_header.h
|
||||
runtime_info.h
|
||||
shader_info.h
|
||||
varying_state.h
|
||||
|
||||
shader_pool.h
|
||||
)
|
||||
|
||||
target_link_libraries(shader_recompiler PUBLIC common fmt::fmt sirit::sirit)
|
||||
@@ -257,7 +256,8 @@ else()
|
||||
# Bracket depth determines maximum size of a fold expression in Clang since 9c9974c3ccb6.
|
||||
# And this in turns limits the size of a std::array.
|
||||
$<$<CXX_COMPILER_ID:Clang>:-fbracket-depth=1024>
|
||||
$<$<CXX_COMPILER_ID:AppleClang>:-fbracket-depth=1024>)
|
||||
$<$<CXX_COMPILER_ID:AppleClang>:-fbracket-depth=1024>
|
||||
)
|
||||
endif()
|
||||
|
||||
create_target_directory_groups(shader_recompiler)
|
||||
|
||||
@@ -31,7 +31,7 @@ struct CbufWordKey {
|
||||
|
||||
struct CbufWordKeyHash {
|
||||
constexpr size_t operator()(const CbufWordKey& k) const noexcept {
|
||||
return size_t((u64(k.index) << 32) ^ u64(k.offset));
|
||||
return (size_t(k.index) << 32) ^ k.offset;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
Block::Block(ObjectPool<Inst>& inst_pool_) : inst_pool{&inst_pool_} {}
|
||||
Block::Block(boost::container::stable_vector<Inst>& inst_pool_) : inst_pool{&inst_pool_} {}
|
||||
|
||||
Block::~Block() = default;
|
||||
|
||||
@@ -23,13 +23,12 @@ void Block::AppendNewInst(Opcode op, std::initializer_list<Value> args) {
|
||||
}
|
||||
|
||||
Block::iterator Block::PrependNewInst(iterator insertion_point, const Inst& base_inst) {
|
||||
Inst* const inst{inst_pool->Create(base_inst)};
|
||||
Inst* const inst{&inst_pool->emplace_back(base_inst)};
|
||||
return instructions.insert(insertion_point, *inst);
|
||||
}
|
||||
|
||||
Block::iterator Block::PrependNewInst(iterator insertion_point, Opcode op,
|
||||
std::initializer_list<Value> args, u32 flags) {
|
||||
Inst* const inst{inst_pool->Create(op, flags)};
|
||||
Block::iterator Block::PrependNewInst(iterator insertion_point, Opcode op, std::initializer_list<Value> args, u32 flags) {
|
||||
Inst* const inst{&inst_pool->emplace_back(op, flags)};
|
||||
const auto result_it{instructions.insert(insertion_point, *inst)};
|
||||
|
||||
if (inst->NumArgs() != args.size()) {
|
||||
@@ -53,12 +52,10 @@ void Block::AddBranch(Block* block) {
|
||||
block->imm_predecessors.push_back(this);
|
||||
}
|
||||
|
||||
static std::string BlockToIndex(const std::map<const Block*, size_t>& block_to_index,
|
||||
Block* block) {
|
||||
if (const auto it{block_to_index.find(block)}; it != block_to_index.end()) {
|
||||
static std::string BlockToIndex(const std::map<const Block*, size_t>& block_to_index, Block* block) {
|
||||
if (const auto it{block_to_index.find(block)}; it != block_to_index.end())
|
||||
return fmt::format("{{Block ${}}}", it->second);
|
||||
}
|
||||
return fmt::format("$<unknown block {:016x}>", reinterpret_cast<u64>(block));
|
||||
return fmt::format("$<unknown block {:016x}>", u64(block));
|
||||
}
|
||||
|
||||
static size_t InstIndex(std::map<const Inst*, size_t>& inst_to_index, size_t& inst_index,
|
||||
|
||||
@@ -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 2021 yuzu Emulator Project
|
||||
@@ -13,11 +13,11 @@
|
||||
#include <bit>
|
||||
#include <numeric>
|
||||
#include <boost/intrusive/list.hpp>
|
||||
#include <boost/container/stable_vector.hpp>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "shader_recompiler/frontend/ir/condition.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
#include "shader_recompiler/object_pool.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
@@ -30,7 +30,7 @@ public:
|
||||
using reverse_iterator = InstructionList::reverse_iterator;
|
||||
using const_reverse_iterator = InstructionList::const_reverse_iterator;
|
||||
|
||||
explicit Block(ObjectPool<Inst>& inst_pool_);
|
||||
explicit Block(boost::container::stable_vector<Inst>& inst_pool_);
|
||||
~Block();
|
||||
|
||||
Block(const Block&) = delete;
|
||||
@@ -170,7 +170,7 @@ public:
|
||||
|
||||
private:
|
||||
/// Memory pool for instruction list
|
||||
ObjectPool<Inst>* inst_pool;
|
||||
boost::container::stable_vector<Inst>* inst_pool;
|
||||
|
||||
/// List of instructions in this block
|
||||
InstructionList instructions;
|
||||
|
||||
@@ -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 2021 yuzu Emulator Project
|
||||
@@ -174,11 +174,10 @@ bool Block::Contains(Location pc) const noexcept {
|
||||
return pc >= begin && pc < end;
|
||||
}
|
||||
|
||||
Function::Function(ObjectPool<Block>& block_pool, Location start_address)
|
||||
: entrypoint{start_address} {
|
||||
Function::Function(boost::container::stable_vector<Block>& block_pool, Location start_address) : entrypoint{start_address} {
|
||||
Label& label{labels.emplace_back()};
|
||||
label.address = start_address;
|
||||
label.block = block_pool.Create(Block{});
|
||||
label.block = &block_pool.emplace_back(Block{});
|
||||
label.block->begin = start_address;
|
||||
label.block->end = start_address;
|
||||
label.block->end_class = EndClass::Branch;
|
||||
@@ -187,12 +186,13 @@ Function::Function(ObjectPool<Block>& block_pool, Location start_address)
|
||||
label.block->branch_false = nullptr;
|
||||
}
|
||||
|
||||
CFG::CFG(Environment& env_, ObjectPool<Block>& block_pool_, Location start_address,
|
||||
bool exits_to_dispatcher_)
|
||||
: env{env_}, block_pool{block_pool_}, program_start{start_address}, exits_to_dispatcher{
|
||||
exits_to_dispatcher_} {
|
||||
CFG::CFG(Environment& env_, boost::container::stable_vector<Block>& block_pool_, Location start_address, bool exits_to_dispatcher_)
|
||||
: env{env_}
|
||||
, block_pool{block_pool_}
|
||||
, program_start{start_address}
|
||||
, exits_to_dispatcher{exits_to_dispatcher_} {
|
||||
if (exits_to_dispatcher) {
|
||||
dispatch_block = block_pool.Create(Block{});
|
||||
dispatch_block = &block_pool.emplace_back(Block{});
|
||||
dispatch_block->begin = {};
|
||||
dispatch_block->end = {};
|
||||
dispatch_block->end_class = EndClass::Exit;
|
||||
@@ -371,7 +371,7 @@ void CFG::AnalyzeCondInst(Block* block, FunctionId function_id, Location pc,
|
||||
return;
|
||||
}
|
||||
// Create a virtual block and a conditional block
|
||||
Block* const conditional_block{block_pool.Create()};
|
||||
Block* const conditional_block{&block_pool.emplace_back()};
|
||||
Block virtual_block{};
|
||||
virtual_block.begin = block->begin.Virtual();
|
||||
virtual_block.end = block->begin.Virtual();
|
||||
@@ -546,7 +546,7 @@ Block* CFG::AddLabel(Block* block, Stack stack, Location pc, FunctionId function
|
||||
if (label_it != function.labels.end()) {
|
||||
return label_it->block;
|
||||
}
|
||||
Block* const new_block{block_pool.Create()};
|
||||
Block* const new_block{&block_pool.emplace_back()};
|
||||
new_block->begin = pc;
|
||||
new_block->end = pc;
|
||||
new_block->end_class = EndClass::Branch;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -9,6 +12,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include <boost/container/small_vector.hpp>
|
||||
#include <boost/container/stable_vector.hpp>
|
||||
#include <boost/intrusive/set.hpp>
|
||||
|
||||
#include "shader_recompiler/environment.h"
|
||||
@@ -17,7 +21,6 @@
|
||||
#include "shader_recompiler/frontend/maxwell/instruction.h"
|
||||
#include "shader_recompiler/frontend/maxwell/location.h"
|
||||
#include "shader_recompiler/frontend/maxwell/opcodes.h"
|
||||
#include "shader_recompiler/object_pool.h"
|
||||
|
||||
namespace Shader::Maxwell::Flow {
|
||||
|
||||
@@ -96,7 +99,7 @@ struct Label {
|
||||
};
|
||||
|
||||
struct Function {
|
||||
explicit Function(ObjectPool<Block>& block_pool, Location start_address);
|
||||
explicit Function(boost::container::stable_vector<Block>& block_pool, Location start_address);
|
||||
|
||||
Location entrypoint;
|
||||
boost::container::small_vector<Label, 16> labels;
|
||||
@@ -110,8 +113,7 @@ class CFG {
|
||||
};
|
||||
|
||||
public:
|
||||
explicit CFG(Environment& env, ObjectPool<Block>& block_pool, Location start_address,
|
||||
bool exits_to_dispatcher = false);
|
||||
explicit CFG(Environment& env, boost::container::stable_vector<Block>& block_pool, Location start_address, bool exits_to_dispatcher = false);
|
||||
|
||||
CFG& operator=(const CFG&) = delete;
|
||||
CFG(const CFG&) = delete;
|
||||
@@ -138,27 +140,20 @@ private:
|
||||
/// Inspect already visited blocks.
|
||||
/// Return true when the block has already been visited
|
||||
bool InspectVisitedBlocks(FunctionId function_id, const Label& label);
|
||||
|
||||
AnalysisState AnalyzeInst(Block* block, FunctionId function_id, Location pc);
|
||||
|
||||
void AnalyzeCondInst(Block* block, FunctionId function_id, Location pc, EndClass insn_end_class,
|
||||
IR::Condition cond);
|
||||
void AnalyzeCondInst(Block* block, FunctionId function_id, Location pc, EndClass insn_end_class, IR::Condition cond);
|
||||
|
||||
/// Return true when the branch instruction is confirmed to be a branch
|
||||
bool AnalyzeBranch(Block* block, FunctionId function_id, Location pc, Instruction inst,
|
||||
Opcode opcode);
|
||||
|
||||
void AnalyzeBRA(Block* block, FunctionId function_id, Location pc, Instruction inst,
|
||||
bool is_absolute);
|
||||
AnalysisState AnalyzeBRX(Block* block, Location pc, Instruction inst, bool is_absolute,
|
||||
FunctionId function_id);
|
||||
bool AnalyzeBranch(Block* block, FunctionId function_id, Location pc, Instruction inst, Opcode opcode);
|
||||
void AnalyzeBRA(Block* block, FunctionId function_id, Location pc, Instruction inst, bool is_absolute);
|
||||
AnalysisState AnalyzeBRX(Block* block, Location pc, Instruction inst, bool is_absolute, FunctionId function_id);
|
||||
AnalysisState AnalyzeEXIT(Block* block, FunctionId function_id, Location pc, Instruction inst);
|
||||
|
||||
/// Return the branch target block id
|
||||
Block* AddLabel(Block* block, Stack stack, Location pc, FunctionId function_id);
|
||||
|
||||
Environment& env;
|
||||
ObjectPool<Block>& block_pool;
|
||||
boost::container::stable_vector<Block>& block_pool;
|
||||
boost::container::small_vector<Function, 1> functions;
|
||||
Location program_start;
|
||||
bool exits_to_dispatcher{};
|
||||
|
||||
@@ -13,143 +13,19 @@
|
||||
|
||||
#include <fmt/ranges.h>
|
||||
|
||||
#include <boost/intrusive/list.hpp>
|
||||
|
||||
#include <ranges>
|
||||
#include "shader_recompiler/shader_pool.h"
|
||||
#include "shader_recompiler/environment.h"
|
||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||
#include "shader_recompiler/frontend/ir/ir_emitter.h"
|
||||
#include "shader_recompiler/frontend/maxwell/structured_control_flow.h"
|
||||
#include "shader_recompiler/frontend/maxwell/translate/translate.h"
|
||||
#include "shader_recompiler/frontend/maxwell/translate_program.h"
|
||||
#include "shader_recompiler/frontend/maxwell/control_flow.h"
|
||||
#include "shader_recompiler/host_translate_info.h"
|
||||
#include "shader_recompiler/object_pool.h"
|
||||
|
||||
namespace Shader::Maxwell {
|
||||
namespace {
|
||||
struct Statement;
|
||||
|
||||
// Use normal_link because we are not guaranteed to destroy the tree in order
|
||||
using ListBaseHook =
|
||||
boost::intrusive::list_base_hook<boost::intrusive::link_mode<boost::intrusive::normal_link>>;
|
||||
|
||||
using Tree = boost::intrusive::list<Statement,
|
||||
// Allow using Statement without a definition
|
||||
boost::intrusive::base_hook<ListBaseHook>,
|
||||
// Avoid linear complexity on splice, size is never called
|
||||
boost::intrusive::constant_time_size<false>>;
|
||||
using Node = Tree::iterator;
|
||||
|
||||
enum class StatementType {
|
||||
Code,
|
||||
Goto,
|
||||
Label,
|
||||
If,
|
||||
Loop,
|
||||
Break,
|
||||
Return,
|
||||
Kill,
|
||||
Unreachable,
|
||||
Function,
|
||||
Identity,
|
||||
Not,
|
||||
Or,
|
||||
SetVariable,
|
||||
SetIndirectBranchVariable,
|
||||
Variable,
|
||||
IndirectBranchCond,
|
||||
};
|
||||
|
||||
bool HasChildren(StatementType type) {
|
||||
switch (type) {
|
||||
case StatementType::If:
|
||||
case StatementType::Loop:
|
||||
case StatementType::Function:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
struct Goto {};
|
||||
struct Label {};
|
||||
struct If {};
|
||||
struct Loop {};
|
||||
struct Break {};
|
||||
struct Return {};
|
||||
struct Kill {};
|
||||
struct Unreachable {};
|
||||
struct FunctionTag {};
|
||||
struct Identity {};
|
||||
struct Not {};
|
||||
struct Or {};
|
||||
struct SetVariable {};
|
||||
struct SetIndirectBranchVariable {};
|
||||
struct Variable {};
|
||||
struct IndirectBranchCond {};
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 26495) // Always initialize a member variable, expected in Statement
|
||||
#endif
|
||||
struct Statement : ListBaseHook {
|
||||
Statement(const Flow::Block* block_, Statement* up_)
|
||||
: block{block_}, up{up_}, type{StatementType::Code} {}
|
||||
Statement(Goto, Statement* cond_, Node label_, Statement* up_)
|
||||
: label{label_}, cond{cond_}, up{up_}, type{StatementType::Goto} {}
|
||||
Statement(Label, u32 id_, Statement* up_) : id{id_}, up{up_}, type{StatementType::Label} {}
|
||||
Statement(If, Statement* cond_, Tree&& children_, Statement* up_)
|
||||
: children{std::move(children_)}, cond{cond_}, up{up_}, type{StatementType::If} {}
|
||||
Statement(Loop, Statement* cond_, Tree&& children_, Statement* up_)
|
||||
: children{std::move(children_)}, cond{cond_}, up{up_}, type{StatementType::Loop} {}
|
||||
Statement(Break, Statement* cond_, Statement* up_)
|
||||
: cond{cond_}, up{up_}, type{StatementType::Break} {}
|
||||
Statement(Return, Statement* up_) : up{up_}, type{StatementType::Return} {}
|
||||
Statement(Kill, Statement* up_) : up{up_}, type{StatementType::Kill} {}
|
||||
Statement(Unreachable, Statement* up_) : up{up_}, type{StatementType::Unreachable} {}
|
||||
Statement(FunctionTag) : children{}, type{StatementType::Function} {}
|
||||
Statement(Identity, IR::Condition cond_, Statement* up_)
|
||||
: guest_cond{cond_}, up{up_}, type{StatementType::Identity} {}
|
||||
Statement(Not, Statement* op_, Statement* up_) : op{op_}, up{up_}, type{StatementType::Not} {}
|
||||
Statement(Or, Statement* op_a_, Statement* op_b_, Statement* up_)
|
||||
: op_a{op_a_}, op_b{op_b_}, up{up_}, type{StatementType::Or} {}
|
||||
Statement(SetVariable, u32 id_, Statement* op_, Statement* up_)
|
||||
: op{op_}, id{id_}, up{up_}, type{StatementType::SetVariable} {}
|
||||
Statement(SetIndirectBranchVariable, IR::Reg branch_reg_, s32 branch_offset_, Statement* up_)
|
||||
: branch_offset{branch_offset_},
|
||||
branch_reg{branch_reg_}, up{up_}, type{StatementType::SetIndirectBranchVariable} {}
|
||||
Statement(Variable, u32 id_, Statement* up_)
|
||||
: id{id_}, up{up_}, type{StatementType::Variable} {}
|
||||
Statement(IndirectBranchCond, u32 location_, Statement* up_)
|
||||
: location{location_}, up{up_}, type{StatementType::IndirectBranchCond} {}
|
||||
|
||||
~Statement() {
|
||||
if (HasChildren(type)) {
|
||||
std::destroy_at(&children);
|
||||
}
|
||||
}
|
||||
|
||||
union {
|
||||
const Flow::Block* block;
|
||||
Node label;
|
||||
Tree children;
|
||||
IR::Condition guest_cond;
|
||||
Statement* op;
|
||||
Statement* op_a;
|
||||
u32 location;
|
||||
s32 branch_offset;
|
||||
};
|
||||
union {
|
||||
Statement* cond;
|
||||
Statement* op_b;
|
||||
u32 id;
|
||||
IR::Reg branch_reg;
|
||||
};
|
||||
Statement* up{};
|
||||
StatementType type;
|
||||
};
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
std::string DumpExpr(const Statement* stmt) {
|
||||
switch (stmt->type) {
|
||||
@@ -314,7 +190,9 @@ bool NeedsLift(Node goto_stmt, Node label_stmt) noexcept {
|
||||
|
||||
class GotoPass {
|
||||
public:
|
||||
explicit GotoPass(Flow::CFG& cfg, ObjectPool<Statement>& stmt_pool) : pool{stmt_pool} {
|
||||
explicit GotoPass(Flow::CFG& cfg, ShaderPools& pools_)
|
||||
: pools{pools_}
|
||||
{
|
||||
std::vector gotos{BuildTree(cfg)};
|
||||
const auto end{gotos.rend()};
|
||||
for (auto goto_stmt = gotos.rbegin(); goto_stmt != end; ++goto_stmt) {
|
||||
@@ -385,16 +263,14 @@ private:
|
||||
return gotos;
|
||||
}
|
||||
|
||||
void BuildTree(Flow::CFG& cfg, Flow::Function& function, u32& label_id,
|
||||
std::vector<Node>& gotos, Node function_insert_point,
|
||||
std::optional<Node> return_label) {
|
||||
Statement* const false_stmt{pool.Create(Identity{}, IR::Condition{false}, &root_stmt)};
|
||||
void BuildTree(Flow::CFG& cfg, Flow::Function& function, u32& label_id, std::vector<Node>& gotos, Node function_insert_point, std::optional<Node> return_label) {
|
||||
Statement* const false_stmt{&pools.stmt.emplace_back(Identity{}, IR::Condition{false}, &root_stmt)};
|
||||
Tree& root{root_stmt.children};
|
||||
::Common::unordered_map<Flow::Block*, Node> local_labels;
|
||||
local_labels.reserve(function.blocks.size());
|
||||
|
||||
for (Flow::Block& block : function.blocks) {
|
||||
Statement* const label{pool.Create(Label{}, label_id, &root_stmt)};
|
||||
Statement* const label{&pools.stmt.emplace_back(Label{}, label_id, &root_stmt)};
|
||||
const Node label_it{root.insert(function_insert_point, *label)};
|
||||
local_labels.emplace(&block, label_it);
|
||||
++label_id;
|
||||
@@ -406,46 +282,39 @@ private:
|
||||
|
||||
// Reset goto variables before the first block and after its respective label
|
||||
const auto make_reset_variable{[&]() -> Statement& {
|
||||
return *pool.Create(SetVariable{}, label->id, false_stmt, &root_stmt);
|
||||
return pools.stmt.emplace_back(SetVariable{}, label->id, false_stmt, &root_stmt);
|
||||
}};
|
||||
root.push_front(make_reset_variable());
|
||||
root.insert(ip, make_reset_variable());
|
||||
root.insert(ip, *pool.Create(&block, &root_stmt));
|
||||
root.insert(ip, pools.stmt.emplace_back(&block, &root_stmt));
|
||||
|
||||
switch (block.end_class) {
|
||||
case Flow::EndClass::Branch: {
|
||||
Statement* const always_cond{
|
||||
pool.Create(Identity{}, IR::Condition{true}, &root_stmt)};
|
||||
Statement* const always_cond{&pools.stmt.emplace_back(Identity{}, IR::Condition{true}, &root_stmt)};
|
||||
if (block.cond == IR::Condition{true}) {
|
||||
const Node true_label{local_labels.at(block.branch_true)};
|
||||
gotos.push_back(
|
||||
root.insert(ip, *pool.Create(Goto{}, always_cond, true_label, &root_stmt)));
|
||||
gotos.push_back(root.insert(ip, pools.stmt.emplace_back(Goto{}, always_cond, true_label, &root_stmt)));
|
||||
} else if (block.cond == IR::Condition{false}) {
|
||||
const Node false_label{local_labels.at(block.branch_false)};
|
||||
gotos.push_back(root.insert(
|
||||
ip, *pool.Create(Goto{}, always_cond, false_label, &root_stmt)));
|
||||
gotos.push_back(root.insert(ip, pools.stmt.emplace_back(Goto{}, always_cond, false_label, &root_stmt)));
|
||||
} else {
|
||||
const Node true_label{local_labels.at(block.branch_true)};
|
||||
const Node false_label{local_labels.at(block.branch_false)};
|
||||
Statement* const true_cond{pool.Create(Identity{}, block.cond, &root_stmt)};
|
||||
gotos.push_back(
|
||||
root.insert(ip, *pool.Create(Goto{}, true_cond, true_label, &root_stmt)));
|
||||
gotos.push_back(root.insert(
|
||||
ip, *pool.Create(Goto{}, always_cond, false_label, &root_stmt)));
|
||||
Statement* const true_cond{&pools.stmt.emplace_back(Identity{}, block.cond, &root_stmt)};
|
||||
gotos.push_back(root.insert(ip, pools.stmt.emplace_back(Goto{}, true_cond, true_label, &root_stmt)));
|
||||
gotos.push_back(root.insert(ip, pools.stmt.emplace_back(Goto{}, always_cond, false_label, &root_stmt)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Flow::EndClass::IndirectBranch:
|
||||
root.insert(ip, *pool.Create(SetIndirectBranchVariable{}, block.branch_reg,
|
||||
block.branch_offset, &root_stmt));
|
||||
root.insert(ip, pools.stmt.emplace_back(SetIndirectBranchVariable{}, block.branch_reg, block.branch_offset, &root_stmt));
|
||||
for (const Flow::IndirectBranch& indirect : block.indirect_branches) {
|
||||
const Node indirect_label{local_labels.at(indirect.block)};
|
||||
Statement* cond{
|
||||
pool.Create(IndirectBranchCond{}, indirect.address, &root_stmt)};
|
||||
Statement* goto_stmt{pool.Create(Goto{}, cond, indirect_label, &root_stmt)};
|
||||
Statement* cond{&pools.stmt.emplace_back(IndirectBranchCond{}, indirect.address, &root_stmt)};
|
||||
Statement* goto_stmt{&pools.stmt.emplace_back(Goto{}, cond, indirect_label, &root_stmt)};
|
||||
gotos.push_back(root.insert(ip, *goto_stmt));
|
||||
}
|
||||
root.insert(ip, *pool.Create(Unreachable{}, &root_stmt));
|
||||
root.insert(ip, pools.stmt.emplace_back(Unreachable{}, &root_stmt));
|
||||
break;
|
||||
case Flow::EndClass::Call: {
|
||||
Flow::Function& call{cfg.Functions()[block.function_call]};
|
||||
@@ -454,16 +323,16 @@ private:
|
||||
break;
|
||||
}
|
||||
case Flow::EndClass::Exit:
|
||||
root.insert(ip, *pool.Create(Return{}, &root_stmt));
|
||||
root.insert(ip, pools.stmt.emplace_back(Return{}, &root_stmt));
|
||||
break;
|
||||
case Flow::EndClass::Return: {
|
||||
Statement* const always_cond{pool.Create(Identity{}, block.cond, &root_stmt)};
|
||||
auto goto_stmt{pool.Create(Goto{}, always_cond, return_label.value(), &root_stmt)};
|
||||
Statement* const always_cond{&pools.stmt.emplace_back(Identity{}, block.cond, &root_stmt)};
|
||||
auto goto_stmt{&pools.stmt.emplace_back(Goto{}, always_cond, return_label.value(), &root_stmt)};
|
||||
gotos.push_back(root.insert(ip, *goto_stmt));
|
||||
break;
|
||||
}
|
||||
case Flow::EndClass::Kill:
|
||||
root.insert(ip, *pool.Create(Kill{}, &root_stmt));
|
||||
root.insert(ip, pools.stmt.emplace_back(Kill{}, &root_stmt));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -479,8 +348,8 @@ private:
|
||||
Tree& body{goto_stmt->up->children};
|
||||
Tree if_body;
|
||||
if_body.splice(if_body.begin(), body, std::next(goto_stmt), label_stmt);
|
||||
Statement* const cond{pool.Create(Not{}, goto_stmt->cond, &root_stmt)};
|
||||
Statement* const if_stmt{pool.Create(If{}, cond, std::move(if_body), goto_stmt->up)};
|
||||
Statement* const cond{&pools.stmt.emplace_back(Not{}, goto_stmt->cond, &root_stmt)};
|
||||
Statement* const if_stmt{&pools.stmt.emplace_back(If{}, cond, std::move(if_body), goto_stmt->up)};
|
||||
UpdateTreeUp(if_stmt);
|
||||
body.insert(goto_stmt, *if_stmt);
|
||||
body.erase(goto_stmt);
|
||||
@@ -491,7 +360,7 @@ private:
|
||||
Tree loop_body;
|
||||
loop_body.splice(loop_body.begin(), body, label_stmt, goto_stmt);
|
||||
Statement* const cond{goto_stmt->cond};
|
||||
Statement* const loop{pool.Create(Loop{}, cond, std::move(loop_body), goto_stmt->up)};
|
||||
Statement* const loop{&pools.stmt.emplace_back(Loop{}, cond, std::move(loop_body), goto_stmt->up)};
|
||||
UpdateTreeUp(loop);
|
||||
body.insert(goto_stmt, *loop);
|
||||
body.erase(goto_stmt);
|
||||
@@ -516,15 +385,15 @@ private:
|
||||
const u32 label_id{label->id};
|
||||
|
||||
Statement* const goto_cond{goto_stmt->cond};
|
||||
Statement* const set_var{pool.Create(SetVariable{}, label_id, goto_cond, parent)};
|
||||
Statement* const set_var{&pools.stmt.emplace_back(SetVariable{}, label_id, goto_cond, parent)};
|
||||
body.insert(goto_stmt, *set_var);
|
||||
|
||||
Tree if_body;
|
||||
if_body.splice(if_body.begin(), body, std::next(goto_stmt), label_nested_stmt);
|
||||
Statement* const variable{pool.Create(Variable{}, label_id, &root_stmt)};
|
||||
Statement* const neg_var{pool.Create(Not{}, variable, &root_stmt)};
|
||||
Statement* const variable{&pools.stmt.emplace_back(Variable{}, label_id, &root_stmt)};
|
||||
Statement* const neg_var{&pools.stmt.emplace_back(Not{}, variable, &root_stmt)};
|
||||
if (!if_body.empty()) {
|
||||
Statement* const if_stmt{pool.Create(If{}, neg_var, std::move(if_body), parent)};
|
||||
Statement* const if_stmt{&pools.stmt.emplace_back(If{}, neg_var, std::move(if_body), parent)};
|
||||
UpdateTreeUp(if_stmt);
|
||||
body.insert(goto_stmt, *if_stmt);
|
||||
}
|
||||
@@ -533,8 +402,7 @@ private:
|
||||
switch (label_nested_stmt->type) {
|
||||
case StatementType::If:
|
||||
// Update nested if condition
|
||||
label_nested_stmt->cond =
|
||||
pool.Create(Or{}, variable, label_nested_stmt->cond, &root_stmt);
|
||||
label_nested_stmt->cond = &pools.stmt.emplace_back(Or{}, variable, label_nested_stmt->cond, &root_stmt);
|
||||
break;
|
||||
case StatementType::Loop:
|
||||
break;
|
||||
@@ -542,7 +410,7 @@ private:
|
||||
throw LogicError("Invalid inward movement");
|
||||
}
|
||||
Tree& nested_tree{label_nested_stmt->children};
|
||||
Statement* const new_goto{pool.Create(Goto{}, variable, label, &*label_nested_stmt)};
|
||||
Statement* const new_goto{&pools.stmt.emplace_back(Goto{}, variable, label, &*label_nested_stmt)};
|
||||
return nested_tree.insert(nested_tree.begin(), *new_goto);
|
||||
}
|
||||
|
||||
@@ -556,16 +424,16 @@ private:
|
||||
Tree loop_body;
|
||||
loop_body.splice(loop_body.begin(), body, label_nested_stmt, goto_stmt);
|
||||
SanitizeNoBreaks(loop_body);
|
||||
Statement* const variable{pool.Create(Variable{}, label_id, &root_stmt)};
|
||||
Statement* const loop_stmt{pool.Create(Loop{}, variable, std::move(loop_body), parent)};
|
||||
Statement* const variable{&pools.stmt.emplace_back(Variable{}, label_id, &root_stmt)};
|
||||
Statement* const loop_stmt{&pools.stmt.emplace_back(Loop{}, variable, std::move(loop_body), parent)};
|
||||
UpdateTreeUp(loop_stmt);
|
||||
body.insert(goto_stmt, *loop_stmt);
|
||||
|
||||
Statement* const new_goto{pool.Create(Goto{}, variable, label, loop_stmt)};
|
||||
Statement* const new_goto{&pools.stmt.emplace_back(Goto{}, variable, label, loop_stmt)};
|
||||
loop_stmt->children.push_front(*new_goto);
|
||||
const Node new_goto_node{loop_stmt->children.begin()};
|
||||
|
||||
Statement* const set_var{pool.Create(SetVariable{}, label_id, goto_stmt->cond, loop_stmt)};
|
||||
Statement* const set_var{&pools.stmt.emplace_back(SetVariable{}, label_id, goto_stmt->cond, loop_stmt)};
|
||||
loop_stmt->children.push_back(*set_var);
|
||||
|
||||
body.erase(goto_stmt);
|
||||
@@ -577,22 +445,22 @@ private:
|
||||
Tree& body{parent->children};
|
||||
const u32 label_id{goto_stmt->label->id};
|
||||
Statement* const goto_cond{goto_stmt->cond};
|
||||
Statement* const set_goto_var{pool.Create(SetVariable{}, label_id, goto_cond, &*parent)};
|
||||
Statement* const set_goto_var{&pools.stmt.emplace_back(SetVariable{}, label_id, goto_cond, &*parent)};
|
||||
body.insert(goto_stmt, *set_goto_var);
|
||||
|
||||
Tree if_body;
|
||||
if_body.splice(if_body.begin(), body, std::next(goto_stmt), body.end());
|
||||
if_body.pop_front();
|
||||
Statement* const cond{pool.Create(Variable{}, label_id, &root_stmt)};
|
||||
Statement* const neg_cond{pool.Create(Not{}, cond, &root_stmt)};
|
||||
Statement* const if_stmt{pool.Create(If{}, neg_cond, std::move(if_body), &*parent)};
|
||||
Statement* const cond{&pools.stmt.emplace_back(Variable{}, label_id, &root_stmt)};
|
||||
Statement* const neg_cond{&pools.stmt.emplace_back(Not{}, cond, &root_stmt)};
|
||||
Statement* const if_stmt{&pools.stmt.emplace_back(If{}, neg_cond, std::move(if_body), &*parent)};
|
||||
UpdateTreeUp(if_stmt);
|
||||
body.insert(goto_stmt, *if_stmt);
|
||||
|
||||
body.erase(goto_stmt);
|
||||
|
||||
Statement* const new_cond{pool.Create(Variable{}, label_id, &root_stmt)};
|
||||
Statement* const new_goto{pool.Create(Goto{}, new_cond, goto_stmt->label, parent->up)};
|
||||
Statement* const new_cond{&pools.stmt.emplace_back(Variable{}, label_id, &root_stmt)};
|
||||
Statement* const new_goto{&pools.stmt.emplace_back(Goto{}, new_cond, goto_stmt->label, parent->up)};
|
||||
Tree& parent_tree{parent->up->children};
|
||||
return parent_tree.insert(std::next(parent), *new_goto);
|
||||
}
|
||||
@@ -602,21 +470,21 @@ private:
|
||||
Tree& body{parent->children};
|
||||
const u32 label_id{goto_stmt->label->id};
|
||||
Statement* const goto_cond{goto_stmt->cond};
|
||||
Statement* const set_goto_var{pool.Create(SetVariable{}, label_id, goto_cond, parent)};
|
||||
Statement* const cond{pool.Create(Variable{}, label_id, &root_stmt)};
|
||||
Statement* const break_stmt{pool.Create(Break{}, cond, parent)};
|
||||
Statement* const set_goto_var{&pools.stmt.emplace_back(SetVariable{}, label_id, goto_cond, parent)};
|
||||
Statement* const cond{&pools.stmt.emplace_back(Variable{}, label_id, &root_stmt)};
|
||||
Statement* const break_stmt{&pools.stmt.emplace_back(Break{}, cond, parent)};
|
||||
body.insert(goto_stmt, *set_goto_var);
|
||||
body.insert(goto_stmt, *break_stmt);
|
||||
body.erase(goto_stmt);
|
||||
|
||||
const Node loop{Tree::s_iterator_to(*goto_stmt->up)};
|
||||
Statement* const new_goto_cond{pool.Create(Variable{}, label_id, &root_stmt)};
|
||||
Statement* const new_goto{pool.Create(Goto{}, new_goto_cond, goto_stmt->label, loop->up)};
|
||||
Statement* const new_goto_cond{&pools.stmt.emplace_back(Variable{}, label_id, &root_stmt)};
|
||||
Statement* const new_goto{&pools.stmt.emplace_back(Goto{}, new_goto_cond, goto_stmt->label, loop->up)};
|
||||
Tree& parent_tree{loop->up->children};
|
||||
return parent_tree.insert(std::next(loop), *new_goto);
|
||||
}
|
||||
|
||||
ObjectPool<Statement>& pool;
|
||||
ShaderPools& pools;
|
||||
Statement root_stmt{FunctionTag{}};
|
||||
};
|
||||
|
||||
@@ -652,11 +520,11 @@ private:
|
||||
|
||||
class TranslatePass {
|
||||
public:
|
||||
TranslatePass(ObjectPool<IR::Inst>& inst_pool_, ObjectPool<IR::Block>& block_pool_,
|
||||
ObjectPool<Statement>& stmt_pool_, Environment& env_, Statement& root_stmt,
|
||||
IR::AbstractSyntaxList& syntax_list_, const HostTranslateInfo& host_info)
|
||||
: stmt_pool{stmt_pool_}, inst_pool{inst_pool_}, block_pool{block_pool_}, env{env_},
|
||||
syntax_list{syntax_list_} {
|
||||
TranslatePass(ShaderPools& pools_, Environment& env_, Statement& root_stmt, IR::AbstractSyntaxList& syntax_list_, const HostTranslateInfo& host_info)
|
||||
: pools{pools_}
|
||||
, env{env_}
|
||||
, syntax_list{syntax_list_}
|
||||
{
|
||||
Visit(root_stmt, nullptr, nullptr);
|
||||
|
||||
IR::Block& first_block{*syntax_list.front().data.block};
|
||||
@@ -674,7 +542,7 @@ private:
|
||||
if (current_block) {
|
||||
return;
|
||||
}
|
||||
current_block = block_pool.Create(inst_pool);
|
||||
current_block = &pools.block.emplace_back(pools.inst);
|
||||
auto& node{syntax_list.emplace_back()};
|
||||
node.type = IR::AbstractSyntaxNode::Type::Block;
|
||||
node.data.block = current_block;
|
||||
@@ -740,7 +608,7 @@ private:
|
||||
break;
|
||||
}
|
||||
case StatementType::Loop: {
|
||||
IR::Block* const loop_header_block{block_pool.Create(inst_pool)};
|
||||
IR::Block* const loop_header_block{&pools.block.emplace_back(pools.inst)};
|
||||
if (current_block) {
|
||||
current_block->AddBranch(loop_header_block);
|
||||
}
|
||||
@@ -748,7 +616,7 @@ private:
|
||||
header_node.type = IR::AbstractSyntaxNode::Type::Block;
|
||||
header_node.data.block = loop_header_block;
|
||||
|
||||
IR::Block* const continue_block{block_pool.Create(inst_pool)};
|
||||
IR::Block* const continue_block{&pools.block.emplace_back(pools.inst)};
|
||||
IR::Block* const merge_block{MergeBlock(parent, stmt)};
|
||||
|
||||
const size_t loop_node_index{syntax_list.size()};
|
||||
@@ -814,7 +682,7 @@ private:
|
||||
}
|
||||
case StatementType::Return: {
|
||||
ensure_block();
|
||||
IR::Block* return_block{block_pool.Create(inst_pool)};
|
||||
IR::Block* return_block{&pools.block.emplace_back(pools.inst)};
|
||||
IR::IREmitter{*return_block}.Epilogue();
|
||||
current_block->AddBranch(return_block);
|
||||
|
||||
@@ -862,10 +730,10 @@ private:
|
||||
Statement* merge_stmt{TryFindForwardBlock(stmt)};
|
||||
if (!merge_stmt) {
|
||||
// Create a merge block we can visit later
|
||||
merge_stmt = stmt_pool.Create(&dummy_flow_block, &parent);
|
||||
merge_stmt = &pools.stmt.emplace_back(&dummy_flow_block, &parent);
|
||||
parent.children.insert(std::next(Tree::s_iterator_to(stmt)), *merge_stmt);
|
||||
}
|
||||
return block_pool.Create(inst_pool);
|
||||
return &pools.block.emplace_back(pools.inst);
|
||||
}
|
||||
|
||||
void DemoteCombinationPass() {
|
||||
@@ -973,9 +841,7 @@ private:
|
||||
asl.insert(next_it_2, demote_if_node);
|
||||
}
|
||||
|
||||
ObjectPool<Statement>& stmt_pool;
|
||||
ObjectPool<IR::Inst>& inst_pool;
|
||||
ObjectPool<IR::Block>& block_pool;
|
||||
ShaderPools& pools;
|
||||
Environment& env;
|
||||
IR::AbstractSyntaxList& syntax_list;
|
||||
bool uses_demote_to_helper{};
|
||||
@@ -983,15 +849,12 @@ private:
|
||||
};
|
||||
} // Anonymous namespace
|
||||
|
||||
IR::AbstractSyntaxList BuildASL(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool,
|
||||
Environment& env, Flow::CFG& cfg,
|
||||
const HostTranslateInfo& host_info) {
|
||||
ObjectPool<Statement> stmt_pool{64};
|
||||
GotoPass goto_pass{cfg, stmt_pool};
|
||||
IR::AbstractSyntaxList BuildASL(ShaderPools& pools, Environment& env, Flow::CFG& cfg, const HostTranslateInfo& host_info) {
|
||||
GotoPass goto_pass{cfg, pools};
|
||||
Statement& root{goto_pass.RootStatement()};
|
||||
IR::AbstractSyntaxList syntax_list;
|
||||
TranslatePass{inst_pool, block_pool, stmt_pool, env, root, syntax_list, host_info};
|
||||
stmt_pool.ReleaseContents();
|
||||
TranslatePass pass{pools, env, root, syntax_list, host_info};
|
||||
pools.stmt.clear();
|
||||
return syntax_list;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -8,15 +11,13 @@
|
||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
#include "shader_recompiler/frontend/maxwell/control_flow.h"
|
||||
#include "shader_recompiler/object_pool.h"
|
||||
|
||||
namespace Shader {
|
||||
struct HostTranslateInfo;
|
||||
namespace Maxwell {
|
||||
|
||||
[[nodiscard]] IR::AbstractSyntaxList BuildASL(ObjectPool<IR::Inst>& inst_pool,
|
||||
ObjectPool<IR::Block>& block_pool, Environment& env,
|
||||
Flow::CFG& cfg, const HostTranslateInfo& host_info);
|
||||
struct ShaderPools;
|
||||
[[nodiscard]] IR::AbstractSyntaxList BuildASL(ShaderPools& pools, Environment& env, Flow::CFG& cfg, const HostTranslateInfo& host_info);
|
||||
|
||||
} // namespace Maxwell
|
||||
} // 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
|
||||
|
||||
@@ -160,14 +157,16 @@ IR::Value Sample(TranslatorVisitor& v, u64 insn) {
|
||||
|
||||
unsigned Swizzle(u64 insn) {
|
||||
const Encoding texs{insn};
|
||||
u8 const encoding = u8(texs.swizzle);
|
||||
const size_t encoding{texs.swizzle};
|
||||
if (texs.dest_reg_b == IR::Reg::RZ) {
|
||||
if (encoding >= RG_LUT.size())
|
||||
if (encoding >= RG_LUT.size()) {
|
||||
throw NotImplementedException("Illegal RG encoding {}", encoding);
|
||||
}
|
||||
return RG_LUT[encoding];
|
||||
} else {
|
||||
if (encoding >= RGBA_LUT.size())
|
||||
if (encoding >= RGBA_LUT.size()) {
|
||||
throw NotImplementedException("Illegal RGBA encoding {}", encoding);
|
||||
}
|
||||
return RGBA_LUT[encoding];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -145,14 +142,16 @@ IR::Value Sample(TranslatorVisitor& v, u64 insn) {
|
||||
|
||||
unsigned Swizzle(u64 insn) {
|
||||
const Encoding tlds{insn};
|
||||
u8 const encoding = u8(tlds.swizzle);
|
||||
const size_t encoding{tlds.swizzle};
|
||||
if (tlds.dest_reg_b == IR::Reg::RZ) {
|
||||
if (encoding >= RG_LUT.size())
|
||||
if (encoding >= RG_LUT.size()) {
|
||||
throw NotImplementedException("Illegal RG encoding {}", encoding);
|
||||
}
|
||||
return RG_LUT[encoding];
|
||||
} else {
|
||||
if (encoding >= RGBA_LUT.size())
|
||||
if (encoding >= RGBA_LUT.size()) {
|
||||
throw NotImplementedException("Illegal RGBA encoding {}", encoding);
|
||||
}
|
||||
return RGBA_LUT[encoding];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <queue>
|
||||
|
||||
#include "common/settings.h"
|
||||
#include "shader_recompiler/shader_pool.h"
|
||||
#include "shader_recompiler/exception.h"
|
||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||
#include "shader_recompiler/frontend/ir/ir_emitter.h"
|
||||
@@ -234,13 +235,12 @@ void LowerGeometryPassthrough(const IR::Program& program, const HostTranslateInf
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool,
|
||||
Environment& env, Flow::CFG& cfg, const HostTranslateInfo& host_info) {
|
||||
IR::Program TranslateProgram(ShaderPools& pools, 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(pools, env, cfg, host_info);
|
||||
program.blocks = GenerateBlocks(program.syntax_list);
|
||||
program.post_order_blocks = PostOrder(program.syntax_list.front());
|
||||
program.stage = env.ShaderStage();
|
||||
@@ -410,11 +410,7 @@ void ConvertLegacyToGeneric(IR::Program& program, const Shader::RuntimeInfo& run
|
||||
}
|
||||
}
|
||||
|
||||
IR::Program GenerateGeometryPassthrough(ObjectPool<IR::Inst>& inst_pool,
|
||||
ObjectPool<IR::Block>& block_pool,
|
||||
const HostTranslateInfo& host_info,
|
||||
IR::Program& source_program,
|
||||
Shader::OutputTopology output_topology) {
|
||||
IR::Program GenerateGeometryPassthrough(ShaderPools& pools, const HostTranslateInfo& host_info, IR::Program& source_program, Shader::OutputTopology output_topology) {
|
||||
IR::Program program;
|
||||
program.stage = Stage::Geometry;
|
||||
program.output_topology = output_topology;
|
||||
@@ -426,16 +422,15 @@ IR::Program GenerateGeometryPassthrough(ObjectPool<IR::Inst>& inst_pool,
|
||||
program.info.stores.Set(IR::Attribute::Layer, true);
|
||||
program.info.stores.Set(source_program.info.emulated_layer, false);
|
||||
|
||||
IR::Block* current_block = block_pool.Create(inst_pool);
|
||||
IR::Block* current_block = &pools.block.emplace_back(pools.inst);
|
||||
auto& node{program.syntax_list.emplace_back()};
|
||||
node.type = IR::AbstractSyntaxNode::Type::Block;
|
||||
node.data.block = current_block;
|
||||
|
||||
IR::IREmitter ir{*current_block};
|
||||
EmitGeometryPassthrough(ir, program, program.info.stores, true,
|
||||
source_program.info.emulated_layer);
|
||||
EmitGeometryPassthrough(ir, program, program.info.stores, true, source_program.info.emulated_layer);
|
||||
|
||||
IR::Block* return_block{block_pool.Create(inst_pool)};
|
||||
IR::Block* return_block{&pools.block.emplace_back(pools.inst)};
|
||||
IR::IREmitter{*return_block}.Epilogue();
|
||||
current_block->AddBranch(return_block);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -7,7 +10,7 @@
|
||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||
#include "shader_recompiler/frontend/ir/program.h"
|
||||
#include "shader_recompiler/frontend/maxwell/control_flow.h"
|
||||
#include "shader_recompiler/object_pool.h"
|
||||
#include "shader_recompiler/frontend/maxwell/structured_control_flow.h"
|
||||
#include "shader_recompiler/runtime_info.h"
|
||||
|
||||
namespace Shader {
|
||||
@@ -16,22 +19,17 @@ struct HostTranslateInfo;
|
||||
|
||||
namespace Shader::Maxwell {
|
||||
|
||||
[[nodiscard]] IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool,
|
||||
ObjectPool<IR::Block>& block_pool, Environment& env,
|
||||
Flow::CFG& cfg, const HostTranslateInfo& host_info);
|
||||
struct ShaderPools;
|
||||
|
||||
[[nodiscard]] IR::Program MergeDualVertexPrograms(IR::Program& vertex_a, IR::Program& vertex_b,
|
||||
Environment& env_vertex_b);
|
||||
[[nodiscard]] IR::Program TranslateProgram(ShaderPools& pools, Environment& env, Flow::CFG& cfg, const HostTranslateInfo& host_info);
|
||||
|
||||
[[nodiscard]] IR::Program MergeDualVertexPrograms(IR::Program& vertex_a, IR::Program& vertex_b, Environment& env_vertex_b);
|
||||
|
||||
void ConvertLegacyToGeneric(IR::Program& program, const RuntimeInfo& runtime_info);
|
||||
|
||||
// Maxwell v1 and older Nvidia cards don't support setting gl_Layer from non-geometry stages.
|
||||
// This creates a workaround by setting the layer as a generic output and creating a
|
||||
// passthrough geometry shader that reads the generic and sets the layer.
|
||||
[[nodiscard]] IR::Program GenerateGeometryPassthrough(ObjectPool<IR::Inst>& inst_pool,
|
||||
ObjectPool<IR::Block>& block_pool,
|
||||
const HostTranslateInfo& host_info,
|
||||
IR::Program& source_program,
|
||||
Shader::OutputTopology output_topology);
|
||||
[[nodiscard]] IR::Program GenerateGeometryPassthrough(ShaderPools& pools, const HostTranslateInfo& host_info, IR::Program& source_program, Shader::OutputTopology output_topology);
|
||||
|
||||
} // namespace Shader::Maxwell
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace Shader {
|
||||
|
||||
template <typename T>
|
||||
requires std::is_destructible_v<T>
|
||||
class ObjectPool {
|
||||
public:
|
||||
explicit ObjectPool(size_t chunk_size = 8192) : new_chunk_size{chunk_size} {
|
||||
node = &chunks.emplace_back(new_chunk_size);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
requires std::is_constructible_v<T, Args...>
|
||||
[[nodiscard]] T* Create(Args&&... args) {
|
||||
return std::construct_at(Memory(), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
void ReleaseContents() {
|
||||
if (chunks.empty()) {
|
||||
return;
|
||||
}
|
||||
Chunk& root{chunks.front()};
|
||||
if (root.used_objects == root.num_objects) {
|
||||
// Root chunk has been filled, squash allocations into it
|
||||
const size_t total_objects{root.num_objects + new_chunk_size * (chunks.size() - 1)};
|
||||
chunks.clear();
|
||||
chunks.emplace_back(total_objects);
|
||||
} else {
|
||||
root.Release();
|
||||
chunks.resize(1);
|
||||
}
|
||||
chunks.shrink_to_fit();
|
||||
node = &chunks.front();
|
||||
}
|
||||
|
||||
private:
|
||||
struct NonTrivialDummy {
|
||||
NonTrivialDummy() noexcept {}
|
||||
};
|
||||
|
||||
union Storage {
|
||||
Storage() noexcept {}
|
||||
~Storage() noexcept {}
|
||||
|
||||
NonTrivialDummy dummy{};
|
||||
T object;
|
||||
};
|
||||
|
||||
struct Chunk {
|
||||
explicit Chunk() = default;
|
||||
explicit Chunk(size_t size)
|
||||
: num_objects{size}, storage{std::make_unique<Storage[]>(size)} {}
|
||||
|
||||
Chunk& operator=(Chunk&& rhs) noexcept {
|
||||
Release();
|
||||
used_objects = std::exchange(rhs.used_objects, 0);
|
||||
num_objects = std::exchange(rhs.num_objects, 0);
|
||||
storage = std::move(rhs.storage);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Chunk(Chunk&& rhs) noexcept
|
||||
: used_objects{std::exchange(rhs.used_objects, 0)},
|
||||
num_objects{std::exchange(rhs.num_objects, 0)}, storage{std::move(rhs.storage)} {}
|
||||
|
||||
~Chunk() {
|
||||
Release();
|
||||
}
|
||||
|
||||
void Release() {
|
||||
std::destroy_n(storage.get(), used_objects);
|
||||
used_objects = 0;
|
||||
}
|
||||
|
||||
size_t used_objects{};
|
||||
size_t num_objects{};
|
||||
std::unique_ptr<Storage[]> storage;
|
||||
};
|
||||
|
||||
[[nodiscard]] T* Memory() {
|
||||
Chunk* const chunk{FreeChunk()};
|
||||
return &chunk->storage[chunk->used_objects++].object;
|
||||
}
|
||||
|
||||
[[nodiscard]] Chunk* FreeChunk() {
|
||||
if (node->used_objects != node->num_objects) {
|
||||
return node;
|
||||
}
|
||||
node = &chunks.emplace_back(new_chunk_size);
|
||||
return node;
|
||||
}
|
||||
|
||||
Chunk* node{};
|
||||
std::vector<Chunk> chunks;
|
||||
size_t new_chunk_size{};
|
||||
};
|
||||
|
||||
} // namespace Shader
|
||||
@@ -0,0 +1,134 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/container/stable_vector.hpp>
|
||||
#include <boost/intrusive/list.hpp>
|
||||
#include "shader_recompiler/frontend/maxwell/control_flow.h"
|
||||
|
||||
namespace Shader::Maxwell {
|
||||
|
||||
struct Statement;
|
||||
|
||||
// Use normal_link because we are not guaranteed to destroy the tree in order
|
||||
using ListBaseHook = boost::intrusive::list_base_hook<boost::intrusive::link_mode<boost::intrusive::normal_link>>;
|
||||
using Tree = boost::intrusive::list<Statement,
|
||||
// Allow using Statement without a definition
|
||||
boost::intrusive::base_hook<ListBaseHook>,
|
||||
// Avoid linear complexity on splice, size is never called
|
||||
boost::intrusive::constant_time_size<false>>;
|
||||
using Node = Tree::iterator;
|
||||
|
||||
enum class StatementType {
|
||||
Code,
|
||||
Goto,
|
||||
Label,
|
||||
If,
|
||||
Loop,
|
||||
Break,
|
||||
Return,
|
||||
Kill,
|
||||
Unreachable,
|
||||
Function,
|
||||
Identity,
|
||||
Not,
|
||||
Or,
|
||||
SetVariable,
|
||||
SetIndirectBranchVariable,
|
||||
Variable,
|
||||
IndirectBranchCond,
|
||||
};
|
||||
|
||||
[[nodiscard]] inline bool HasChildren(StatementType type) {
|
||||
switch (type) {
|
||||
case StatementType::If:
|
||||
case StatementType::Loop:
|
||||
case StatementType::Function:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
struct Goto {};
|
||||
struct Label {};
|
||||
struct If {};
|
||||
struct Loop {};
|
||||
struct Break {};
|
||||
struct Return {};
|
||||
struct Kill {};
|
||||
struct Unreachable {};
|
||||
struct FunctionTag {};
|
||||
struct Identity {};
|
||||
struct Not {};
|
||||
struct Or {};
|
||||
struct SetVariable {};
|
||||
struct SetIndirectBranchVariable {};
|
||||
struct Variable {};
|
||||
struct IndirectBranchCond {};
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 26495) // Always initialize a member variable, expected in Statement
|
||||
#endif
|
||||
struct Statement : ListBaseHook {
|
||||
Statement(const Flow::Block* block_, Statement* up_) : block{block_}, up{up_}, type{StatementType::Code} {}
|
||||
Statement(Goto, Statement* cond_, Node label_, Statement* up_) : label{label_}, cond{cond_}, up{up_}, type{StatementType::Goto} {}
|
||||
Statement(Label, u32 id_, Statement* up_) : id{id_}, up{up_}, type{StatementType::Label} {}
|
||||
Statement(If, Statement* cond_, Tree&& children_, Statement* up_) : children{std::move(children_)}, cond{cond_}, up{up_}, type{StatementType::If} {}
|
||||
Statement(Loop, Statement* cond_, Tree&& children_, Statement* up_) : children{std::move(children_)}, cond{cond_}, up{up_}, type{StatementType::Loop} {}
|
||||
Statement(Break, Statement* cond_, Statement* up_) : cond{cond_}, up{up_}, type{StatementType::Break} {}
|
||||
Statement(Return, Statement* up_) : up{up_}, type{StatementType::Return} {}
|
||||
Statement(Kill, Statement* up_) : up{up_}, type{StatementType::Kill} {}
|
||||
Statement(Unreachable, Statement* up_) : up{up_}, type{StatementType::Unreachable} {}
|
||||
Statement(FunctionTag) : children{}, type{StatementType::Function} {}
|
||||
Statement(Identity, IR::Condition cond_, Statement* up_) : guest_cond{cond_}, up{up_}, type{StatementType::Identity} {}
|
||||
Statement(Not, Statement* op_, Statement* up_) : op{op_}, up{up_}, type{StatementType::Not} {}
|
||||
Statement(Or, Statement* op_a_, Statement* op_b_, Statement* up_) : op_a{op_a_}, op_b{op_b_}, up{up_}, type{StatementType::Or} {}
|
||||
Statement(SetVariable, u32 id_, Statement* op_, Statement* up_) : op{op_}, id{id_}, up{up_}, type{StatementType::SetVariable} {}
|
||||
Statement(SetIndirectBranchVariable, IR::Reg branch_reg_, s32 branch_offset_, Statement* up_) : branch_offset{branch_offset_}, branch_reg{branch_reg_}, up{up_}, type{StatementType::SetIndirectBranchVariable} {}
|
||||
Statement(Variable, u32 id_, Statement* up_) : id{id_}, up{up_}, type{StatementType::Variable} {}
|
||||
Statement(IndirectBranchCond, u32 location_, Statement* up_) : location{location_}, up{up_}, type{StatementType::IndirectBranchCond} {}
|
||||
~Statement() {
|
||||
if (HasChildren(type)) {
|
||||
std::destroy_at(&children);
|
||||
}
|
||||
}
|
||||
union {
|
||||
const Flow::Block* block;
|
||||
Node label;
|
||||
Tree children;
|
||||
IR::Condition guest_cond;
|
||||
Statement* op;
|
||||
Statement* op_a;
|
||||
u32 location;
|
||||
s32 branch_offset;
|
||||
};
|
||||
union {
|
||||
Statement* cond;
|
||||
Statement* op_b;
|
||||
u32 id;
|
||||
IR::Reg branch_reg;
|
||||
};
|
||||
Statement* up{};
|
||||
StatementType type;
|
||||
};
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
struct ShaderPools {
|
||||
void clear() {
|
||||
flow_block.clear();
|
||||
block.clear();
|
||||
inst.clear();
|
||||
stmt.clear();
|
||||
}
|
||||
boost::container::stable_vector<Shader::IR::Inst> inst{};
|
||||
boost::container::stable_vector<Shader::IR::Block> block{};
|
||||
boost::container::stable_vector<Shader::Maxwell::Flow::Block> flow_block{};
|
||||
boost::container::stable_vector<Shader::Maxwell::Statement> stmt;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -43,7 +43,6 @@ using Shader::Backend::GLASM::EmitGLASM;
|
||||
using Shader::Backend::GLSL::EmitGLSL;
|
||||
using Shader::Backend::SPIRV::EmitSPIRV;
|
||||
using Shader::Maxwell::ConvertLegacyToGeneric;
|
||||
using Shader::Maxwell::GenerateGeometryPassthrough;
|
||||
using Shader::Maxwell::MergeDualVertexPrograms;
|
||||
using Shader::Maxwell::TranslateProgram;
|
||||
using VideoCommon::ComputeEnvironment;
|
||||
@@ -317,7 +316,7 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
|
||||
ComputePipelineKey key;
|
||||
file.read(reinterpret_cast<char*>(&key), sizeof(key));
|
||||
queue_work([this, key, env_ = std::move(env), &state, &callback](Context* ctx) mutable {
|
||||
ctx->pools.ReleaseContents();
|
||||
ctx->pools.clear();
|
||||
auto pipeline{CreateComputePipeline(ctx->pools, key, env_, true)};
|
||||
std::scoped_lock lock{state.mutex};
|
||||
if (pipeline) {
|
||||
@@ -338,7 +337,7 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
|
||||
for (auto& env : envs_) {
|
||||
env_ptrs.push_back(&env);
|
||||
}
|
||||
ctx->pools.ReleaseContents();
|
||||
ctx->pools.clear();
|
||||
auto pipeline{CreateGraphicsPipeline(ctx->pools, key, MakeSpan(env_ptrs), false, true)};
|
||||
std::scoped_lock lock{state.mutex};
|
||||
if (pipeline) {
|
||||
@@ -448,9 +447,8 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline() {
|
||||
GraphicsEnvironments environments;
|
||||
GetGraphicsEnvironments(environments, graphics_key.unique_hashes);
|
||||
|
||||
main_pools.ReleaseContents();
|
||||
auto pipeline{CreateGraphicsPipeline(main_pools, graphics_key, environments.Span(),
|
||||
use_asynchronous_shaders)};
|
||||
main_pools.clear();
|
||||
auto pipeline{CreateGraphicsPipeline(main_pools, graphics_key, environments.Span(), use_asynchronous_shaders)};
|
||||
if (!pipeline || shader_cache_filename.empty()) {
|
||||
return pipeline;
|
||||
}
|
||||
@@ -464,10 +462,7 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline() {
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
|
||||
ShaderContext::ShaderPools& pools, const GraphicsPipelineKey& key,
|
||||
std::span<Shader::Environment* const> envs, bool use_shader_workers,
|
||||
bool force_context_flush) try {
|
||||
std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(Shader::Maxwell::ShaderPools& pools, const GraphicsPipelineKey& key, std::span<Shader::Environment* const> envs, bool use_shader_workers, bool force_context_flush) try {
|
||||
auto hash = key.Hash();
|
||||
LOG_INFO(Render_OpenGL, "{:#016x}", hash);
|
||||
size_t env_index{};
|
||||
@@ -480,12 +475,10 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
|
||||
Shader::IR::Program* layer_source_program{};
|
||||
|
||||
for (size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) {
|
||||
const bool is_emulated_stage = layer_source_program != nullptr
|
||||
&& index == u32(Maxwell::ShaderType::Geometry);
|
||||
const bool is_emulated_stage = layer_source_program != nullptr && index == u32(Maxwell::ShaderType::Geometry);
|
||||
if (key.unique_hashes[index] == 0 && is_emulated_stage) {
|
||||
auto topology = MaxwellToOutputTopology(key.gs_input_topology);
|
||||
programs[index] = GenerateGeometryPassthrough(pools.inst, pools.block, host_info,
|
||||
*layer_source_program, topology);
|
||||
programs[index] = Shader::Maxwell::GenerateGeometryPassthrough(pools, host_info, *layer_source_program, topology);
|
||||
continue;
|
||||
}
|
||||
if (key.unique_hashes[index] == 0) {
|
||||
@@ -503,13 +496,13 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
|
||||
|
||||
if (!uses_vertex_a || index != 1) {
|
||||
// Normal path
|
||||
programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info);
|
||||
programs[index] = TranslateProgram(pools, env, cfg, host_info);
|
||||
|
||||
total_storage_buffers += Shader::NumDescriptors(programs[index].info.storage_buffers_descriptors);
|
||||
} else {
|
||||
// VertexB path when VertexA is present.
|
||||
auto& program_va{programs[0]};
|
||||
auto program_vb{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
|
||||
auto program_vb{TranslateProgram(pools, env, cfg, host_info)};
|
||||
total_storage_buffers += Shader::NumDescriptors(program_vb.info.storage_buffers_descriptors);
|
||||
programs[index] = MergeDualVertexPrograms(program_va, program_vb, env);
|
||||
}
|
||||
@@ -576,7 +569,7 @@ std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(
|
||||
ComputeEnvironment env{*kepler_compute, *gpu_memory, program_base, qmd.program_start};
|
||||
env.SetCachedSize(shader->size_bytes);
|
||||
|
||||
main_pools.ReleaseContents();
|
||||
main_pools.clear();
|
||||
auto pipeline{CreateComputePipeline(main_pools, key, env)};
|
||||
if (!pipeline || shader_cache_filename.empty()) {
|
||||
return pipeline;
|
||||
@@ -586,9 +579,7 @@ std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(
|
||||
ShaderContext::ShaderPools& pools, const ComputePipelineKey& key, Shader::Environment& env,
|
||||
bool force_context_flush) try {
|
||||
std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(Shader::Maxwell::ShaderPools& pools, const ComputePipelineKey& key, Shader::Environment& env, bool force_context_flush) try {
|
||||
auto hash = key.Hash();
|
||||
LOG_INFO(Render_OpenGL, "{:#016x}", hash);
|
||||
|
||||
@@ -598,7 +589,7 @@ std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(
|
||||
env.Dump(hash, key.unique_hash);
|
||||
}
|
||||
|
||||
auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
|
||||
auto program{TranslateProgram(pools, env, cfg, host_info)};
|
||||
const u32 num_storage_buffers{Shader::NumDescriptors(program.info.storage_buffers_descriptors)};
|
||||
Shader::RuntimeInfo info;
|
||||
info.glasm_use_storage_buffers = num_storage_buffers <= device.GetMaxGLASMStorageBufferBlocks();
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/thread_worker.h"
|
||||
#include "shader_recompiler/host_translate_info.h"
|
||||
#include "shader_recompiler/shader_pool.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
#include "video_core/renderer_opengl/gl_compute_pipeline.h"
|
||||
#include "video_core/renderer_opengl/gl_graphics_pipeline.h"
|
||||
@@ -51,20 +52,9 @@ private:
|
||||
[[nodiscard]] GraphicsPipeline* BuiltPipeline(GraphicsPipeline* pipeline) const noexcept;
|
||||
|
||||
std::unique_ptr<GraphicsPipeline> CreateGraphicsPipeline();
|
||||
|
||||
std::unique_ptr<GraphicsPipeline> CreateGraphicsPipeline(
|
||||
ShaderContext::ShaderPools& pools, const GraphicsPipelineKey& key,
|
||||
std::span<Shader::Environment* const> envs, bool use_shader_workers,
|
||||
bool force_context_flush = false);
|
||||
|
||||
std::unique_ptr<ComputePipeline> CreateComputePipeline(const ComputePipelineKey& key,
|
||||
const VideoCommon::ShaderInfo* shader);
|
||||
|
||||
std::unique_ptr<ComputePipeline> CreateComputePipeline(ShaderContext::ShaderPools& pools,
|
||||
const ComputePipelineKey& key,
|
||||
Shader::Environment& env,
|
||||
bool force_context_flush = false);
|
||||
|
||||
std::unique_ptr<GraphicsPipeline> CreateGraphicsPipeline(Shader::Maxwell::ShaderPools& pools, const GraphicsPipelineKey& key, std::span<Shader::Environment* const> envs, bool use_shader_workers, bool force_context_flush = false);
|
||||
std::unique_ptr<ComputePipeline> CreateComputePipeline(const ComputePipelineKey& key, const VideoCommon::ShaderInfo* shader);
|
||||
std::unique_ptr<ComputePipeline> CreateComputePipeline(Shader::Maxwell::ShaderPools& pools, const ComputePipelineKey& key, Shader::Environment& env, bool force_context_flush = false);
|
||||
std::unique_ptr<ShaderWorker> CreateWorkers() const;
|
||||
|
||||
Core::Frontend::EmuWindow& emu_window;
|
||||
@@ -80,7 +70,7 @@ private:
|
||||
GraphicsPipelineKey graphics_key{};
|
||||
GraphicsPipeline* current_pipeline{};
|
||||
|
||||
ShaderContext::ShaderPools main_pools;
|
||||
Shader::Maxwell::ShaderPools main_pools;
|
||||
::Common::unordered_map<GraphicsPipelineKey, std::unique_ptr<GraphicsPipeline>> graphics_cache;
|
||||
::Common::unordered_map<ComputePipelineKey, std::unique_ptr<ComputePipeline>> compute_cache;
|
||||
|
||||
|
||||
@@ -1,33 +1,26 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/container/stable_vector.hpp>
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "core/frontend/graphics_context.h"
|
||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||
#include "shader_recompiler/frontend/maxwell/control_flow.h"
|
||||
#include "shader_recompiler/frontend/maxwell/structured_control_flow.h"
|
||||
#include "shader_recompiler/frontend/maxwell/translate_program.h"
|
||||
|
||||
namespace OpenGL::ShaderContext {
|
||||
struct ShaderPools {
|
||||
void ReleaseContents() {
|
||||
flow_block.ReleaseContents();
|
||||
block.ReleaseContents();
|
||||
inst.ReleaseContents();
|
||||
}
|
||||
|
||||
Shader::ObjectPool<Shader::IR::Inst> inst{8192};
|
||||
Shader::ObjectPool<Shader::IR::Block> block{32};
|
||||
Shader::ObjectPool<Shader::Maxwell::Flow::Block> flow_block{32};
|
||||
};
|
||||
|
||||
struct Context {
|
||||
explicit Context(Core::Frontend::EmuWindow& emu_window)
|
||||
: gl_context{emu_window.CreateSharedContext()}, scoped{*gl_context} {}
|
||||
|
||||
explicit Context(Core::Frontend::EmuWindow& emu_window) : gl_context{emu_window.CreateSharedContext()}, scoped{*gl_context} {}
|
||||
std::unique_ptr<Core::Frontend::GraphicsContext> gl_context;
|
||||
Core::Frontend::GraphicsContext::Scoped scoped;
|
||||
ShaderPools pools;
|
||||
Shader::Maxwell::ShaderPools pools;
|
||||
};
|
||||
|
||||
} // namespace OpenGL::ShaderContext
|
||||
|
||||
@@ -52,15 +52,6 @@
|
||||
namespace Vulkan {
|
||||
|
||||
namespace {
|
||||
using Shader::Backend::SPIRV::EmitSPIRV;
|
||||
using Shader::Maxwell::ConvertLegacyToGeneric;
|
||||
using Shader::Maxwell::GenerateGeometryPassthrough;
|
||||
using Shader::Maxwell::MergeDualVertexPrograms;
|
||||
using Shader::Maxwell::TranslateProgram;
|
||||
using VideoCommon::ComputeEnvironment;
|
||||
using VideoCommon::FileEnvironment;
|
||||
using VideoCommon::GenericEnvironment;
|
||||
using VideoCommon::GraphicsEnvironment;
|
||||
|
||||
constexpr u32 CACHE_VERSION = 18;
|
||||
constexpr size_t VULKAN_CACHE_FLUSH_PIPELINES = 128;
|
||||
@@ -627,12 +618,12 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
|
||||
if (device.IsKhrPipelineExecutablePropertiesEnabled()) {
|
||||
state.statistics = std::make_unique<PipelineStatistics>(device);
|
||||
}
|
||||
const auto load_compute{[&](std::ifstream& file, FileEnvironment env) {
|
||||
const auto load_compute{[&](std::ifstream& file, VideoCommon::FileEnvironment env) {
|
||||
ComputePipelineCacheKey key;
|
||||
file.read(reinterpret_cast<char*>(&key), sizeof(key));
|
||||
|
||||
workers.QueueWork([this, key, env_ = std::move(env), &state, &callback]() mutable {
|
||||
ShaderPools pools;
|
||||
Shader::Maxwell::ShaderPools pools;
|
||||
auto pipeline{CreateComputePipeline(pools, key, env_, state.statistics.get(), false)};
|
||||
std::scoped_lock lock{state.mutex};
|
||||
if (pipeline) {
|
||||
@@ -645,7 +636,7 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
|
||||
});
|
||||
++state.total;
|
||||
}};
|
||||
const auto load_graphics{[&](std::ifstream& file, std::vector<FileEnvironment> envs) {
|
||||
const auto load_graphics{[&](std::ifstream& file, std::vector<VideoCommon::FileEnvironment> envs) {
|
||||
GraphicsPipelineCacheKey key;
|
||||
file.read(reinterpret_cast<char*>(&key), sizeof(key));
|
||||
|
||||
@@ -678,7 +669,7 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
|
||||
}
|
||||
|
||||
workers.QueueWork([this, key, envs_ = std::move(envs), &state, &callback]() mutable {
|
||||
ShaderPools pools;
|
||||
Shader::Maxwell::ShaderPools pools;
|
||||
boost::container::static_vector<Shader::Environment*, 5> env_ptrs;
|
||||
for (auto& env : envs_) {
|
||||
env_ptrs.push_back(&env);
|
||||
@@ -785,10 +776,7 @@ GraphicsPipeline* PipelineCache::BuiltPipeline(GraphicsPipeline* pipeline) const
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
|
||||
ShaderPools& pools, const GraphicsPipelineCacheKey& key,
|
||||
std::span<Shader::Environment* const> envs, PipelineStatistics* statistics,
|
||||
bool build_in_parallel) try {
|
||||
std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(Shader::Maxwell::ShaderPools& pools, const GraphicsPipelineCacheKey& key, std::span<Shader::Environment* const> envs, PipelineStatistics* statistics, bool build_in_parallel) try {
|
||||
auto hash = key.Hash();
|
||||
LOG_DEBUG(Render_Vulkan, "{:#016x}", hash);
|
||||
size_t env_index{0};
|
||||
@@ -800,12 +788,10 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
|
||||
Shader::IR::Program* layer_source_program{};
|
||||
|
||||
for (size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) {
|
||||
const bool is_emulated_stage = layer_source_program != nullptr &&
|
||||
index == static_cast<u32>(Maxwell::ShaderType::Geometry);
|
||||
const bool is_emulated_stage = layer_source_program != nullptr && index == u32(Maxwell::ShaderType::Geometry);
|
||||
if (key.unique_hashes[index] == 0 && is_emulated_stage) {
|
||||
auto topology = MaxwellToOutputTopology(key.state.topology);
|
||||
programs[index] = GenerateGeometryPassthrough(pools.inst, pools.block, host_info,
|
||||
*layer_source_program, topology);
|
||||
programs[index] = Shader::Maxwell::GenerateGeometryPassthrough(pools, host_info, *layer_source_program, topology);
|
||||
continue;
|
||||
}
|
||||
if (key.unique_hashes[index] == 0) {
|
||||
@@ -814,16 +800,16 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
|
||||
Shader::Environment& env{*envs[env_index]};
|
||||
++env_index;
|
||||
|
||||
const u32 cfg_offset{static_cast<u32>(env.StartAddress() + sizeof(Shader::ProgramHeader))};
|
||||
const u32 cfg_offset{u32(env.StartAddress() + sizeof(Shader::ProgramHeader))};
|
||||
Shader::Maxwell::Flow::CFG cfg(env, pools.flow_block, cfg_offset, index == 0);
|
||||
if (!uses_vertex_a || index != 1) {
|
||||
// Normal path
|
||||
programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info);
|
||||
programs[index] = Shader::Maxwell::TranslateProgram(pools, env, cfg, host_info);
|
||||
} else {
|
||||
// VertexB path when VertexA is present.
|
||||
auto& program_va{programs[0]};
|
||||
auto program_vb{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
|
||||
programs[index] = MergeDualVertexPrograms(program_va, program_vb, env);
|
||||
auto program_vb{Shader::Maxwell::TranslateProgram(pools, env, cfg, host_info)};
|
||||
programs[index] = Shader::Maxwell::MergeDualVertexPrograms(program_va, program_vb, env);
|
||||
}
|
||||
|
||||
if (Settings::values.dump_guest_shaders) {
|
||||
@@ -853,8 +839,8 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
|
||||
infos[stage_index] = &program.info;
|
||||
|
||||
const auto runtime_info{MakeRuntimeInfo(programs, key, program, previous_stage, device)};
|
||||
ConvertLegacyToGeneric(program, runtime_info);
|
||||
const std::vector<u32> code{EmitSPIRV(profile, runtime_info, program, binding)};
|
||||
Shader::Maxwell::ConvertLegacyToGeneric(program, runtime_info);
|
||||
const std::vector<u32> code{Shader::Backend::SPIRV::EmitSPIRV(profile, runtime_info, program, binding)};
|
||||
device.SaveShader(code);
|
||||
modules[stage_index] = BuildShader(device, code);
|
||||
|
||||
@@ -910,14 +896,14 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline() {
|
||||
GraphicsEnvironments environments;
|
||||
GetGraphicsEnvironments(environments, graphics_key.unique_hashes);
|
||||
|
||||
main_pools.ReleaseContents();
|
||||
main_pools.clear();
|
||||
auto pipeline{
|
||||
CreateGraphicsPipeline(main_pools, graphics_key, environments.Span(), nullptr, true)};
|
||||
if (!pipeline || pipeline_cache_filename.empty()) {
|
||||
return pipeline;
|
||||
}
|
||||
serialization_thread.QueueWork([this, key = graphics_key, envs = std::move(environments.envs)] {
|
||||
boost::container::static_vector<const GenericEnvironment*, Maxwell::MaxShaderProgram>
|
||||
boost::container::static_vector<const VideoCommon::GenericEnvironment*, Maxwell::MaxShaderProgram>
|
||||
env_ptrs;
|
||||
for (size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) {
|
||||
if (key.unique_hashes[index] != 0) {
|
||||
@@ -934,25 +920,23 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
|
||||
const ComputePipelineCacheKey& key, const ShaderInfo* shader) {
|
||||
const GPUVAddr program_base{kepler_compute->regs.code_loc.Address()};
|
||||
const auto& qmd{kepler_compute->launch_description};
|
||||
ComputeEnvironment env{*kepler_compute, *gpu_memory, program_base, qmd.program_start};
|
||||
VideoCommon::ComputeEnvironment env{*kepler_compute, *gpu_memory, program_base, qmd.program_start};
|
||||
env.SetCachedSize(shader->size_bytes);
|
||||
|
||||
main_pools.ReleaseContents();
|
||||
main_pools.clear();
|
||||
auto pipeline{CreateComputePipeline(main_pools, key, env, nullptr, true)};
|
||||
if (!pipeline || pipeline_cache_filename.empty()) {
|
||||
return pipeline;
|
||||
}
|
||||
serialization_thread.QueueWork([this, key, env_ = std::move(env)] {
|
||||
SerializePipeline(key, std::array<const GenericEnvironment*, 1>{&env_},
|
||||
SerializePipeline(key, std::array<const VideoCommon::GenericEnvironment*, 1>{&env_},
|
||||
pipeline_cache_filename, CACHE_VERSION);
|
||||
});
|
||||
QueueVulkanPipelineCacheFlush();
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
|
||||
ShaderPools& pools, const ComputePipelineCacheKey& key, Shader::Environment& env,
|
||||
PipelineStatistics* statistics, bool build_in_parallel) try {
|
||||
std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(Shader::Maxwell::ShaderPools& pools, const ComputePipelineCacheKey& key, Shader::Environment& env, PipelineStatistics* statistics, bool build_in_parallel) try {
|
||||
auto hash = key.Hash();
|
||||
if (device.HasBrokenCompute()) {
|
||||
LOG_ERROR(Render_Vulkan, "Skipping {:#016x}", hash);
|
||||
@@ -968,11 +952,9 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
|
||||
env.Dump(hash, key.unique_hash);
|
||||
}
|
||||
|
||||
auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
|
||||
auto program{Shader::Maxwell::TranslateProgram(pools, env, cfg, host_info)};
|
||||
const VkDriverIdKHR driver_id = device.GetDriverID();
|
||||
const bool needs_shared_mem_clamp =
|
||||
driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY ||
|
||||
driver_id == VK_DRIVER_ID_ARM_PROPRIETARY;
|
||||
const bool needs_shared_mem_clamp = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || driver_id == VK_DRIVER_ID_ARM_PROPRIETARY;
|
||||
const u32 max_shared_memory = device.GetMaxComputeSharedMemorySize();
|
||||
if (needs_shared_mem_clamp && program.shared_memory_size > max_shared_memory) {
|
||||
LOG_WARNING(Render_Vulkan,
|
||||
@@ -982,7 +964,7 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
|
||||
max_shared_memory / 1024);
|
||||
program.shared_memory_size = max_shared_memory;
|
||||
}
|
||||
const std::vector<u32> code{EmitSPIRV(profile, program)};
|
||||
const std::vector<u32> code{Shader::Backend::SPIRV::EmitSPIRV(profile, program)};
|
||||
device.SaveShader(code);
|
||||
vk::ShaderModule spv_module{BuildShader(device, code)};
|
||||
|
||||
|
||||
@@ -15,15 +15,18 @@
|
||||
#include <type_traits>
|
||||
#include "common/container/unordered_map.h"
|
||||
#include <vector>
|
||||
#include <boost/container/stable_vector.hpp>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/thread_worker.h"
|
||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
#include "shader_recompiler/frontend/maxwell/control_flow.h"
|
||||
#include "shader_recompiler/frontend/maxwell/structured_control_flow.h"
|
||||
#include "shader_recompiler/frontend/maxwell/translate_program.h"
|
||||
#include "shader_recompiler/host_translate_info.h"
|
||||
#include "shader_recompiler/object_pool.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
#include "shader_recompiler/shader_pool.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/host1x/gpu_device_memory_manager.h"
|
||||
#include "video_core/renderer_vulkan/fixed_pipeline_state.h"
|
||||
@@ -90,18 +93,6 @@ class Scheduler;
|
||||
|
||||
using VideoCommon::ShaderInfo;
|
||||
|
||||
struct ShaderPools {
|
||||
void ReleaseContents() {
|
||||
flow_block.ReleaseContents();
|
||||
block.ReleaseContents();
|
||||
inst.ReleaseContents();
|
||||
}
|
||||
|
||||
Shader::ObjectPool<Shader::IR::Inst> inst{8192};
|
||||
Shader::ObjectPool<Shader::IR::Block> block{32};
|
||||
Shader::ObjectPool<Shader::Maxwell::Flow::Block> flow_block{32};
|
||||
};
|
||||
|
||||
class PipelineCache : public VideoCommon::ShaderCache {
|
||||
public:
|
||||
explicit PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, const Device& device,
|
||||
@@ -127,14 +118,14 @@ private:
|
||||
std::unique_ptr<GraphicsPipeline> CreateGraphicsPipeline();
|
||||
|
||||
std::unique_ptr<GraphicsPipeline> CreateGraphicsPipeline(
|
||||
ShaderPools& pools, const GraphicsPipelineCacheKey& key,
|
||||
Shader::Maxwell::ShaderPools& pools, const GraphicsPipelineCacheKey& key,
|
||||
std::span<Shader::Environment* const> envs, PipelineStatistics* statistics,
|
||||
bool build_in_parallel);
|
||||
|
||||
std::unique_ptr<ComputePipeline> CreateComputePipeline(const ComputePipelineCacheKey& key,
|
||||
const ShaderInfo* shader);
|
||||
|
||||
std::unique_ptr<ComputePipeline> CreateComputePipeline(ShaderPools& pools,
|
||||
std::unique_ptr<ComputePipeline> CreateComputePipeline(Shader::Maxwell::ShaderPools& pools,
|
||||
const ComputePipelineCacheKey& key,
|
||||
Shader::Environment& env,
|
||||
PipelineStatistics* statistics,
|
||||
@@ -166,7 +157,7 @@ private:
|
||||
::Common::unordered_map<ComputePipelineCacheKey, std::unique_ptr<ComputePipeline>> compute_cache;
|
||||
::Common::unordered_map<GraphicsPipelineCacheKey, std::unique_ptr<GraphicsPipeline>> graphics_cache;
|
||||
|
||||
ShaderPools main_pools;
|
||||
Shader::Maxwell::ShaderPools main_pools;
|
||||
|
||||
Shader::Profile profile;
|
||||
Shader::HostTranslateInfo host_info;
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "shader_recompiler/frontend/maxwell/control_flow.h"
|
||||
#include "shader_recompiler/object_pool.h"
|
||||
#include "video_core/control/channel_state.h"
|
||||
#include "video_core/dirty_flags.h"
|
||||
#include "video_core/engines/kepler_compute.h"
|
||||
@@ -236,7 +235,7 @@ const ShaderInfo* ShaderCache::MakeShaderInfo(GenericEnvironment& env, VAddr cpu
|
||||
} else {
|
||||
// Slow path, not really hit on commercial games
|
||||
// Build a control flow graph to get the real shader size
|
||||
Shader::ObjectPool<Shader::Maxwell::Flow::Block> flow_block;
|
||||
boost::container::stable_vector<Shader::Maxwell::Flow::Block> flow_block;
|
||||
Shader::Maxwell::Flow::CFG cfg{env, flow_block, env.StartAddress()};
|
||||
info->unique_hash = env.CalculateHash();
|
||||
info->size_bytes = env.ReadSizeBytes();
|
||||
|
||||
@@ -75,15 +75,3 @@ if (NOT MSVC)
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-unused-parameter>
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-missing-field-initializers>)
|
||||
endif()
|
||||
|
||||
if (EMSCRIPTEN)
|
||||
# 10GB is required at max... yikes!
|
||||
target_link_options(yuzu-cmd PRIVATE
|
||||
-sALLOW_MEMORY_GROWTH=1
|
||||
-sINITIAL_MEMORY=33554432
|
||||
-sMAXIMUM_MEMORY=10737418240
|
||||
-sGLOBAL_BASE=16777216
|
||||
-sEXPORTED_RUNTIME_METHODS=['FS']
|
||||
-sPTHREAD_POOL_SIZE_STRICT=0
|
||||
-sPTHREAD_POOL_SIZE=navigator.hardwareConcurrency)
|
||||
endif()
|
||||
|
||||
@@ -30,7 +30,6 @@ Tools for Eden and other subprojects. When adding new scripts please use `#!/bin
|
||||
- `find-unused-strings.sh`: Find any unused strings in the Android app (XML -> Kotlin).
|
||||
- `cpp-lint.sh`: Homemade dumb C++ linter.
|
||||
- `fuzzsettings.cpp`: Fuzz settings files.
|
||||
- `miniserver.js`: Make a quick server that serves a page with the WASM on it, takes a single argument which is the path to the build directory containing *both* `eden-cli.js` and `eden-cli.wasm`. Run via `node.js`, `wasmtime` isn't supported.
|
||||
|
||||
## Android
|
||||
|
||||
|
||||
@@ -1,127 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import { createServer } from 'http';
|
||||
import { readFile } from 'fs';
|
||||
import { join } from 'path';
|
||||
console.log(`dont forget to run: "npm --global install @jdmichaud/dwarf-2-sourcemap" for better debugging!`);
|
||||
const server = createServer((req, res) => {
|
||||
console.log(`get ${req.url}`);
|
||||
if (req.url === '/') {
|
||||
// https://developer.mozilla.org/en-US/docs/WebAssembly/Guides/Loading_and_running
|
||||
// If your browser doesn't support fetch... HAHA GET FUCKED
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'text/html',
|
||||
'Cross-Origin-Opener-Policy': 'same-origin',
|
||||
'Cross-Origin-Embedder-Policy': 'require-corp'
|
||||
});
|
||||
res.end(`<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>eden-cli</title>
|
||||
</head>
|
||||
<body style="margin:0;padding:0;background-color:black;color:white;font-family:Monospace,Tahoma,Arial;">
|
||||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:2px;width:100%;height:100vh;">
|
||||
<canvas id="canvas" oncontextmenu="event.preventDefault()" style="width:100%;height:100%;background-color:gray;"></canvas>
|
||||
<div id="tty-stdout"></div>
|
||||
</div>
|
||||
<script>
|
||||
var Module = { //do not prepend var
|
||||
mainScriptUrlOrBlob: 'eden-cli.js',
|
||||
arguments: ['--null-render', '--singlecore', '--filter', '*:Trace', '/home/web_user/game.nro'],
|
||||
canvas: document.getElementById('canvas'),
|
||||
print: (e) => {
|
||||
e = e.replace('[1;31m', '<span style="color:red;font-weight:bold;">');
|
||||
e = e.replace('[0;37m', '<span style="color:white;font-weight:bold;">');
|
||||
e = e.replace('[1;35m', '<span style="color:pink;font-weight:bold;">');
|
||||
e = e.replace('[1;33m', '<span style="color:yellow;font-weight:bold;">');
|
||||
e = e.replace('[0;36m', '<span style="color:white;font-weight:bold;">');
|
||||
e = e.replace('[0m', '</span>');
|
||||
document.getElementById('tty-stdout').innerHTML += \`\${e}</br>\`;
|
||||
},
|
||||
printErr: (e) => {
|
||||
document.getElementById('tty-stdout').innerHTML += \`<span style="color:red">\${e}</span></br>\`;
|
||||
},
|
||||
// not a wasm func but idc
|
||||
printInternal: (e) => {
|
||||
document.getElementById('tty-stdout').innerHTML += \`<span style="color:white">Internal WASM: \${e}</span></br>\`;
|
||||
},
|
||||
preInit: [
|
||||
() => {
|
||||
// copy just most relevant :)
|
||||
Module.FS.mkdir('/home/web_user/.local');
|
||||
Module.FS.mkdir('/home/web_user/.local/share');
|
||||
Module.FS.mkdir('/home/web_user/.local/share/eden');
|
||||
Module.FS.mkdir('/home/web_user/.config');
|
||||
Module.FS.mkdir('/home/web_user/.config/eden');
|
||||
Module.FS.createDataFile('/home/web_user', 'game.nro', gameNroFileBuffer, true, false, true);
|
||||
}
|
||||
],
|
||||
onRuntimeInitialized: () => { Module.printInternal("runtime ok"); },
|
||||
setStatus: (e) => { Module.printInternal(e); },
|
||||
monitorRunDependencies: (e) => { Module.printInternal("monitor deps: " + e); },
|
||||
__wasm_call_ctors: () => { Module.printInternal("ctors beep"); },
|
||||
};
|
||||
var gameNroFileBuffer = {};
|
||||
Module.printInternal(\`Atomics: \${window.Atomics}, SharedArrayBuffer: \${window.SharedArrayBuffer}\`);
|
||||
Module.printInternal("trying to load script (if it hangs here check console)");
|
||||
fetch('game.nro').then((resp) => {
|
||||
if (!resp.ok)
|
||||
throw Error(\`\${resp.status}\`);
|
||||
return resp.bytes();
|
||||
}).then((buffer) => {
|
||||
gameNroFileBuffer = buffer;
|
||||
// load the thingy AFTER loading the nro
|
||||
Module.printInternal(\`loading from ${build_dir}/\${Module.mainScriptUrlOrBlob}\`);
|
||||
var script = document.createElement('script');
|
||||
script.src = '/eden-cli.js';
|
||||
script.onload = (e) => Module.printInternal(\`loaded WASMy script \${e}!!\`);
|
||||
document.head.appendChild(script);
|
||||
}).catch(Module.printErr);
|
||||
</script>
|
||||
</body>
|
||||
</html>`);
|
||||
} else if (req.url === '/eden-cli.js') {
|
||||
readFile(join(build_dir, 'eden-cli.js'), (err, content) => {
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'application/javascript',
|
||||
'Cross-Origin-Opener-Policy': 'same-origin',
|
||||
'Cross-Origin-Embedder-Policy': 'require-corp'
|
||||
});
|
||||
res.end(content, 'utf-8');
|
||||
});
|
||||
} else if (req.url === '/eden-cli.wasm') {
|
||||
readFile(join(build_dir, 'eden-cli.wasm'), (err, content) => {
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'application/wasm',
|
||||
'Cross-Origin-Opener-Policy': 'same-origin',
|
||||
'Cross-Origin-Embedder-Policy': 'require-corp'
|
||||
});
|
||||
res.end(content);
|
||||
});
|
||||
} else if (req.url === '/game.nro') {
|
||||
readFile(nro_file, (err, content) => {
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'application/octet-stream',
|
||||
'Cross-Origin-Opener-Policy': 'same-origin',
|
||||
'Cross-Origin-Embedder-Policy': 'require-corp'
|
||||
});
|
||||
res.end(content);
|
||||
});
|
||||
} else {
|
||||
res.writeHead(404, {});
|
||||
res.end('', 'utf-8');
|
||||
}
|
||||
});
|
||||
|
||||
const build_dir = process.argv[2];
|
||||
const nro_file = process.argv[3];
|
||||
if (typeof build_dir == "undefined" || typeof nro_file == "undefined") {
|
||||
console.log(`Usage: ${process.argv[0]} ${process.argv[1]} [build directory] [NRO file]`);
|
||||
} else {
|
||||
server.listen(2210, () => {
|
||||
console.log(`${process.argv[0]} ${process.argv[1]} http://localhost:2210`);
|
||||
console.log(`build dir = ${build_dir}`);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user