mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-09-05 03:54:32 +00:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9a99044df8 | |||
| 79c4142760 | |||
| 11d65f2ab7 | |||
| f6e7686038 | |||
| 1dcc574591 | |||
| 5c20f244c9 | |||
| aa4e494c08 |
@@ -1,66 +0,0 @@
|
||||
#!/usr/local/bin/bash -ex
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
[ -z ${OO_PS4_TOOLCHAIN+x} ] && exit
|
||||
|
||||
# cmake
|
||||
[ -f "ps4-toolchain.cmake" ] || cat << EOF >"ps4-toolchain.cmake"
|
||||
set(CMAKE_SYSROOT "$OO_PS4_TOOLCHAIN")
|
||||
set(CMAKE_STAGING_PREFIX "$OO_PS4_TOOLCHAIN")
|
||||
set(CMAKE_SYSTEM_NAME "OpenOrbis")
|
||||
|
||||
set(CMAKE_C_FLAGS "-D__OPENORBIS__ -D_LIBCPP_HAS_MUSL_LIBC=1 -D_GNU_SOURCE=1 --target=x86_64-pc-freebsd12-elf -mtune=btver2 -march=btver2 -fPIC -funwind-tables -femulated-tls")
|
||||
set(CMAKE_CXX_FLAGS "-D__OPENORBIS__ -D_LIBCPP_HAS_MUSL_LIBC=1 -D_GNU_SOURCE=1 --target=x86_64-pc-freebsd12-elf -mtune=btver2 -march=btver2 -fPIC -funwind-tables -femulated-tls")
|
||||
|
||||
set(CMAKE_EXE_LINKER_FLAGS "-m elf_x86_64 -pie -T $OO_PS4_TOOLCHAIN/link.x --eh-frame-hdr -L$OO_PS4_TOOLCHAIN/lib")
|
||||
set(CMAKE_C_LINK_FLAGS "-m elf_x86_64 -pie -T $OO_PS4_TOOLCHAIN/link.x --eh-frame-hdr -L$OO_PS4_TOOLCHAIN/lib")
|
||||
set(CMAKE_CXX_LINK_FLAGS "-m elf_x86_64 -pie -T $OO_PS4_TOOLCHAIN/link.x --eh-frame-hdr -L$OO_PS4_TOOLCHAIN/lib")
|
||||
|
||||
set(CMAKE_C_COMPILER clang)
|
||||
set(CMAKE_CXX_COMPILER clang++)
|
||||
set(CMAKE_LINKER ld.lld)
|
||||
set(CMAKE_C_LINK_EXECUTABLE "<CMAKE_LINKER> <CMAKE_C_LINK_FLAGS> <OBJECTS> -o <TARGET> -lc -lkernel -lSceUserService -lSceSysmodule -lSceNet -lSceLibcInternal $OO_PS4_TOOLCHAIN/lib/crt1.o <LINK_LIBRARIES>")
|
||||
set(CMAKE_CXX_LINK_EXECUTABLE "<CMAKE_LINKER> <CMAKE_CXX_LINK_FLAGS> <OBJECTS> -o <TARGET> -lc -lkernel -lc++ -lSceUserService -lSceSysmodule -lSceNet -lSceLibcInternal $OO_PS4_TOOLCHAIN/lib/crt1.o <LINK_LIBRARIES>")
|
||||
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
|
||||
|
||||
# TODO: Why does cmake not set this?
|
||||
set(CMAKE_SIZEOF_VOID_P 8)
|
||||
EOF
|
||||
|
||||
[ -z ${NPROC+x} ] && NPROC=$(nproc || 1)
|
||||
|
||||
# Normally a platform has a package manager
|
||||
# PS4 does not, atleast not in the normal sense
|
||||
export EXTRA_CMAKE_FLAGS=("${EXTRA_CMAKE_FLAGS[@]}" $@)
|
||||
[ -f "build/Makefile" ] || cmake -S . -B build -G "Unix Makefiles" \
|
||||
-DCMAKE_TOOLCHAIN_FILE="ps4-toolchain.cmake" \
|
||||
-DENABLE_QT_TRANSLATION=OFF \
|
||||
-DENABLE_CUBEB=OFF \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_CXX_FLAGS="$ARCH_FLAGS" \
|
||||
-DCMAKE_C_FLAGS="$ARCH_FLAGS" \
|
||||
-DENABLE_SDL2=ON \
|
||||
-DENABLE_LIBUSB=OFF \
|
||||
-DENABLE_UPDATE_CHECKER=OFF \
|
||||
-DENABLE_QT=OFF \
|
||||
-DENABLE_OPENGL=ON \
|
||||
-DENABLE_WEB_SERVICE=OFF \
|
||||
-DUSE_DISCORD_PRESENCE=OFF \
|
||||
-DCPMUTIL_FORCE_BUNDLED=ON \
|
||||
-DYUZU_USE_EXTERNAL_FFMPEG=ON \
|
||||
-DYUZU_USE_CPM=ON \
|
||||
-DDYNARMIC_ENABLE_NO_EXECUTE_SUPPORT=OFF \
|
||||
-DDYNARMIC_TESTS=ON \
|
||||
-DYUZU_TESTS=ON \
|
||||
-DYUZU_USE_EXTERNAL_SDL2=ON \
|
||||
"${EXTRA_CMAKE_FLAGS[@]}" || exit
|
||||
|
||||
cmake --build build -t yuzu-cmd_pkg -- -j$NPROC
|
||||
#cmake --build build -t dynarmic_tests_pkg -- -j$NPROC
|
||||
#cmake --build build -t testps4_pkg -- -j$NPROC
|
||||
+2
-2
@@ -64,5 +64,5 @@ artifacts
|
||||
vulkansdk*.exe
|
||||
*.tar.zst
|
||||
|
||||
# PS4 toolchain stuff
|
||||
ps4-toolchain.cmake
|
||||
# Build-time generated external sources
|
||||
externals/generated
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
diff --git a/libs/asio/include/boost/asio/detail/impl/socket_ops.ipp b/libs/asio/include/boost/asio/detail/impl/socket_ops.ipp
|
||||
index 0129511c..10fc9b04 100644
|
||||
--- a/libs/asio/include/boost/asio/detail/impl/socket_ops.ipp
|
||||
+++ b/libs/asio/include/boost/asio/detail/impl/socket_ops.ipp
|
||||
@@ -15,6 +15,12 @@
|
||||
# pragma once
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
|
||||
+// hacky fix for ps4
|
||||
+#if defined(__OPENORBIS__)
|
||||
+# define FIONBIO 0
|
||||
+# define FIONREAD 1
|
||||
+#endif
|
||||
+
|
||||
#include <boost/asio/detail/config.hpp>
|
||||
|
||||
#include <cctype>
|
||||
@@ -1,13 +0,0 @@
|
||||
diff --git a/unix.c b/unix.c
|
||||
index 6669216..86a2faa 100644
|
||||
--- a/unix.c
|
||||
+++ b/unix.c
|
||||
@@ -53,7 +53,7 @@
|
||||
#include <poll.h>
|
||||
#endif
|
||||
|
||||
-#if !defined(HAS_SOCKLEN_T) && !defined(__socklen_t_defined)
|
||||
+#if !defined(__OPENORBIS__) && !defined(HAS_SOCKLEN_T) && !defined(__socklen_t_defined)
|
||||
typedef int socklen_t;
|
||||
#endif
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
diff --git a/libavutil/cpu.c b/libavutil/cpu.c
|
||||
index 03e2720..874258f 100644
|
||||
--- a/libavutil/cpu.c
|
||||
+++ b/libavutil/cpu.c
|
||||
@@ -43,8 +43,10 @@
|
||||
#include <sys/param.h>
|
||||
#endif
|
||||
#include <sys/types.h>
|
||||
+#ifndef __OPENORBIS__
|
||||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
+#endif
|
||||
#if HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
@@ -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,33 +0,0 @@
|
||||
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||||
index 1ca57e1..593fd62 100644
|
||||
--- a/CMakeLists.txt
|
||||
+++ b/CMakeLists.txt
|
||||
@@ -148,6 +148,16 @@ if(NOT BUILD_SHARED_LIBS)
|
||||
list(APPEND OPENSSL_CONFIGURE_OPTIONS no-shared)
|
||||
endif()
|
||||
|
||||
+list(APPEND OPENSSL_CONFIGURE_OPTIONS
|
||||
+ no-threads no-thread-pool no-shared no-afalgeng no-async no-capieng no-cmp no-cms no-comp no-ct
|
||||
+ no-docs no-dgram no-dso no-dynamic-engine no-engine no-filenames no-gost
|
||||
+ no-http no-legacy no-module no-nextprotoneg no-ocsp no-padlockeng no-quic
|
||||
+ no-srp no-srtp no-ssl-trace no-static-engine no-tests no-thread-pool no-ts
|
||||
+ no-ui-console no-uplink no-ssl3-method no-tls1-method no-tls1_1-method
|
||||
+ no-dtls1-method no-dtls1_2-method no-argon2 no-bf no-blake2 no-cast
|
||||
+ no-dsa no-idea no-md4 no-mdc2 no-ocb no-rc2 no-rc4 no-rmd160 no-scrypt
|
||||
+ no-siphash no-siv no-sm2 no-sm3 no-sm4 no-whirlpool no-apps)
|
||||
+
|
||||
list(SORT OPENSSL_CONFIGURE_OPTIONS)
|
||||
list(REMOVE_DUPLICATES OPENSSL_CONFIGURE_OPTIONS)
|
||||
list(PREPEND OPENSSL_CONFIGURE_OPTIONS ${OPENSSL_TARGET_PLATFORM})
|
||||
diff --git a/cmake/ConfigureOpenSSL.cmake b/cmake/ConfigureOpenSSL.cmake
|
||||
index 2ae23ff..22a4a27 100644
|
||||
--- a/cmake/ConfigureOpenSSL.cmake
|
||||
+++ b/cmake/ConfigureOpenSSL.cmake
|
||||
@@ -154,6 +154,7 @@ function(configure_openssl)
|
||||
"CXX=${CMAKE_CXX_COMPILER}"
|
||||
"LD=${EMSCRIPTEN_LINKER}"
|
||||
${CONFIGURE_COMMAND}
|
||||
+ "--prefix=${CMAKE_SYSROOT}"
|
||||
WORKING_DIRECTORY ${CONFIGURE_BUILD_DIR}
|
||||
${VERBOSE_OPTION}
|
||||
COMMAND_ERROR_IS_FATAL ANY
|
||||
@@ -1,13 +0,0 @@
|
||||
diff --git a/crypto/o_str.c b/crypto/o_str.c
|
||||
index 8aadff5..ba427c9 100644
|
||||
--- a/crypto/o_str.c
|
||||
+++ b/crypto/o_str.c
|
||||
@@ -385,7 +385,7 @@ int openssl_strerror_r(int errnum, char *buf, size_t buflen)
|
||||
* It can return a pointer to some (immutable) static string in which case
|
||||
* buf is left unused.
|
||||
*/
|
||||
- err = strerror_r(errnum, buf, buflen);
|
||||
+ err = (char *)strerror_r(errnum, buf, buflen);
|
||||
if (err == NULL || buflen == 0)
|
||||
return 0;
|
||||
/*
|
||||
@@ -1,196 +0,0 @@
|
||||
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||||
index 8872895..b0eeca8 100644
|
||||
--- a/CMakeLists.txt
|
||||
+++ b/CMakeLists.txt
|
||||
@@ -1770,7 +1770,6 @@ elseif(EMSCRIPTEN)
|
||||
|
||||
CheckPTHREAD()
|
||||
CheckLibUnwind()
|
||||
-
|
||||
elseif(UNIX AND NOT APPLE AND NOT RISCOS AND NOT HAIKU)
|
||||
|
||||
set(SDL_DISABLE_DLOPEN_NOTES TRUE)
|
||||
@@ -3647,10 +3646,30 @@ if(NOT HAVE_CAMERA)
|
||||
)
|
||||
endif()
|
||||
|
||||
+# Always on?
|
||||
+if (OPENORBIS)
|
||||
+ sdl_sources(
|
||||
+ "${SDL3_SOURCE_DIR}/src/thread/pthread/SDL_systhread.c"
|
||||
+ "${SDL3_SOURCE_DIR}/src/thread/pthread/SDL_sysmutex.c" # Can be faked, if necessary
|
||||
+ "${SDL3_SOURCE_DIR}/src/thread/pthread/SDL_syscond.c" # Can be faked, if necessary
|
||||
+ "${SDL3_SOURCE_DIR}/src/thread/pthread/SDL_sysrwlock.c" # Can be faked, if necessary
|
||||
+ "${SDL3_SOURCE_DIR}/src/thread/pthread/SDL_systls.c"
|
||||
+ "${SDL3_SOURCE_DIR}/src/thread/generic/SDL_syssem.c"
|
||||
+ )
|
||||
+ sdl_glob_sources("${SDL3_SOURCE_DIR}/src/time/unix/*.c")
|
||||
+ sdl_glob_sources("${SDL3_SOURCE_DIR}/src/timer/unix/*.c")
|
||||
+ set(HAVE_SDL_THREADS TRUE)
|
||||
+ set(HAVE_SDL_TIMERS TRUE)
|
||||
+ set(HAVE_SDL_TIME TRUE)
|
||||
+ set(SDL_TIME_UNIX 1)
|
||||
+ set(SDL_TIMER_UNIX 1)
|
||||
+ set(SDL_THREAD_PTHREAD 1)
|
||||
+endif ()
|
||||
+
|
||||
# We always need to have threads and timers around
|
||||
if(NOT HAVE_SDL_THREADS)
|
||||
# The Emscripten and N-Gage platform has been carefully vetted to work without threads
|
||||
- if(EMSCRIPTEN OR NGAGE)
|
||||
+ if(EMSCRIPTEN OR NGAGE OR OPENORBIS)
|
||||
set(SDL_THREADS_DISABLED 1)
|
||||
sdl_glob_sources(
|
||||
"${SDL3_SOURCE_DIR}/src/thread/generic/*.c"
|
||||
diff --git a/build-scripts/rename_macros.py b/build-scripts/rename_macros.py
|
||||
index 978120c..b6063dd 100755
|
||||
--- a/build-scripts/rename_macros.py
|
||||
+++ b/build-scripts/rename_macros.py
|
||||
@@ -124,6 +124,7 @@ RENAMED_MACROS = {
|
||||
"__RISCOS__": "SDL_PLATFORM_RISCOS",
|
||||
"__SOLARIS__": "SDL_PLATFORM_SOLARIS",
|
||||
"__PSP__": "SDL_PLATFORM_PSP",
|
||||
+ "__PS4__": "SDL_PLATFORM_PS4",
|
||||
"__PS2__": "SDL_PLATFORM_PS2",
|
||||
"__VITA__": "SDL_PLATFORM_VITA",
|
||||
"__3DS__": "SDL_PLATFORM_3DS",
|
||||
diff --git a/include/SDL3/SDL_platform_defines.h b/include/SDL3/SDL_platform_defines.h
|
||||
index 7963149..6c0dd5d 100644
|
||||
--- a/include/SDL3/SDL_platform_defines.h
|
||||
+++ b/include/SDL3/SDL_platform_defines.h
|
||||
@@ -442,6 +442,16 @@
|
||||
#define SDL_PLATFORM_PSP 1
|
||||
#endif
|
||||
|
||||
+#if defined(__PS4__) || defined(__OPENORBIS__)
|
||||
+
|
||||
+/**
|
||||
+ * A preprocessor macro that is only defined if compiling for Sony PSP.
|
||||
+ *
|
||||
+ * \since This macro is available since SDL 3.2.0.
|
||||
+ */
|
||||
+#define SDL_PLATFORM_PS4 1
|
||||
+#endif
|
||||
+
|
||||
#if defined(__PS2__) || defined(PS2)
|
||||
|
||||
/**
|
||||
diff --git a/src/SDL.c b/src/SDL.c
|
||||
index b1a1a56..0a3fec6 100644
|
||||
--- a/src/SDL.c
|
||||
+++ b/src/SDL.c
|
||||
@@ -714,7 +714,7 @@ void SDL_Quit(void)
|
||||
SDL_DBus_Quit();
|
||||
#endif
|
||||
|
||||
-#if defined(SDL_PLATFORM_UNIX) && !defined(SDL_PLATFORM_ANDROID) && !defined(SDL_PLATFORM_EMSCRIPTEN) && !defined(SDL_PLATFORM_PRIVATE)
|
||||
+#if defined(SDL_PLATFORM_UNIX) && !defined(SDL_PLATFORM_ANDROID) && !defined(SDL_PLATFORM_EMSCRIPTEN) && !defined(SDL_PLATFORM_PRIVATE) && !defined(SDL_PLATFORM_PS4)
|
||||
SDL_Gtk_Quit();
|
||||
#endif
|
||||
|
||||
@@ -813,6 +813,8 @@ const char *SDL_GetPlatform(void)
|
||||
return "tvOS";
|
||||
#elif defined(SDL_PLATFORM_PS2)
|
||||
return "PlayStation 2";
|
||||
+#elif defined(SDL_PLATFORM_PS4)
|
||||
+ return "PlayStation 4";
|
||||
#elif defined(SDL_PLATFORM_PSP)
|
||||
return "PlayStation Portable";
|
||||
#elif defined(SDL_PLATFORM_VITA)
|
||||
diff --git a/src/audio/SDL_audio.c b/src/audio/SDL_audio.c
|
||||
index c1e4241..65b1e6d 100644
|
||||
--- a/src/audio/SDL_audio.c
|
||||
+++ b/src/audio/SDL_audio.c
|
||||
@@ -80,6 +80,9 @@ static const AudioBootStrap *const bootstrap[] = {
|
||||
#ifdef SDL_AUDIO_DRIVER_NGAGE
|
||||
&NGAGEAUDIO_bootstrap,
|
||||
#endif
|
||||
+#ifdef SDL_AUDIO_DRIVER_PS4
|
||||
+ &PS4AUDIO_bootstrap,
|
||||
+#endif
|
||||
#ifdef SDL_AUDIO_DRIVER_EMSCRIPTEN
|
||||
&EMSCRIPTENAUDIO_bootstrap,
|
||||
#endif
|
||||
diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c
|
||||
index 60c9743..2d12c3a 100644
|
||||
--- a/src/joystick/SDL_joystick.c
|
||||
+++ b/src/joystick/SDL_joystick.c
|
||||
@@ -106,6 +106,9 @@ static SDL_JoystickDriver *SDL_joystick_drivers[] = {
|
||||
#ifdef SDL_JOYSTICK_N3DS
|
||||
&SDL_N3DS_JoystickDriver,
|
||||
#endif
|
||||
+#ifdef SDL_JOYSTICK_PS4
|
||||
+ &SDL_PS4_JoystickDriver,
|
||||
+#endif
|
||||
#if defined(SDL_JOYSTICK_DUMMY) || defined(SDL_JOYSTICK_DISABLED)
|
||||
&SDL_DUMMY_JoystickDriver
|
||||
#endif
|
||||
diff --git a/src/joystick/SDL_sysjoystick.h b/src/joystick/SDL_sysjoystick.h
|
||||
index 47ce42c..42ab9de 100644
|
||||
--- a/src/joystick/SDL_sysjoystick.h
|
||||
+++ b/src/joystick/SDL_sysjoystick.h
|
||||
@@ -253,6 +253,7 @@ extern SDL_JoystickDriver SDL_HAIKU_JoystickDriver;
|
||||
extern SDL_JoystickDriver SDL_HIDAPI_JoystickDriver;
|
||||
extern SDL_JoystickDriver SDL_RAWINPUT_JoystickDriver;
|
||||
extern SDL_JoystickDriver SDL_IOS_JoystickDriver;
|
||||
+extern SDL_JoystickDriver SDL_PS4_JoystickDriver;
|
||||
extern SDL_JoystickDriver SDL_LINUX_JoystickDriver;
|
||||
extern SDL_JoystickDriver SDL_VIRTUAL_JoystickDriver;
|
||||
extern SDL_JoystickDriver SDL_WGI_JoystickDriver;
|
||||
diff --git a/src/render/SDL_sysrender.h b/src/render/SDL_sysrender.h
|
||||
index a1bb44a..51843c4 100644
|
||||
--- a/src/render/SDL_sysrender.h
|
||||
+++ b/src/render/SDL_sysrender.h
|
||||
@@ -381,6 +381,7 @@ extern SDL_RenderDriver NGAGE_RenderDriver;
|
||||
extern SDL_RenderDriver VULKAN_RenderDriver;
|
||||
extern SDL_RenderDriver PS2_RenderDriver;
|
||||
extern SDL_RenderDriver PSP_RenderDriver;
|
||||
+extern SDL_RenderDriver PS4_RenderDriver;
|
||||
extern SDL_RenderDriver SW_RenderDriver;
|
||||
extern SDL_RenderDriver VITA_GXM_RenderDriver;
|
||||
extern SDL_RenderDriver GPU_RenderDriver;
|
||||
diff --git a/src/stdlib/SDL_string.c b/src/stdlib/SDL_string.c
|
||||
index 4a870f6..69ca1db 100644
|
||||
--- a/src/stdlib/SDL_string.c
|
||||
+++ b/src/stdlib/SDL_string.c
|
||||
@@ -32,7 +32,9 @@
|
||||
|
||||
#include "SDL_casefolding.h"
|
||||
|
||||
-#if defined(__SIZEOF_WCHAR_T__)
|
||||
+#if defined(SDL_PLATFORM_PS4)
|
||||
+#define SDL_SIZEOF_WCHAR_T 2
|
||||
+#elif defined(__SIZEOF_WCHAR_T__)
|
||||
#define SDL_SIZEOF_WCHAR_T __SIZEOF_WCHAR_T__
|
||||
#elif defined(SDL_PLATFORM_NGAGE)
|
||||
#define SDL_SIZEOF_WCHAR_T 2
|
||||
@@ -43,7 +45,6 @@
|
||||
#endif
|
||||
SDL_COMPILE_TIME_ASSERT(sizeof_wchar_t, sizeof(wchar_t) == SDL_SIZEOF_WCHAR_T);
|
||||
|
||||
-
|
||||
char *SDL_UCS4ToUTF8(Uint32 codepoint, char *dst)
|
||||
{
|
||||
if (!dst) {
|
||||
diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c
|
||||
index f650f27..e23424f 100644
|
||||
--- a/src/video/SDL_video.c
|
||||
+++ b/src/video/SDL_video.c
|
||||
@@ -131,6 +131,9 @@ static VideoBootStrap *bootstrap[] = {
|
||||
#ifdef SDL_VIDEO_DRIVER_NGAGE
|
||||
&NGAGE_bootstrap,
|
||||
#endif
|
||||
+#ifdef SDL_VIDEO_DRIVER_PS4
|
||||
+ &PS4_bootstrap,
|
||||
+#endif
|
||||
#ifdef SDL_VIDEO_DRIVER_KMSDRM
|
||||
&KMSDRM_bootstrap,
|
||||
#endif
|
||||
@@ -314,6 +317,7 @@ static bool SDL_CreateWindowTexture(SDL_VideoDevice *_this, SDL_Window *window,
|
||||
SDL_GetWindowSizeInPixels(window, &w, &h);
|
||||
|
||||
if (!data) {
|
||||
+
|
||||
SDL_Renderer *renderer = NULL;
|
||||
const char *render_driver = NULL;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,25 +0,0 @@
|
||||
diff --git a/source/opt/loop_dependence.cpp b/source/opt/loop_dependence.cpp
|
||||
index e41c044..a51b53b 100644
|
||||
--- a/source/opt/loop_dependence.cpp
|
||||
+++ b/source/opt/loop_dependence.cpp
|
||||
@@ -12,6 +12,12 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
+// PS4: issue?
|
||||
+#ifdef __PS4__
|
||||
+#pragma clang diagnostic ignored "-Wabsolute-value"
|
||||
+#pragma clang diagnostic ignored "-Wshorten-64-to-32"
|
||||
+#endif
|
||||
+
|
||||
#include "source/opt/loop_dependence.h"
|
||||
|
||||
#include <functional>
|
||||
@@ -19,6 +25,7 @@
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
+#include <cstdlib>
|
||||
|
||||
#include "source/opt/instruction.h"
|
||||
#include "source/opt/scalar_analysis_nodes.h"
|
||||
+3
-4
@@ -22,7 +22,6 @@ include(UseCcache)
|
||||
include(CMakeDependentOption)
|
||||
include(CTest)
|
||||
include(CPMUtil)
|
||||
include(OpenOrbis)
|
||||
|
||||
if (NOT DEFINED ARCHITECTURE)
|
||||
message(FATAL_ERROR "Architecture didn't make it out of scope, did you delete DetectArchitecture.cmake?")
|
||||
@@ -215,8 +214,6 @@ if(MSVC)
|
||||
endif()
|
||||
|
||||
# TODO(crueter): Cleanup, each dep that has a bundled option should allow to choose between bundled, external, system
|
||||
cmake_dependent_option(YUZU_USE_BUNDLED_SDL3 "Download bundled SDL3 build" "${MSVC}" "NOT ANDROID" OFF)
|
||||
|
||||
option(ENABLE_CUBEB "Enables the cubeb audio backend" ON)
|
||||
|
||||
set(EXT_DEFAULT OFF)
|
||||
@@ -224,6 +221,8 @@ if (MSVC OR ANDROID)
|
||||
set(EXT_DEFAULT ON)
|
||||
endif()
|
||||
|
||||
option(YUZU_USE_BUNDLED_SDL3 "Download bundled SDL3 build" ${EXT_DEFAULT})
|
||||
|
||||
# ffmpeg
|
||||
option(YUZU_USE_BUNDLED_FFMPEG "Download bundled FFmpeg" ${EXT_DEFAULT})
|
||||
cmake_dependent_option(YUZU_USE_EXTERNAL_FFMPEG "Build FFmpeg from external source" "${SOLARIS}" "NOT WIN32 AND NOT ANDROID" OFF)
|
||||
@@ -239,7 +238,7 @@ option(YUZU_USE_BUNDLED_SIRIT "Download bundled sirit" ${BUNDLED_SIRIT_DEFAULT})
|
||||
# FreeBSD 15+ has libusb, versions below should disable it
|
||||
cmake_dependent_option(ENABLE_LIBUSB "Enable the use of LibUSB" ON "WIN32 OR LINUX OR FREEBSD OR APPLE" OFF)
|
||||
|
||||
cmake_dependent_option(ENABLE_OPENGL "Enable OpenGL" ON "NOT (WIN32 AND ARCHITECTURE_arm64) AND NOT APPLE" OFF)
|
||||
cmake_dependent_option(ENABLE_OPENGL "Enable OpenGL" ON "NOT (WIN32 AND ARCHITECTURE_arm64) AND NOT APPLE AND NOT ANDROID" OFF)
|
||||
mark_as_advanced(FORCE ENABLE_OPENGL)
|
||||
|
||||
option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON)
|
||||
|
||||
@@ -18,13 +18,10 @@ if (DEFINED GIT_RELEASE)
|
||||
set(BUILD_VERSION "${GIT_TAG}")
|
||||
set(GIT_REFSPEC "${GIT_RELEASE}")
|
||||
set(IS_DEV_BUILD false)
|
||||
elseif(DEFINED GIT_COMMIT)
|
||||
else()
|
||||
string(SUBSTRING ${GIT_COMMIT} 0 10 BUILD_VERSION)
|
||||
set(BUILD_VERSION "${BUILD_VERSION}-${GIT_REFSPEC}")
|
||||
set(IS_DEV_BUILD true)
|
||||
else()
|
||||
set(BUILD_VERSION "NoGitInfo")
|
||||
set(IS_DEV_BUILD true)
|
||||
endif()
|
||||
|
||||
if (NIGHTLY_BUILD)
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
function(create_ps4_eboot project target content_id)
|
||||
set(sce_sys_dir sce_sys)
|
||||
set(sce_sys_param ${sce_sys_dir}/param.sfo)
|
||||
add_custom_command(
|
||||
OUTPUT "${target}.pkg"
|
||||
COMMAND ${CMAKE_SYSROOT}/bin/create-fself -in=bin/${target} -out=${target}.oelf --eboot ${target}_eboot.bin
|
||||
VERBATIM
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
DEPENDS ${project}
|
||||
)
|
||||
add_custom_target(${project}_pkg ALL DEPENDS "${target}.pkg")
|
||||
endfunction()
|
||||
|
||||
function(create_ps4_pkg project target content_id)
|
||||
set(sce_sys_dir sce_sys)
|
||||
set(sce_sys_param ${sce_sys_dir}/param.sfo)
|
||||
add_custom_command(
|
||||
OUTPUT "${target}.pkg"
|
||||
COMMAND ${CMAKE_SYSROOT}/bin/create-fself -in=bin/${target} -out=${target}.oelf --eboot ${target}_eboot.bin
|
||||
COMMAND mkdir -p ${sce_sys_dir}
|
||||
COMMAND ${CMAKE_SYSROOT}/bin/PkgTool.Core sfo_new ${sce_sys_param}
|
||||
COMMAND ${CMAKE_SYSROOT}/bin/PkgTool.Core sfo_setentry ${sce_sys_param} APP_TYPE --type Integer --maxsize 4 --value 1
|
||||
COMMAND ${CMAKE_SYSROOT}/bin/PkgTool.Core sfo_setentry ${sce_sys_param} APP_VER --type Utf8 --maxsize 8 --value 1.03
|
||||
COMMAND ${CMAKE_SYSROOT}/bin/PkgTool.Core sfo_setentry ${sce_sys_param} ATTRIBUTE --type Integer --maxsize 4 --value 0
|
||||
COMMAND ${CMAKE_SYSROOT}/bin/PkgTool.Core sfo_setentry ${sce_sys_param} CATEGORY --type Utf8 --maxsize 4 --value gd
|
||||
COMMAND ${CMAKE_SYSROOT}/bin/PkgTool.Core sfo_setentry ${sce_sys_param} CONTENT_ID --type Utf8 --maxsize 48 --value ${content_id}
|
||||
COMMAND ${CMAKE_SYSROOT}/bin/PkgTool.Core sfo_setentry ${sce_sys_param} DOWNLOAD_DATA_SIZE --type Integer --maxsize 4 --value 0
|
||||
COMMAND ${CMAKE_SYSROOT}/bin/PkgTool.Core sfo_setentry ${sce_sys_param} SYSTEM_VER --type Integer --maxsize 4 --value 0
|
||||
COMMAND ${CMAKE_SYSROOT}/bin/PkgTool.Core sfo_setentry ${sce_sys_param} TITLE --type Utf8 --maxsize 128 --value ${target}
|
||||
COMMAND ${CMAKE_SYSROOT}/bin/PkgTool.Core sfo_setentry ${sce_sys_param} TITLE_ID --type Utf8 --maxsize 12 --value BREW00090
|
||||
COMMAND ${CMAKE_SYSROOT}/bin/PkgTool.Core sfo_setentry ${sce_sys_param} VERSION --type Utf8 --maxsize 8 --value 1.03
|
||||
COMMAND ${CMAKE_SYSROOT}/bin/create-gp4 -out ${target}.gp4 --content-id=${content_id} --files "${target}_eboot.bin ${sce_sys_param} sce_module/libc.prx sce_module/libSceFios2.prx"
|
||||
COMMAND ${CMAKE_SYSROOT}/bin/PkgTool.Core pkg_build ${target}.gp4 .
|
||||
VERBATIM
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
DEPENDS ${project}
|
||||
)
|
||||
add_custom_target(${project}_pkg ALL DEPENDS "${target}.pkg")
|
||||
endfunction()
|
||||
|
||||
if (NOT DEFINED ENV{OO_PS4_TOOLCHAIN})
|
||||
set(ENV{OO_PS4_TOOLCHAIN} ${CMAKE_SYSROOT})
|
||||
endif ()
|
||||
+6
-26
@@ -20,8 +20,7 @@
|
||||
"min_version": "1.57",
|
||||
"package": "Boost",
|
||||
"patches": [
|
||||
"0001-clang-cl.patch",
|
||||
"0004-openorbis.patch"
|
||||
"0001-clang-cl.patch"
|
||||
],
|
||||
"repo": "boostorg/boost",
|
||||
"version": "boost-1.90.0"
|
||||
@@ -79,18 +78,12 @@
|
||||
"hash": "a0d2fa8c957704dd49e00a726284ac5ca034b50b00d2b20a94fa1bbfbb80841467834bfdc84aa0ed0d6aab894608fd6c86c3b94eee46343f0e6d9c22e391dbf9",
|
||||
"min_version": "1.3",
|
||||
"repo": "lsalzman/enet",
|
||||
"version": "v1.3.18",
|
||||
"patches": [
|
||||
"0001-openorbis.patch"
|
||||
]
|
||||
"version": "v1.3.18"
|
||||
},
|
||||
"ffmpeg": {
|
||||
"hash": "ed177621176b3961bdcaa339187d3a7688c1c8b060b79c4bb0257cbc67ad7021ae5d5adca5303b45625abbbe3d9aafdd87ce777b8690ac295290d744c875489a",
|
||||
"repo": "FFmpeg/FFmpeg",
|
||||
"version": "c7b5f1537d",
|
||||
"patches": [
|
||||
"0001-sysctl-openorbis.patch"
|
||||
]
|
||||
"version": "c7b5f1537d"
|
||||
},
|
||||
"ffmpeg-ci": {
|
||||
"ci": true,
|
||||
@@ -193,19 +186,12 @@
|
||||
"repo": "eden-emulator/oaknut",
|
||||
"version": "v2.0.3"
|
||||
},
|
||||
"oboe": {
|
||||
"bundled": true,
|
||||
"hash": "ce4011afe7345370d4ead3b891cd69a5ef224b129535783586c0ca75051d303ed446e6c7f10bde8da31fff58d6e307f1732a3ffd03b249f9ef1fd48fd4132715",
|
||||
"repo": "google/oboe",
|
||||
"version": "1.10.0"
|
||||
},
|
||||
"openssl": {
|
||||
"hash": "29002ce50cb95a4f4f1d0e9d3f684401fbd4eac34203dc2eef3b6334af5d44aa46bf788b63a6f5c139c383eafb7269ae87a58a9a3ad5912903b9773e545ccc0a",
|
||||
"min_version": "3.0.0",
|
||||
"package": "OpenSSL",
|
||||
"patches": [
|
||||
"0001-add-bundled-cert.patch",
|
||||
"0002-fix-gnu-openorbis.patch"
|
||||
"0001-add-bundled-cert.patch"
|
||||
],
|
||||
"repo": "openssl/openssl",
|
||||
"version": "openssl-3.6.2"
|
||||
@@ -227,9 +213,7 @@
|
||||
"0001-cpmutil-compat.patch",
|
||||
"0002-use-ccache.patch",
|
||||
"0003-use-cmake-compiler-flags.patch",
|
||||
"0004-use-shell-wrapper.patch",
|
||||
"0005-wasm-support.patch",
|
||||
"0006-openorbis-prefix.patch"
|
||||
"0004-use-shell-wrapper.patch"
|
||||
],
|
||||
"repo": "jimmy-park/openssl-cmake",
|
||||
"version": "3.6.2"
|
||||
@@ -267,11 +251,7 @@
|
||||
"min_version": "3.2.10",
|
||||
"package": "SDL3",
|
||||
"repo": "libsdl-org/SDL",
|
||||
"version": "release-3.4.8",
|
||||
"patches": [
|
||||
"0001-ps4.patch",
|
||||
"0002-ps4-drivers.patch"
|
||||
]
|
||||
"version": "release-3.4.8"
|
||||
},
|
||||
"sdl3-ci": {
|
||||
"ci": true,
|
||||
|
||||
Vendored
-223
@@ -1,223 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="512"
|
||||
height="512"
|
||||
fill="none"
|
||||
viewBox="0 0 512 512"
|
||||
version="1.1"
|
||||
id="svg7"
|
||||
sodipodi:docname="ps4.svg"
|
||||
inkscape:version="1.4.3 (0d15f75042, 2025-12-25)"
|
||||
inkscape:export-filename="base.svg.2026_01_12_14_43_47.0.svg"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs7">
|
||||
<linearGradient
|
||||
id="linearGradient5"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
style="stop-color:#003e74;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop4" />
|
||||
<stop
|
||||
style="stop-color:#2ea8ff;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop5" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient1"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
style="stop-color:#3579ff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop2" />
|
||||
<stop
|
||||
style="stop-color:#0b00ff;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop3" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="swatch37"
|
||||
inkscape:swatch="solid">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop37" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="swatch28"
|
||||
inkscape:swatch="solid">
|
||||
<stop
|
||||
style="stop-color:#252525;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop28" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="swatch27"
|
||||
inkscape:swatch="solid">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop27" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="swatch15"
|
||||
inkscape:swatch="solid">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop16" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient14"
|
||||
inkscape:swatch="gradient">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop14" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop15" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="swatch9"
|
||||
inkscape:swatch="solid">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop10" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="swatch8"
|
||||
inkscape:swatch="solid">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop9" />
|
||||
</linearGradient>
|
||||
<rect
|
||||
x="22.627417"
|
||||
y="402.76802"
|
||||
width="521.34025"
|
||||
height="248.94868"
|
||||
id="rect24" />
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath18">
|
||||
<circle
|
||||
style="opacity:1;mix-blend-mode:normal;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10.8382;stroke-opacity:0.566238;paint-order:stroke fill markers"
|
||||
id="circle18"
|
||||
cx="-246.8315"
|
||||
cy="246.8338"
|
||||
inkscape:label="Circle"
|
||||
r="191.89999" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath22">
|
||||
<circle
|
||||
style="opacity:1;mix-blend-mode:normal;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10.8382;stroke-opacity:0.566238;paint-order:stroke fill markers"
|
||||
id="circle22"
|
||||
cx="256"
|
||||
cy="256"
|
||||
inkscape:label="Circle"
|
||||
r="191.89999" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath128">
|
||||
<circle
|
||||
style="fill:none;fill-opacity:1;stroke:#03ffff;stroke-width:0;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="circle128"
|
||||
cx="256"
|
||||
cy="256"
|
||||
r="192" />
|
||||
</clipPath>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient1"
|
||||
id="linearGradient3"
|
||||
x1="256"
|
||||
y1="64"
|
||||
x2="256"
|
||||
y2="448"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.1874952,0,0,-1.1874952,-47.370662,560.66391)" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5"
|
||||
id="linearGradient4"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(-1.1874952,0,0,1.1874952,560.61528,-47.345282)"
|
||||
x1="256"
|
||||
y1="64"
|
||||
x2="256"
|
||||
y2="448" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="1.4142136"
|
||||
inkscape:cx="262.69017"
|
||||
inkscape:cy="76.367532"
|
||||
inkscape:window-width="1600"
|
||||
inkscape:window-height="849"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg7" />
|
||||
<circle
|
||||
style="opacity:1;mix-blend-mode:screen;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:15.907;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path1"
|
||||
cx="256.61652"
|
||||
cy="257.31778"
|
||||
r="227.33479" />
|
||||
<path
|
||||
id="path8-7"
|
||||
style="display:inline;mix-blend-mode:multiply;fill:url(#linearGradient3);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;stroke-dasharray:none;stroke-opacity:0.566238;paint-order:stroke fill markers"
|
||||
inkscape:label="Circle"
|
||||
d="m 256.62812,484.66422 a 227.99908,228.00372 0 0 1 -94.5288,-20.52182 c 17.54241,-0.96226 34.77843,-4.71825 46.2868,-10.46733 12.69502,-6.3416 25.70492,-17.24174 35.38366,-26.3736 h 36.45053 c 0.93211,0.61234 1.88758,1.23061 2.86668,1.85086 4.89722,14.18571 13.27668,38.80747 17.49933,51.23264 a 227.99908,228.00372 0 0 1 -43.9582,4.27925 z m 55.26491,-6.80041 -16.39068,-41.47736 c 6.99845,3.71973 14.6813,7.07649 22.62503,9.76921 20.75463,7.03513 39.46594,9.14022 49.31353,9.76688 a 227.99908,228.00372 0 0 1 -55.54788,21.94127 z m 58.6952,-23.72487 c -8.69086,-4.31956 -29.66977,-14.9556 -49.97917,-26.83747 h 87.23219 a 227.99908,228.00372 0 0 1 -37.25302,26.83747 z m -241.49988,-8.48659 a 227.99908,228.00372 0 0 1 -23.67337,-18.35088 h 95.80906 c -1.32304,0.72101 -2.11058,1.13186 -2.11058,1.13186 l 9.37935,10.63433 -34.47214,-6.30174 c 0,-2e-5 -10.96427,4.13624 -36.05858,11.02861 -3.09966,0.85131 -6.06162,1.45425 -8.87374,1.85782 z M 96.054184,418.52496 A 227.99908,228.00372 0 0 1 64.743277,379.80069 h 53.833883 c 16.2593,10.82121 36.03109,20.56336 55.93056,22.04797 47.50893,3.54468 62.979,-1.67459 62.979,-1.67459 0,0 -4.13964,4.3321 -13.0323,12.01204 -2.4447,2.11135 -5.46123,4.28998 -8.54904,6.33885 z m 156.603246,0 c 4.10483,-4.24043 6.54978,-7.04162 6.54978,-7.04162 0,0 3.17296,2.8621 8.63949,7.04162 z m 54.10293,0 c -19.35695,-13.88741 -22.86161,-25.14433 -22.86161,-25.14433 0,0 49.2172,17.2685 90.39112,0.28064 10.4477,-4.31055 19.37408,-9.1049 26.88099,-13.86058 h 47.3421 a 227.99908,228.00372 0 0 1 -31.31091,38.72427 z M 59.385631,371.02418 A 227.99908,228.00372 0 0 1 41.543041,332.29992 H 159.20017 c 1.30773,2.3199 2.75142,4.57103 4.34409,6.73083 12.77878,17.32895 25.06121,26.96783 33.0852,31.99343 h -27.15468 c -5.2719,-0.84 -10.81534,-1.78919 -16.40924,-2.84819 -27.04349,-5.11994 -72.845405,-20.48007 -72.845405,-20.48007 0,0 10.296355,11.13669 26.131855,23.32826 z m 238.424449,0 c 30.05703,-10.10404 47.05829,-24.07021 56.34572,-38.72426 h 117.55739 a 227.99908,228.00372 0 0 1 -17.84258,38.72426 h -40.22176 c 13.33114,-10.41219 19.38725,-18.85882 19.38725,-18.85882 0,0 -30.08919,11.11726 -59.29823,18.85882 z m -41.2585,-14.62133 c -0.90134,-0.01 -9.08983,-0.64727 -40.38179,-18.55498 -2.80164,-1.60327 -5.61377,-3.47048 -8.41452,-5.54795 h 37.53597 c 6.43727,15.58979 11.34615,24.10293 11.34615,24.10293 0,0 -0.0258,6.4e-4 -0.0858,0 z m 12.72847,-0.0418 c 0,0 -2.97304,-8.3824 -6.55675,-24.06118 h 46.48163 c -24.16151,23.08045 -39.92488,24.06123 -39.92488,24.06123 z M 38.653164,323.52341 a 227.99908,228.00372 0 0 1 -8.28,-38.72426 H 147.25331 c 0.50409,11.4347 2.40394,25.61361 7.73264,38.72426 z m 158.567726,0 c -12.80955,-11.77756 -24.77977,-26.58932 -33.71374,-38.72426 h 65.85729 c 4.01234,15.08912 8.39298,28.09557 12.44549,38.72426 z m 63.60057,0 c -2.16662,-10.57203 -4.40101,-23.55135 -6.23667,-38.72426 h 95.8253 c -7.46906,9.7857 -18.55287,23.51811 -32.62829,38.72426 z m 97.99155,0 c 5.95644,-13.69644 6.2152,-27.44278 5.00743,-38.72426 h 119.06262 a 227.99908,228.00372 0 0 1 -8.28,38.72426 z M 29.452392,276.02264 A 227.99908,228.00372 0 0 1 28.62903,256.6605 227.99908,228.00372 0 0 1 29.452392,237.29836 H 219.64878 c 2.02639,13.95226 4.61647,26.90106 7.48446,38.72428 H 157.2241 c -5.80191,-8.33616 -9.27267,-13.98815 -9.27267,-13.98815 0,0 -0.7416,5.5263 -0.8396,13.98815 z m 224.144368,0 c -1.22897,-11.76966 -2.19512,-24.70616 -2.7113,-38.72428 h 232.91837 a 227.99908,228.00372 0 0 1 0.82337,19.36214 227.99908,228.00372 0 0 1 -0.82337,19.36214 H 362.56568 c -0.39943,-2.2888 -0.8157,-4.40137 -1.19214,-6.30638 0,0 -1.52342,2.281 -4.43454,6.30638 z M 30.373164,228.52186 a 227.99908,228.00372 0 0 1 8.28,-38.72427 H 218.96921 c -1.16001,13.8461 -1.41371,26.99937 -0.37573,38.72427 z m 220.252536,0 c -0.27246,-12.17105 -0.19776,-25.09608 0.33166,-38.72427 h 223.6457 a 227.99908,228.00372 0 0 1 8.28,38.72427 z M 41.540725,181.02109 a 227.99908,228.00372 0 0 1 17.84259,-38.72427 H 225.63264 c -2.42151,12.98777 -4.45297,26.07281 -5.81919,38.72427 z m 209.892105,0 c 1.03997,-13.90922 3.42234,-26.81596 6.74692,-38.72427 h 195.69317 a 227.99908,228.00372 0 0 1 17.84258,38.72427 z M 64.740961,133.52032 A 227.99908,228.00372 0 0 1 96.051856,94.796041 H 236.06961 c -3.05987,12.287259 -6.07477,25.390049 -8.74155,38.724279 z m 196.112969,0 c 4.80192,-14.52175 10.96848,-27.42708 17.67095,-38.724279 h 138.6795 a 227.99908,228.00372 0 0 1 31.3109,38.724279 z M 105.41267,86.019537 A 227.99908,228.00372 0 0 1 254.61726,28.666053 c -3.30134,10.858112 -9.7429,31.901037 -16.31878,57.353484 z m 178.62756,0 C 299.82749,62.260898 317.24402,46.83239 326.52798,39.636692 a 227.99908,228.00372 0 0 1 81.31559,46.382845 z"
|
||||
sodipodi:insensitive="true" />
|
||||
<path
|
||||
id="path8-7-2"
|
||||
style="display:inline;mix-blend-mode:multiply;fill:url(#linearGradient4);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;stroke-dasharray:none;stroke-opacity:0.566238;paint-order:stroke fill markers"
|
||||
inkscape:label="Circle"
|
||||
d="m 256.61651,28.654412 a 227.99908,228.00372 0 0 1 94.5288,20.521817 C 333.6029,50.138492 316.36688,53.89448 304.85851,59.64356 292.1635,65.985164 279.15359,76.8853 269.47486,86.017162 h -36.45054 c -0.93211,-0.612343 -1.88758,-1.230613 -2.86668,-1.850865 -4.89722,-14.18571 -13.27667,-38.807461 -17.49932,-51.232639 a 227.99908,228.00372 0 0 1 43.95819,-4.279246 z m -55.26491,6.800405 16.39068,41.477366 c -6.99844,-3.719734 -14.6813,-7.076497 -22.62503,-9.769213 -20.75463,-7.035126 -39.46594,-9.140222 -49.31352,-9.766875 A 227.99908,228.00372 0 0 1 201.3516,35.454817 Z m -58.6952,23.724872 c 8.69086,4.319561 29.66977,14.955598 49.97917,26.837473 H 105.40338 A 227.99908,228.00372 0 0 1 142.6564,59.179689 Z m 241.49988,8.486589 a 227.99908,228.00372 0 0 1 23.67337,18.350884 h -95.80906 c 1.32305,-0.721011 2.11058,-1.131861 2.11058,-1.131861 l -9.37935,-10.634328 34.47215,6.30174 c 0,1.2e-5 10.96426,-4.136248 36.05857,-11.028611 3.09966,-0.851315 6.06162,-1.454254 8.87374,-1.857824 z m 33.03417,27.127388 a 227.99908,228.00372 0 0 1 31.31091,38.724274 h -53.83388 c -16.25931,-10.82121 -36.0311,-20.56336 -55.93057,-22.04797 -47.50892,-3.54468 -62.97899,1.67459 -62.97899,1.67459 0,0 4.13963,-4.3321 13.03229,-12.01204 2.4447,-2.111347 5.46123,-4.289985 8.54904,-6.338854 z m -156.60325,0 c -4.10482,4.240439 -6.54978,7.041624 -6.54978,7.041624 0,0 -3.17295,-2.862104 -8.63949,-7.041624 z m -54.10293,0 c 19.35695,13.887414 22.86161,25.144334 22.86161,25.144334 0,0 -49.2172,-17.2685 -90.39112,-0.28064 -10.4477,4.31055 -19.37408,9.1049 -26.88099,13.86058 H 64.73166 A 227.99908,228.00372 0 0 1 96.042582,94.793666 Z m 247.37474,47.500784 a 227.99908,228.00372 0 0 1 17.84257,38.72426 H 354.04446 c -1.30773,-2.3199 -2.75142,-4.57103 -4.34409,-6.73083 -12.77878,-17.32895 -25.06121,-26.96783 -33.0852,-31.99343 h 27.15468 c 5.2719,0.84 10.81534,1.78919 16.40924,2.84819 27.04349,5.11994 72.84541,20.48007 72.84541,20.48007 0,0 -10.29636,-11.13669 -26.13186,-23.32826 z m -238.42446,0 c -30.05702,10.10404 -47.05829,24.07021 -56.34572,38.72426 H 41.531435 a 227.99908,228.00372 0 0 1 17.842579,-38.72426 h 40.221767 c -13.331141,10.41219 -19.387248,18.85882 -19.387248,18.85882 0,0 30.089197,-11.11726 59.298227,-18.85882 z m 41.2585,14.62133 c 0.90134,0.009 9.08984,0.64727 40.38179,18.55498 2.80164,1.60327 5.61377,3.47048 8.41452,5.54795 h -37.53597 c -6.43727,-15.58979 -11.34615,-24.10293 -11.34615,-24.10293 0,0 0.0258,-6.4e-4 0.0859,0 z m -12.72847,0.0418 c 0,0 2.97304,8.3824 6.55675,24.06118 H 204.0397 c 24.16152,-23.08045 39.92488,-24.06123 39.92488,-24.06123 z m 230.62688,32.83764 a 227.99908,228.00372 0 0 1 8.28,38.72426 H 365.99132 c -0.50409,-11.4347 -2.40394,-25.61361 -7.73264,-38.72426 z m -158.56772,0 c 12.80955,11.77756 24.77977,26.58932 33.71374,38.72426 h -65.85729 c -4.01234,-15.08912 -8.39298,-28.09557 -12.44549,-38.72426 z m -63.60057,0 c 2.16662,10.57203 4.40101,23.55135 6.23667,38.72426 h -95.8253 c 7.46906,-9.7857 18.55287,-23.51811 32.62829,-38.72426 z m -97.99154,0 c -5.95644,13.69644 -6.21521,27.44278 -5.00743,38.72426 H 30.361558 a 227.99908,228.00372 0 0 1 8.280001,-38.72426 z m 329.3606,47.50077 a 227.99908,228.00372 0 0 1 0.82336,19.36214 227.99908,228.00372 0 0 1 -0.82336,19.36214 H 293.59585 c -2.02639,-13.95226 -4.61647,-26.90106 -7.48446,-38.72428 h 69.90914 c 5.80191,8.33616 9.27267,13.98815 9.27267,13.98815 0,0 0.74161,-5.5263 0.8396,-13.98815 z m -224.14436,0 c 1.22897,11.76966 2.19512,24.70616 2.7113,38.72428 H 29.440786 a 227.99908,228.00372 0 0 1 -0.823361,-19.36214 227.99908,228.00372 0 0 1 0.823361,-19.36214 H 150.67895 c 0.39943,2.2888 0.8157,4.40137 1.19214,6.30638 0,0 1.52343,-2.281 4.43455,-6.30638 z m 223.22359,47.50078 a 227.99908,228.00372 0 0 1 -8.28,38.72427 H 294.27542 c 1.16002,-13.8461 1.41371,-26.99937 0.37574,-38.72427 z m -220.25252,0 c 0.27245,12.17105 0.19775,25.09608 -0.33167,38.72427 H 38.641559 a 227.99908,228.00372 0 0 1 -8.280001,-38.72427 z m 209.08496,47.50077 a 227.99908,228.00372 0 0 1 -17.84258,38.72427 H 287.61199 c 2.42151,-12.98777 4.45297,-26.07281 5.81919,-38.72427 z m -209.89209,0 c -1.03998,13.90921 -3.42235,26.81596 -6.74692,38.72427 H 59.371698 A 227.99908,228.00372 0 0 1 41.52912,332.29754 Z m 186.69187,47.50077 a 227.99908,228.00372 0 0 1 -31.3109,38.72428 H 277.17503 c 3.05986,-12.28726 6.07476,-25.39005 8.74154,-38.72428 z m -196.11297,0 c -4.80193,14.52175 -10.96849,27.42708 -17.67095,38.72428 H 96.040255 A 227.99908,228.00372 0 0 1 64.729344,379.79831 Z m 155.44125,47.50078 a 227.99908,228.00372 0 0 1 -149.20459,57.35348 c 3.30135,-10.85811 9.7429,-31.90104 16.31879,-57.35348 z m -178.62756,0 c -15.78726,23.75863 -33.20379,39.18714 -42.48775,46.38284 a 227.99908,228.00372 0 0 1 -81.31558,-46.38284 z"
|
||||
sodipodi:insensitive="true" />
|
||||
<ellipse
|
||||
style="fill:none;fill-opacity:0.679924;stroke:#97f0e5;stroke-width:17.5749;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path192"
|
||||
cx="88.20919"
|
||||
cy="216.80666"
|
||||
rx="43.306744"
|
||||
ry="42.75507" />
|
||||
<path
|
||||
style="fill:none;fill-opacity:0.679924;stroke:#97f0e5;stroke-width:17.5749;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 211.35225,346.71976 151.38374,303.96994 137.1338,384.12586 Z"
|
||||
id="path195" />
|
||||
<path
|
||||
id="path196-7"
|
||||
style="fill:none;fill-opacity:0.679924;stroke:#97f0e5;stroke-width:17.5749;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 309.50171,362.30735 86.0678,57.93833 m -75.48269,11.93227 57.93833,-86.0678" />
|
||||
<rect
|
||||
style="fill:none;fill-opacity:0.679924;stroke:#97f0e5;stroke-width:13.9781;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect197"
|
||||
width="66.112526"
|
||||
height="59.501274"
|
||||
x="93.831078"
|
||||
y="-503.61469"
|
||||
transform="rotate(108)" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 16 KiB |
@@ -12,7 +12,6 @@
|
||||
- [NetBSD](#netbsd)
|
||||
- [MSYS2](#msys2)
|
||||
- [RedoxOS](#redoxos)
|
||||
- [PlayStation 4](#playstation-4)
|
||||
<!-- /TOC -->
|
||||
|
||||
## Arch Linux
|
||||
@@ -243,14 +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.
|
||||
|
||||
## PlayStation 4
|
||||
|
||||
```sh
|
||||
export OO_PS4_TOOLCHAIN="$HOME/OpenOrbis/PS4Toolchain/prefix"
|
||||
export DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1
|
||||
```
|
||||
|
||||
```sh
|
||||
cp $OO_PS4_TOOLCHAIN/include/endian.h $OO_PS4_TOOLCHAIN/include/sys/endian.h
|
||||
```
|
||||
|
||||
+1
-1
@@ -76,7 +76,7 @@ The following options are desktop only.
|
||||
|
||||
- `ENABLE_LIBUSB` (ON) Enable the use of the libusb input backend (HIGHLY RECOMMENDED)
|
||||
- `ENABLE_OPENGL` (ON) Enable the OpenGL graphics backend
|
||||
- Unavailable on Windows/ARM64
|
||||
- Unavailable on Windows/ARM64 and on Android
|
||||
- You probably shouldn't turn this off.
|
||||
|
||||
### Qt
|
||||
|
||||
@@ -35,7 +35,6 @@ Eden will store configuration files in the following directories:
|
||||
- **Android**: Data is stored internally.
|
||||
- **Linux, macOS, FreeBSD, Solaris, OpenBSD**: `$XDG_DATA_HOME`, `$XDG_CACHE_HOME`, `$XDG_CONFIG_HOME`.
|
||||
- **HaikuOS**: `/boot/home/config/settings/eden`
|
||||
- **PlayStation 4**: `/data/eden`
|
||||
|
||||
If a `user` directory is present in the current working directory, that will override all global configuration directories and the emulator will use the `user` directory instead.
|
||||
|
||||
|
||||
Vendored
+37
-48
@@ -136,48 +136,49 @@ if(ENABLE_CUBEB)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (NOT ANDROID)
|
||||
if (NOT YUZU_USE_BUNDLED_SDL3)
|
||||
if (NOT WIN32)
|
||||
# Yuzu itself needs: Atomic Audio Events Joystick Haptic Sensor Threads Timers
|
||||
# Since 2.0.18 Atomic+Threads required for HIDAPI/libusb (see https://github.com/libsdl-org/SDL/issues/5095)
|
||||
# Yuzu-cmd also needs: Video (depends on Loadso/Dlopen)
|
||||
# CPUinfo also required for SDL Audio, at least until 2.28.0 (see https://github.com/libsdl-org/SDL/issues/7809)
|
||||
set(SDL_UNUSED_SUBSYSTEMS
|
||||
File Filesystem
|
||||
Locale Power Render)
|
||||
foreach(_SUB ${SDL_UNUSED_SUBSYSTEMS})
|
||||
string(TOUPPER ${_SUB} _OPT)
|
||||
set(SDL_${_OPT} OFF)
|
||||
endforeach()
|
||||
if (NOT YUZU_USE_BUNDLED_SDL3)
|
||||
if (NOT WIN32)
|
||||
# Yuzu itself needs: Atomic Audio Events Joystick Haptic Sensor Threads Timers
|
||||
# Since 2.0.18 Atomic+Threads required for HIDAPI/libusb (see https://github.com/libsdl-org/SDL/issues/5095)
|
||||
# Yuzu-cmd also needs: Video (depends on Loadso/Dlopen)
|
||||
# CPUinfo also required for SDL Audio, at least until 2.28.0 (see https://github.com/libsdl-org/SDL/issues/7809)
|
||||
set(SDL_UNUSED_SUBSYSTEMS
|
||||
File Filesystem
|
||||
Locale Power Render)
|
||||
foreach(_SUB ${SDL_UNUSED_SUBSYSTEMS})
|
||||
string(TOUPPER ${_SUB} _OPT)
|
||||
set(SDL_${_OPT} OFF)
|
||||
endforeach()
|
||||
|
||||
set(HIDAPI ON)
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
set(SDL_FILE ON)
|
||||
endif()
|
||||
|
||||
AddJsonPackage(sdl3)
|
||||
else()
|
||||
message(STATUS "Using bundled SDL3")
|
||||
AddJsonPackage(sdl3-ci)
|
||||
set(HIDAPI ON)
|
||||
endif()
|
||||
|
||||
# Normalize SDL3 link target across package variants.
|
||||
# Some SDL3 packages export only SDL3::SDL3-shared or SDL3::SDL3-static.
|
||||
if (NOT TARGET SDL3::SDL3)
|
||||
if (TARGET SDL3::SDL3-shared)
|
||||
add_library(SDL3::SDL3 ALIAS SDL3::SDL3-shared)
|
||||
elseif (TARGET SDL3::SDL3-static)
|
||||
add_library(SDL3::SDL3 ALIAS SDL3::SDL3-static)
|
||||
else()
|
||||
message(FATAL_ERROR "SDL3 package found, but no usable SDL3 target was exported")
|
||||
endif()
|
||||
if (APPLE)
|
||||
set(SDL_FILE ON)
|
||||
endif()
|
||||
|
||||
AddJsonPackage(sdl3)
|
||||
else()
|
||||
message(STATUS "Using bundled SDL3")
|
||||
AddJsonPackage(sdl3-ci)
|
||||
|
||||
# copy sdl java sources for consumption by Gradle
|
||||
if (ANDROID)
|
||||
file(COPY ${SDL3_SOURCE_DIR}/java DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/generated/sdl)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(BUILD_SHARED_LIBS OFF)
|
||||
# Normalize SDL3 link target across package variants.
|
||||
# Some SDL3 packages export only SDL3::SDL3-shared or SDL3::SDL3-static.
|
||||
if (NOT TARGET SDL3::SDL3)
|
||||
if (TARGET SDL3::SDL3-shared)
|
||||
add_library(SDL3::SDL3 ALIAS SDL3::SDL3-shared)
|
||||
elseif (TARGET SDL3::SDL3-static)
|
||||
add_library(SDL3::SDL3 ALIAS SDL3::SDL3-static)
|
||||
else()
|
||||
message(FATAL_ERROR "SDL3 package found, but no usable SDL3 target was exported")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# SPIRV Headers
|
||||
AddJsonPackage(spirv-headers)
|
||||
@@ -254,11 +255,6 @@ target_include_directories(tz PUBLIC ./tz)
|
||||
add_library(bc_decoder bc_decoder/bc_decoder.cpp)
|
||||
target_include_directories(bc_decoder PUBLIC ./bc_decoder)
|
||||
|
||||
if (OPENORBIS)
|
||||
add_library(ps4sup ps4sup/emutls.c ps4sup/stub.cpp)
|
||||
target_include_directories(ps4sup PUBLIC ./ps4sup)
|
||||
endif()
|
||||
|
||||
if (NOT TARGET RenderDoc::API)
|
||||
add_library(renderdoc INTERFACE)
|
||||
target_include_directories(renderdoc SYSTEM INTERFACE ./renderdoc)
|
||||
@@ -390,13 +386,6 @@ if (YUZU_CRASH_DUMPS AND NOT TARGET libbreakpad_client)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# oboe
|
||||
if (ANDROID)
|
||||
AddJsonPackage(oboe)
|
||||
|
||||
add_library(oboe::oboe ALIAS oboe)
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
# moltenvk
|
||||
if (NOT YUZU_USE_BUNDLED_MOLTENVK)
|
||||
|
||||
Vendored
-184
@@ -1,184 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* ===---------- emutls.c - Implements __emutls_get_address ---------------===
|
||||
*
|
||||
* The LLVM Compiler Infrastructure
|
||||
*
|
||||
* This file is dual licensed under the MIT and the University of Illinois Open
|
||||
* Source Licenses. See LICENSE.TXT for details.
|
||||
*
|
||||
* ===----------------------------------------------------------------------===
|
||||
*/
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
//#include "int_util.h"
|
||||
|
||||
/* Default is not to use posix_memalign, so systems like Android
|
||||
* can use thread local data without heavier POSIX memory allocators.
|
||||
*/
|
||||
#ifndef EMUTLS_USE_POSIX_MEMALIGN
|
||||
#define EMUTLS_USE_POSIX_MEMALIGN 0
|
||||
#endif
|
||||
|
||||
/* For every TLS variable xyz,
|
||||
* there is one __emutls_control variable named __emutls_v.xyz.
|
||||
* If xyz has non-zero initial value, __emutls_v.xyz's "value"
|
||||
* will point to __emutls_t.xyz, which has the initial value.
|
||||
*/
|
||||
typedef struct __emutls_control {
|
||||
size_t size; /* size of the object in bytes */
|
||||
size_t align; /* alignment of the object in bytes */
|
||||
union {
|
||||
uintptr_t index; /* data[index-1] is the object address */
|
||||
void* address; /* object address, when in single thread env */
|
||||
} object;
|
||||
void* value; /* null or non-zero initial value for the object */
|
||||
} __emutls_control;
|
||||
|
||||
static inline void* emutls_memalign_alloc(size_t align, size_t size) {
|
||||
void *base;
|
||||
#if EMUTLS_USE_POSIX_MEMALIGN
|
||||
if (posix_memalign(&base, align, size) != 0)
|
||||
abort();
|
||||
#else
|
||||
#define EXTRA_ALIGN_PTR_BYTES (align - 1 + sizeof(void*))
|
||||
char* object;
|
||||
if ((object = malloc(EXTRA_ALIGN_PTR_BYTES + size)) == NULL)
|
||||
abort();
|
||||
base = (void*)(((uintptr_t)(object + EXTRA_ALIGN_PTR_BYTES))
|
||||
& ~(uintptr_t)(align - 1));
|
||||
|
||||
((void**)base)[-1] = object;
|
||||
#endif
|
||||
return base;
|
||||
}
|
||||
|
||||
static inline void emutls_memalign_free(void* base) {
|
||||
#if EMUTLS_USE_POSIX_MEMALIGN
|
||||
free(base);
|
||||
#else
|
||||
/* The mallocated address is in ((void**)base)[-1] */
|
||||
free(((void**)base)[-1]);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Emulated TLS objects are always allocated at run-time. */
|
||||
static inline void* emutls_allocate_object(__emutls_control* control) {
|
||||
/* Use standard C types, check with gcc's emutls.o. */
|
||||
//typedef unsigned int gcc_word __attribute__((mode(word)));
|
||||
//typedef unsigned int gcc_pointer __attribute__((mode(pointer)));
|
||||
//COMPILE_TIME_ASSERT(sizeof(size_t) == sizeof(gcc_word));
|
||||
//COMPILE_TIME_ASSERT(sizeof(uintptr_t) == sizeof(gcc_pointer));
|
||||
//COMPILE_TIME_ASSERT(sizeof(uintptr_t) == sizeof(void*));
|
||||
|
||||
size_t size = control->size;
|
||||
size_t align = control->align;
|
||||
if (align < sizeof(void*))
|
||||
align = sizeof(void*);
|
||||
/* Make sure that align is power of 2. */
|
||||
if ((align & (align - 1)) != 0)
|
||||
abort();
|
||||
|
||||
void* base = emutls_memalign_alloc(align, size);
|
||||
if (control->value)
|
||||
memcpy(base, control->value, size);
|
||||
else
|
||||
memset(base, 0, size);
|
||||
return base;
|
||||
}
|
||||
|
||||
static pthread_mutex_t emutls_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
static size_t emutls_num_object = 0; /* number of allocated TLS objects */
|
||||
|
||||
typedef struct emutls_address_array {
|
||||
uintptr_t size; /* number of elements in the 'data' array */
|
||||
void* data[];
|
||||
} emutls_address_array;
|
||||
|
||||
static pthread_key_t emutls_pthread_key;
|
||||
|
||||
static void emutls_key_destructor(void* ptr) {
|
||||
emutls_address_array* array = (emutls_address_array*)ptr;
|
||||
uintptr_t i;
|
||||
for (i = 0; i < array->size; ++i) {
|
||||
if (array->data[i])
|
||||
emutls_memalign_free(array->data[i]);
|
||||
}
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
static void emutls_init(void) {
|
||||
if (pthread_key_create(&emutls_pthread_key, emutls_key_destructor) != 0)
|
||||
abort();
|
||||
}
|
||||
|
||||
/* Returns control->object.index; set index if not allocated yet. */
|
||||
static inline uintptr_t emutls_get_index(__emutls_control* control) {
|
||||
uintptr_t index = __atomic_load_n(&control->object.index, __ATOMIC_ACQUIRE);
|
||||
if (!index) {
|
||||
static pthread_once_t once = PTHREAD_ONCE_INIT;
|
||||
pthread_once(&once, emutls_init);
|
||||
pthread_mutex_lock(&emutls_mutex);
|
||||
index = control->object.index;
|
||||
if (!index) {
|
||||
index = ++emutls_num_object;
|
||||
__atomic_store_n(&control->object.index, index, __ATOMIC_RELEASE);
|
||||
}
|
||||
pthread_mutex_unlock(&emutls_mutex);
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
/* Updates newly allocated thread local emutls_address_array. */
|
||||
static inline void emutls_check_array_set_size(emutls_address_array* array,
|
||||
uintptr_t size) {
|
||||
if (array == NULL)
|
||||
abort();
|
||||
array->size = size;
|
||||
pthread_setspecific(emutls_pthread_key, (void*)array);
|
||||
}
|
||||
|
||||
/* Returns the new 'data' array size, number of elements,
|
||||
* which must be no smaller than the given index.
|
||||
*/
|
||||
static inline uintptr_t emutls_new_data_array_size(uintptr_t index) {
|
||||
/* Need to allocate emutls_address_array with one extra slot
|
||||
* to store the data array size.
|
||||
* Round up the emutls_address_array size to multiple of 16.
|
||||
*/
|
||||
return ((index + 1 + 15) & ~((uintptr_t)15)) - 1;
|
||||
}
|
||||
|
||||
/* Returns the thread local emutls_address_array.
|
||||
* Extends its size if necessary to hold address at index.
|
||||
*/
|
||||
static inline emutls_address_array* emutls_get_address_array(uintptr_t index) {
|
||||
emutls_address_array* array = pthread_getspecific(emutls_pthread_key);
|
||||
if (array == NULL) {
|
||||
uintptr_t new_size = emutls_new_data_array_size(index);
|
||||
array = calloc(new_size + 1, sizeof(void*));
|
||||
emutls_check_array_set_size(array, new_size);
|
||||
} else if (index > array->size) {
|
||||
uintptr_t orig_size = array->size;
|
||||
uintptr_t new_size = emutls_new_data_array_size(index);
|
||||
array = realloc(array, (new_size + 1) * sizeof(void*));
|
||||
if (array)
|
||||
memset(array->data + orig_size, 0,
|
||||
(new_size - orig_size) * sizeof(void*));
|
||||
emutls_check_array_set_size(array, new_size);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
void* __emutls_get_address(__emutls_control* control) {
|
||||
uintptr_t index = emutls_get_index(control);
|
||||
emutls_address_array* array = emutls_get_address_array(index);
|
||||
if (array->data[index - 1] == NULL)
|
||||
array->data[index - 1] = emutls_allocate_object(control);
|
||||
return array->data[index - 1];
|
||||
}
|
||||
Vendored
-44
@@ -1,44 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#define STUB_WEAK(name) \
|
||||
extern "C" void name() { \
|
||||
printf("called " #name); \
|
||||
asm volatile("ud2"); \
|
||||
}
|
||||
|
||||
#define STUB_ZERO(name) \
|
||||
extern "C" int name() { \
|
||||
printf("called " #name " = ret zero"); \
|
||||
return 0; \
|
||||
}
|
||||
|
||||
|
||||
extern "C" int __pthread_cxa_finalize();
|
||||
extern "C" void __cxa_thread_atexit_impl() {
|
||||
//printf("__cxa_thread_atexit_impl called!\n");
|
||||
//__pthread_cxa_finalize();
|
||||
}
|
||||
|
||||
STUB_WEAK(ZSTD_trace_compress_begin)
|
||||
STUB_WEAK(ZSTD_trace_compress_end)
|
||||
STUB_WEAK(ZSTD_trace_decompress_begin)
|
||||
STUB_WEAK(ZSTD_trace_decompress_end)
|
||||
|
||||
extern "C" thread_local struct {
|
||||
char pad[512];
|
||||
} _ThreadRuneLocale = {};
|
||||
extern "C" struct {
|
||||
char pad[512];
|
||||
} __h_errno = {};
|
||||
|
||||
FILE* __stderrp = stdout;
|
||||
FILE* __stdinp = stdin;
|
||||
|
||||
#undef STUB_WEAK
|
||||
|
||||
// THIS MAKES STD::COUT AND SUCH WORK :)
|
||||
#include <iostream>
|
||||
std::ios_base::Init init;
|
||||
Vendored
+1
@@ -11,6 +11,7 @@
|
||||
#include <limits>
|
||||
#include <span>
|
||||
#include <array>
|
||||
#include <algorithm>
|
||||
#include <time.h>
|
||||
|
||||
namespace Tz {
|
||||
|
||||
@@ -272,6 +272,13 @@ android {
|
||||
resValue("string", "app_name_suffixed", "$currentName$suffix")
|
||||
resValue("string", "app_name", "Eden$suffix")
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
named("main") {
|
||||
java.srcDir("${edenDir}/externals/generated/sdl/java")
|
||||
kotlin.srcDir("${edenDir}/externals/generated/sdl/java")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
idea {
|
||||
|
||||
Vendored
+3
@@ -9,6 +9,9 @@
|
||||
-keep class org.ini4j.spi.IniBuilder
|
||||
-keep class org.ini4j.spi.IniFormatter
|
||||
|
||||
-keep class org.libsdl.app.** { *; }
|
||||
-keepclassmembers class org.libsdl.app.** { *; }
|
||||
|
||||
# Suppress warnings for R8
|
||||
-dontwarn org.bouncycastle.jsse.BCSSLParameters
|
||||
-dontwarn org.bouncycastle.jsse.BCSSLSocket
|
||||
|
||||
@@ -16,6 +16,7 @@ import android.widget.TextView
|
||||
import androidx.annotation.Keep
|
||||
import androidx.core.net.toUri
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import org.libsdl.app.SDL
|
||||
import java.lang.ref.WeakReference
|
||||
import org.yuzu.yuzu_emu.activities.EmulationActivity
|
||||
import org.yuzu.yuzu_emu.fragments.CoreErrorDialogFragment
|
||||
@@ -52,6 +53,8 @@ object NativeLibrary {
|
||||
init {
|
||||
try {
|
||||
System.loadLibrary("yuzu-android")
|
||||
SDL.setupJNI()
|
||||
initJvm()
|
||||
} catch (ex: UnsatisfiedLinkError) {
|
||||
error("[NativeLibrary] $ex")
|
||||
}
|
||||
@@ -367,6 +370,7 @@ object NativeLibrary {
|
||||
NetPlayManager.clearChat()
|
||||
}
|
||||
|
||||
external fun initJvm()
|
||||
external fun initMultiplayer()
|
||||
|
||||
@Keep
|
||||
|
||||
@@ -22,7 +22,7 @@ add_library(yuzu-android SHARED
|
||||
set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR})
|
||||
|
||||
target_link_libraries(yuzu-android PRIVATE audio_core common core input_common frontend_common video_core)
|
||||
target_link_libraries(yuzu-android PRIVATE android camera2ndk EGL glad jnigraphics log)
|
||||
target_link_libraries(yuzu-android PRIVATE android camera2ndk EGL glad jnigraphics log GLESv2)
|
||||
if (ARCHITECTURE_arm64)
|
||||
target_link_libraries(yuzu-android PRIVATE adrenotools)
|
||||
endif()
|
||||
|
||||
@@ -1730,6 +1730,15 @@ jint Java_org_yuzu_yuzu_1emu_NativeLibrary_loadAmiibo(JNIEnv* env, jobject jobj,
|
||||
return static_cast<jint>(info);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_org_yuzu_yuzu_1emu_NativeLibrary_initJvm(JNIEnv *env, jclass clazz) {
|
||||
JavaVM *vm;
|
||||
if (env->GetJavaVM(&vm) != JNI_OK)
|
||||
return;
|
||||
|
||||
Common::Android::Initialize(vm, env);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_org_yuzu_yuzu_1emu_NativeLibrary_initMultiplayer(
|
||||
JNIEnv* env, [[maybe_unused]] jobject obj) {
|
||||
|
||||
@@ -800,7 +800,6 @@
|
||||
<string name="theme_mode_dark">تاریک</string>
|
||||
|
||||
<!-- Audio output engines -->
|
||||
<string name="oboe">oboe</string>
|
||||
<string name="cubeb">cubeb</string>
|
||||
|
||||
<!-- Anisotropic filtering options -->
|
||||
|
||||
@@ -465,13 +465,13 @@
|
||||
|
||||
<string-array name="outputEngineEntries">
|
||||
<item>@string/auto</item>
|
||||
<item>@string/oboe</item>
|
||||
<item>@string/sdl3</item>
|
||||
<item>@string/cubeb</item>
|
||||
<item>@string/string_null</item>
|
||||
</string-array>
|
||||
<integer-array name="outputEngineValues">
|
||||
<item>0</item>
|
||||
<item>4</item>
|
||||
<item>2</item>
|
||||
<item>1</item>
|
||||
<item>3</item>
|
||||
</integer-array>
|
||||
|
||||
@@ -1240,7 +1240,7 @@
|
||||
<string name="theme_mode_dark">Dark</string>
|
||||
|
||||
<!-- Audio output engines -->
|
||||
<string name="oboe" translatable="false">oboe</string>
|
||||
<string name="sdl3" translatable="false">SDL3</string>
|
||||
<string name="cubeb" translatable="false">cubeb</string>
|
||||
|
||||
<!-- Anisotropic filtering options -->
|
||||
|
||||
@@ -238,26 +238,11 @@ if (ENABLE_CUBEB)
|
||||
target_compile_definitions(audio_core PRIVATE HAVE_CUBEB=1)
|
||||
endif()
|
||||
|
||||
if(ANDROID)
|
||||
target_sources(audio_core PRIVATE
|
||||
sink/oboe_sink.cpp
|
||||
sink/oboe_sink.h)
|
||||
target_sources(audio_core PRIVATE
|
||||
sink/sdl3_sink.cpp
|
||||
sink/sdl3_sink.h)
|
||||
|
||||
target_link_libraries(audio_core PRIVATE oboe)
|
||||
target_compile_definitions(audio_core PUBLIC HAVE_OBOE)
|
||||
else()
|
||||
target_sources(audio_core PRIVATE
|
||||
sink/sdl3_sink.cpp
|
||||
sink/sdl3_sink.h)
|
||||
|
||||
target_link_libraries(audio_core PRIVATE SDL3::SDL3)
|
||||
target_compile_definitions(audio_core PRIVATE HAVE_SDL3)
|
||||
endif()
|
||||
|
||||
if(OPENORBIS)
|
||||
target_sources(audio_core PRIVATE
|
||||
sink/ps4_sink.cpp
|
||||
sink/ps4_sink.h)
|
||||
endif()
|
||||
target_link_libraries(audio_core PRIVATE SDL3::SDL3)
|
||||
target_compile_definitions(audio_core PRIVATE HAVE_SDL3)
|
||||
|
||||
create_target_directory_groups(audio_core)
|
||||
|
||||
@@ -1,230 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
#include <oboe/Oboe.h>
|
||||
|
||||
#include "audio_core/common/common.h"
|
||||
#include "audio_core/sink/oboe_sink.h"
|
||||
#include "audio_core/sink/sink_stream.h"
|
||||
#include "common/logging.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/core.h"
|
||||
|
||||
namespace AudioCore::Sink {
|
||||
|
||||
class OboeSinkStream final : public SinkStream,
|
||||
public oboe::AudioStreamDataCallback,
|
||||
public oboe::AudioStreamErrorCallback {
|
||||
public:
|
||||
explicit OboeSinkStream(Core::System& system_, StreamType type_, const std::string& name_,
|
||||
u32 system_channels_)
|
||||
: SinkStream(system_, type_) {
|
||||
name = name_;
|
||||
system_channels = system_channels_;
|
||||
|
||||
this->OpenStream();
|
||||
}
|
||||
|
||||
~OboeSinkStream() override {
|
||||
LOG_INFO(Audio_Sink, "Destroyed Oboe stream");
|
||||
}
|
||||
|
||||
void Finalize() override {
|
||||
this->Stop();
|
||||
m_stream.reset();
|
||||
}
|
||||
|
||||
void Start(bool resume = false) override {
|
||||
if (!m_stream || !paused) {
|
||||
return;
|
||||
}
|
||||
|
||||
paused = false;
|
||||
|
||||
if (m_stream->start() != oboe::Result::OK) {
|
||||
LOG_CRITICAL(Audio_Sink, "Error starting Oboe stream");
|
||||
}
|
||||
}
|
||||
|
||||
void Stop() override {
|
||||
if (!m_stream || paused) {
|
||||
return;
|
||||
}
|
||||
|
||||
this->SignalPause();
|
||||
|
||||
if (m_stream->stop() != oboe::Result::OK) {
|
||||
LOG_CRITICAL(Audio_Sink, "Error stopping Oboe stream");
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
static s32 QueryChannelCount(oboe::Direction direction) {
|
||||
std::shared_ptr<oboe::AudioStream> temp_stream;
|
||||
oboe::AudioStreamBuilder builder;
|
||||
|
||||
const auto result = ConfigureBuilder(builder, direction)->openStream(temp_stream);
|
||||
if (result == oboe::Result::OK) {
|
||||
return temp_stream->getChannelCount() >= 6 ? 6 : 2;
|
||||
}
|
||||
|
||||
LOG_ERROR(Audio_Sink, "Failed to open {} stream. Using default channel count 2",
|
||||
direction == oboe::Direction::Output ? "output" : "input");
|
||||
return 2;
|
||||
}
|
||||
|
||||
protected:
|
||||
oboe::DataCallbackResult onAudioReady(oboe::AudioStream*, void* audio_data,
|
||||
s32 num_buffer_frames) override {
|
||||
const size_t num_channels = this->GetDeviceChannels();
|
||||
const size_t frame_size = num_channels;
|
||||
const size_t num_frames = static_cast<size_t>(num_buffer_frames);
|
||||
|
||||
if (type == StreamType::In) {
|
||||
std::span<const s16> input_buffer{reinterpret_cast<const s16*>(audio_data),
|
||||
num_frames * frame_size};
|
||||
this->ProcessAudioIn(input_buffer, num_frames);
|
||||
} else {
|
||||
std::span<s16> output_buffer{reinterpret_cast<s16*>(audio_data),
|
||||
num_frames * frame_size};
|
||||
this->ProcessAudioOutAndRender(output_buffer, num_frames);
|
||||
}
|
||||
|
||||
return oboe::DataCallbackResult::Continue;
|
||||
}
|
||||
|
||||
void onErrorAfterClose(oboe::AudioStream*, oboe::Result) override {
|
||||
LOG_INFO(Audio_Sink, "Audio stream closed, reinitializing");
|
||||
|
||||
if (this->OpenStream()) {
|
||||
m_stream->start();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static oboe::AudioStreamBuilder* ConfigureBuilder(oboe::AudioStreamBuilder& builder,
|
||||
oboe::Direction direction) {
|
||||
// TODO: investigate callback delay issues when using AAudio
|
||||
return builder.setPerformanceMode(oboe::PerformanceMode::LowLatency)
|
||||
->setAudioApi(oboe::AudioApi::OpenSLES)
|
||||
->setDirection(direction)
|
||||
->setSampleRate(TargetSampleRate)
|
||||
->setSampleRateConversionQuality(oboe::SampleRateConversionQuality::High)
|
||||
->setFormat(oboe::AudioFormat::I16)
|
||||
->setFormatConversionAllowed(true)
|
||||
->setUsage(oboe::Usage::Game)
|
||||
->setBufferCapacityInFrames(TargetSampleCount * 2);
|
||||
}
|
||||
|
||||
bool OpenStream() {
|
||||
const auto direction = [&]() {
|
||||
switch (type) {
|
||||
case StreamType::In:
|
||||
return oboe::Direction::Input;
|
||||
case StreamType::Out:
|
||||
case StreamType::Render:
|
||||
return oboe::Direction::Output;
|
||||
default:
|
||||
ASSERT(false);
|
||||
return oboe::Direction::Output;
|
||||
}
|
||||
}();
|
||||
|
||||
const auto expected_channels = QueryChannelCount(direction);
|
||||
const auto expected_mask = [&]() {
|
||||
switch (expected_channels) {
|
||||
case 1:
|
||||
return oboe::ChannelMask::Mono;
|
||||
case 2:
|
||||
return oboe::ChannelMask::Stereo;
|
||||
case 6:
|
||||
return oboe::ChannelMask::CM5Point1;
|
||||
default:
|
||||
ASSERT(false);
|
||||
return oboe::ChannelMask::Unspecified;
|
||||
}
|
||||
}();
|
||||
|
||||
oboe::AudioStreamBuilder builder;
|
||||
const auto result = ConfigureBuilder(builder, direction)
|
||||
->setChannelCount(expected_channels)
|
||||
->setChannelMask(expected_mask)
|
||||
->setChannelConversionAllowed(true)
|
||||
->setDataCallback(this)
|
||||
->setErrorCallback(this)
|
||||
->openStream(m_stream);
|
||||
ASSERT(result == oboe::Result::OK);
|
||||
return result == oboe::Result::OK && this->SetStreamProperties();
|
||||
}
|
||||
|
||||
bool SetStreamProperties() {
|
||||
ASSERT(m_stream);
|
||||
|
||||
m_stream->setBufferSizeInFrames(TargetSampleCount * 2);
|
||||
device_channels = m_stream->getChannelCount();
|
||||
|
||||
const auto sample_rate = m_stream->getSampleRate();
|
||||
const auto buffer_capacity = m_stream->getBufferCapacityInFrames();
|
||||
const auto stream_backend =
|
||||
m_stream->getAudioApi() == oboe::AudioApi::AAudio ? "AAudio" : "OpenSLES";
|
||||
|
||||
LOG_INFO(Audio_Sink, "Opened Oboe {} stream with {} channels sample rate {} capacity {}",
|
||||
stream_backend, device_channels, sample_rate, buffer_capacity);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::shared_ptr<oboe::AudioStream> m_stream{};
|
||||
};
|
||||
|
||||
OboeSink::OboeSink() {
|
||||
// TODO: This is not generally knowable
|
||||
// The channel count is distinct based on direction and can change
|
||||
device_channels = OboeSinkStream::QueryChannelCount(oboe::Direction::Output);
|
||||
}
|
||||
|
||||
OboeSink::~OboeSink() = default;
|
||||
|
||||
SinkStream* OboeSink::AcquireSinkStream(Core::System& system, u32 system_channels,
|
||||
const std::string& name, StreamType type) {
|
||||
SinkStreamPtr& stream = sink_streams.emplace_back(
|
||||
std::make_unique<OboeSinkStream>(system, type, name, system_channels));
|
||||
|
||||
return stream.get();
|
||||
}
|
||||
|
||||
void OboeSink::CloseStream(SinkStream* to_remove) {
|
||||
sink_streams.remove_if([&](auto& stream) { return stream.get() == to_remove; });
|
||||
}
|
||||
|
||||
void OboeSink::CloseStreams() {
|
||||
sink_streams.clear();
|
||||
}
|
||||
|
||||
f32 OboeSink::GetDeviceVolume() const {
|
||||
if (sink_streams.empty()) {
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
return sink_streams.front()->GetDeviceVolume();
|
||||
}
|
||||
|
||||
void OboeSink::SetDeviceVolume(f32 volume) {
|
||||
for (auto& stream : sink_streams) {
|
||||
stream->SetDeviceVolume(volume);
|
||||
}
|
||||
}
|
||||
|
||||
void OboeSink::SetSystemVolume(f32 volume) {
|
||||
for (auto& stream : sink_streams) {
|
||||
stream->SetSystemVolume(volume);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace AudioCore::Sink
|
||||
@@ -1,75 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <string>
|
||||
|
||||
#include "audio_core/sink/sink.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace AudioCore::Sink {
|
||||
class SinkStream;
|
||||
|
||||
class OboeSink final : public Sink {
|
||||
public:
|
||||
explicit OboeSink();
|
||||
~OboeSink() override;
|
||||
|
||||
/**
|
||||
* Create a new sink stream.
|
||||
*
|
||||
* @param system - Core system.
|
||||
* @param system_channels - Number of channels the audio system expects.
|
||||
* May differ from the device's channel count.
|
||||
* @param name - Name of this stream.
|
||||
* @param type - Type of this stream, render/in/out.
|
||||
*
|
||||
* @return A pointer to the created SinkStream
|
||||
*/
|
||||
SinkStream* AcquireSinkStream(Core::System& system, u32 system_channels,
|
||||
const std::string& name, StreamType type) override;
|
||||
|
||||
/**
|
||||
* Close a given stream.
|
||||
*
|
||||
* @param stream - The stream to close.
|
||||
*/
|
||||
void CloseStream(SinkStream* stream) override;
|
||||
|
||||
/**
|
||||
* Close all streams.
|
||||
*/
|
||||
void CloseStreams() override;
|
||||
|
||||
/**
|
||||
* Get the device volume. Set from calls to the IAudioDevice service.
|
||||
*
|
||||
* @return Volume of the device.
|
||||
*/
|
||||
f32 GetDeviceVolume() const override;
|
||||
|
||||
/**
|
||||
* Set the device volume. Set from calls to the IAudioDevice service.
|
||||
*
|
||||
* @param volume - New volume of the device.
|
||||
*/
|
||||
void SetDeviceVolume(f32 volume) override;
|
||||
|
||||
/**
|
||||
* Set the system volume. Comes from the audio system using this stream.
|
||||
*
|
||||
* @param volume - New volume of the system.
|
||||
*/
|
||||
void SetSystemVolume(f32 volume) override;
|
||||
|
||||
private:
|
||||
/// List of streams managed by this sink
|
||||
std::list<SinkStreamPtr> sink_streams{};
|
||||
};
|
||||
|
||||
} // namespace AudioCore::Sink
|
||||
@@ -1,160 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <span>
|
||||
#include <stop_token>
|
||||
#include <vector>
|
||||
|
||||
#include <orbis/AudioOut.h>
|
||||
|
||||
#include "audio_core/common/common.h"
|
||||
#include "audio_core/sink/ps4_sink.h"
|
||||
#include "audio_core/sink/sink_stream.h"
|
||||
#include "common/alignment.h"
|
||||
#include "common/logging.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/core.h"
|
||||
|
||||
namespace AudioCore::Sink {
|
||||
|
||||
/// @brief PS4 sink stream, responsible for sinking samples to hardware.
|
||||
struct PS4SinkStream final : public SinkStream {
|
||||
/// @brief Create a new sink stream.
|
||||
/// @param device_channels_ - Number of channels supported by the hardware.
|
||||
/// @param system_channels_ - Number of channels the audio systems expect.
|
||||
/// @param output_device - Name of the output device to use for this stream.
|
||||
/// @param input_device - Name of the input device to use for this stream.
|
||||
/// @param type_ - Type of this stream.
|
||||
/// @param system_ - Core system.
|
||||
/// @param event - Event used only for audio renderer, signalled on buffer consume.
|
||||
PS4SinkStream(u32 device_channels_, u32 system_channels_, const std::string& output_device, const std::string& input_device, StreamType type_, Core::System& system_)
|
||||
: SinkStream{system_, type_}
|
||||
{
|
||||
system_channels = system_channels_;
|
||||
device_channels = device_channels_;
|
||||
|
||||
constexpr size_t PREFERRED_SAMPLE_COUNT = 1024;
|
||||
auto const param = (device_channels > 1 ? ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_STEREO : ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_MONO);
|
||||
audio_dev = sceAudioOutOpen(ORBIS_USER_SERVICE_USER_ID_SYSTEM, ORBIS_AUDIO_OUT_PORT_TYPE_MAIN, 0, PREFERRED_SAMPLE_COUNT, TargetSampleRate, param);
|
||||
if (audio_dev > 0) {
|
||||
audio_thread = std::jthread([=, this](std::stop_token stop_token) {
|
||||
std::array<s16, PREFERRED_SAMPLE_COUNT * MaxChannels> buffer;
|
||||
while (!stop_token.stop_requested()) {
|
||||
if (this->type == StreamType::In) {
|
||||
this->ProcessAudioIn(buffer, PREFERRED_SAMPLE_COUNT);
|
||||
(void)buffer; // TODO: microphone support
|
||||
} else {
|
||||
int err = 0;
|
||||
std::fill(buffer.begin(), buffer.end(), 0);
|
||||
this->ProcessAudioOutAndRender(buffer, PREFERRED_SAMPLE_COUNT);
|
||||
sceAudioOutOutput(audio_dev, nullptr);
|
||||
if ((err = sceAudioOutOutput(audio_dev, buffer.data())) < 0)
|
||||
LOG_ERROR(Service_Audio, "{}", err);
|
||||
}
|
||||
// Wait for pause, we don't really have a native pause
|
||||
// so this is the best i can do
|
||||
while (paused && !stop_token.stop_requested())
|
||||
asm volatile ("pause"); //dont shootup cpu usage to 100%
|
||||
}
|
||||
sceAudioOutClose(audio_dev);
|
||||
});
|
||||
} else {
|
||||
LOG_ERROR(Service_Audio, "Failed to create audio device! {:#x}", uint32_t(audio_dev));
|
||||
}
|
||||
}
|
||||
|
||||
~PS4SinkStream() override {
|
||||
LOG_DEBUG(Service_Audio, "Destroying PS4 stream {}", name);
|
||||
if (audio_thread.joinable()) {
|
||||
audio_thread.request_stop();
|
||||
audio_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
void Finalize() override {
|
||||
if (audio_dev > 0) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void Start(bool resume = false) override {
|
||||
if (audio_dev > 0 && paused) {
|
||||
paused = false;
|
||||
}
|
||||
}
|
||||
|
||||
void Stop() override {
|
||||
if (audio_dev > 0 && !paused) {
|
||||
SignalPause();
|
||||
}
|
||||
}
|
||||
|
||||
std::jthread audio_thread;
|
||||
int32_t audio_dev{};
|
||||
};
|
||||
|
||||
PS4Sink::PS4Sink(std::string_view target_device_name) {
|
||||
int32_t rc = sceAudioOutInit();
|
||||
if (rc == 0 || unsigned(rc) == ORBIS_AUDIO_OUT_ERROR_ALREADY_INIT) {
|
||||
if (target_device_name != auto_device_name && !target_device_name.empty()) {
|
||||
output_device = target_device_name;
|
||||
} else {
|
||||
output_device.clear();
|
||||
}
|
||||
device_channels = 2;
|
||||
} else {
|
||||
LOG_ERROR(Service_Audio, "Unable to open audio out! {:#x}", uint32_t(rc));
|
||||
}
|
||||
}
|
||||
|
||||
PS4Sink::~PS4Sink() = default;
|
||||
|
||||
/// @brief Create a new sink stream.
|
||||
/// @param system - Core system.
|
||||
/// @param system_channels - Number of channels the audio system expects. May differ from the device's channel count.
|
||||
/// @param name - Name of this stream.
|
||||
/// @param type - Type of this stream, render/in/out.
|
||||
/// @return A pointer to the created SinkStream
|
||||
SinkStream* PS4Sink::AcquireSinkStream(Core::System& system, u32 system_channels_, const std::string&, StreamType type) {
|
||||
system_channels = system_channels_;
|
||||
SinkStreamPtr& stream = sink_streams.emplace_back(std::make_unique<PS4SinkStream>(device_channels, system_channels, output_device, input_device, type, system));
|
||||
return stream.get();
|
||||
}
|
||||
|
||||
void PS4Sink::CloseStream(SinkStream* stream) {
|
||||
for (size_t i = 0; i < sink_streams.size(); i++) {
|
||||
if (sink_streams[i].get() == stream) {
|
||||
sink_streams[i].reset();
|
||||
sink_streams.erase(sink_streams.begin() + i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PS4Sink::CloseStreams() {
|
||||
sink_streams.clear();
|
||||
}
|
||||
|
||||
f32 PS4Sink::GetDeviceVolume() const {
|
||||
return sink_streams.size() > 0 ? sink_streams[0]->GetDeviceVolume() : 1.f;
|
||||
}
|
||||
|
||||
void PS4Sink::SetDeviceVolume(f32 volume) {
|
||||
for (auto& stream : sink_streams)
|
||||
stream->SetDeviceVolume(volume);
|
||||
}
|
||||
|
||||
void PS4Sink::SetSystemVolume(f32 volume) {
|
||||
for (auto& stream : sink_streams)
|
||||
stream->SetSystemVolume(volume);
|
||||
}
|
||||
|
||||
std::vector<std::string> ListPS4SinkDevices(bool capture) {
|
||||
return {{"Default"}};
|
||||
}
|
||||
|
||||
u32 GetPS4Latency() {
|
||||
return TargetSampleCount;
|
||||
}
|
||||
|
||||
} // namespace AudioCore::Sink
|
||||
@@ -1,43 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "audio_core/sink/sink.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace AudioCore::Sink {
|
||||
class SinkStream;
|
||||
|
||||
/// @brief PS4 backend sink, holds multiple output streams and is responsible for sinking samples to
|
||||
/// hardware. Used by Audio Render, Audio In and Audio Out.
|
||||
struct PS4Sink final : public Sink {
|
||||
explicit PS4Sink(std::string_view device_id);
|
||||
~PS4Sink() override;
|
||||
SinkStream* AcquireSinkStream(Core::System& system, u32 system_channels, const std::string& name, StreamType type) override;
|
||||
void CloseStream(SinkStream* stream) override;
|
||||
void CloseStreams() override;
|
||||
f32 GetDeviceVolume() const override;
|
||||
void SetDeviceVolume(f32 volume) override;
|
||||
void SetSystemVolume(f32 volume) override;
|
||||
/// Name of the output device used by streams
|
||||
std::string output_device;
|
||||
/// Name of the input device used by streams
|
||||
std::string input_device;
|
||||
/// Vector of streams managed by this sink
|
||||
std::vector<SinkStreamPtr> sink_streams;
|
||||
};
|
||||
|
||||
std::vector<std::string> ListPS4SinkDevices(bool capture);
|
||||
u32 GetPS4Latency();
|
||||
|
||||
} // namespace AudioCore::Sink
|
||||
@@ -10,18 +10,12 @@
|
||||
#include <vector>
|
||||
|
||||
#include "audio_core/sink/sink_details.h"
|
||||
#ifdef HAVE_OBOE
|
||||
#include "audio_core/sink/oboe_sink.h"
|
||||
#endif
|
||||
#ifdef HAVE_CUBEB
|
||||
#include "audio_core/sink/cubeb_sink.h"
|
||||
#endif
|
||||
#ifdef HAVE_SDL3
|
||||
#include "audio_core/sink/sdl3_sink.h"
|
||||
#endif
|
||||
#ifdef __OPENORBIS__
|
||||
#include "audio_core/sink/ps4_sink.h"
|
||||
#endif
|
||||
#include "audio_core/sink/null_sink.h"
|
||||
#include "common/logging.h"
|
||||
#include "common/settings_enums.h"
|
||||
@@ -46,34 +40,8 @@ struct SinkDetails {
|
||||
/// SuitableFn is_suitable; // REVERTED FOR LatencyFn latency ABOVE - DIABLO 3 FIX
|
||||
};
|
||||
|
||||
// NOTE TO PROBABLY FIX LATER FOR ANDROID - the return value "0u" for the first HAVE_OBOE
|
||||
// section below was just copied from the null section so there's a somewhat valid value
|
||||
// being returned, since the previous "true" value probably isn't compatible with the
|
||||
// previous EA-3833 code. (HAVE_OBOE was introduced in a later release.) Eventually need
|
||||
// to change "0u" for something else directly from the oboe_sink.cpp functions.
|
||||
|
||||
// sink_details is ordered in terms of desirability, with the best choice at the top.
|
||||
constexpr SinkDetails sink_details[] = {
|
||||
#ifdef __OPENORBIS__
|
||||
SinkDetails{
|
||||
Settings::AudioEngine::Ps4,
|
||||
[](std::string_view device_id) -> std::unique_ptr<Sink> {
|
||||
return std::make_unique<PS4Sink>(device_id);
|
||||
},
|
||||
&ListPS4SinkDevices,
|
||||
&GetPS4Latency,
|
||||
},
|
||||
#endif
|
||||
#ifdef HAVE_OBOE
|
||||
SinkDetails{
|
||||
Settings::AudioEngine::Oboe,
|
||||
[](std::string_view device_id) -> std::unique_ptr<Sink> {
|
||||
return std::make_unique<OboeSink>();
|
||||
},
|
||||
[](bool capture) { return std::vector<std::string>{"Default"}; },
|
||||
[]() { return 0u; },
|
||||
},
|
||||
#endif
|
||||
#ifdef HAVE_CUBEB
|
||||
SinkDetails{
|
||||
Settings::AudioEngine::Cubeb,
|
||||
@@ -128,9 +96,7 @@ const SinkDetails& GetOutputSinkDetails(Settings::AudioEngine sink_id) {
|
||||
// BEGIN REINTRODUCED FROM 3833 - REPLACED CODE BLOCK ABOVE - DIABLO 3 FIX
|
||||
// Auto-select a backend. Prefer CubeB, but it may report a large minimum latency which
|
||||
// causes audio issues, in that case go with SDL.
|
||||
#if defined(__OPENORBIS__)
|
||||
iter = find_backend(Settings::AudioEngine::Ps4);
|
||||
#elif defined(HAVE_CUBEB) && defined(HAVE_SDL3)
|
||||
#if defined(HAVE_CUBEB) && defined(HAVE_SDL3)
|
||||
iter = find_backend(Settings::AudioEngine::Cubeb);
|
||||
if (iter->latency() > TargetSampleCount * 3) {
|
||||
iter = find_backend(Settings::AudioEngine::Sdl3);
|
||||
|
||||
@@ -230,7 +230,6 @@ if(CXX_CLANG)
|
||||
$<$<VERSION_LESS:$<CXX_COMPILER_VERSION>,15>:CANNOT_EXPLICITLY_INSTANTIATE>)
|
||||
endif()
|
||||
|
||||
# Has to be BEFORE httplib
|
||||
if (BOOST_NO_HEADERS)
|
||||
target_link_libraries(common PUBLIC Boost::algorithm Boost::heap Boost::icl Boost::pool)
|
||||
else()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "android_common.h"
|
||||
@@ -51,7 +51,7 @@ jstring ToJString(JNIEnv* env, std::u16string_view str) {
|
||||
}
|
||||
|
||||
double GetJDouble(JNIEnv* env, jobject jdouble) {
|
||||
return env->GetDoubleField(jdouble, GetDoubleValueField());
|
||||
return env->CallDoubleMethod(jdouble, GetDoubleValueMethod());
|
||||
}
|
||||
|
||||
jobject ToJDouble(JNIEnv* env, double value) {
|
||||
@@ -59,7 +59,7 @@ jobject ToJDouble(JNIEnv* env, double value) {
|
||||
}
|
||||
|
||||
s32 GetJInteger(JNIEnv* env, jobject jinteger) {
|
||||
return env->GetIntField(jinteger, GetIntegerValueField());
|
||||
return env->CallIntMethod(jinteger, GetIntegerValueMethod());
|
||||
}
|
||||
|
||||
jobject ToJInteger(JNIEnv* env, s32 value) {
|
||||
@@ -67,7 +67,7 @@ jobject ToJInteger(JNIEnv* env, s32 value) {
|
||||
}
|
||||
|
||||
bool GetJBoolean(JNIEnv* env, jobject jboolean) {
|
||||
return env->GetBooleanField(jboolean, GetBooleanValueField());
|
||||
return env->CallBooleanMethod(jboolean, GetBooleanValueMethod());
|
||||
}
|
||||
|
||||
jobject ToJBoolean(JNIEnv* env, bool value) {
|
||||
|
||||
+188
-192
@@ -63,15 +63,15 @@ static jfieldID s_patch_title_id_field;
|
||||
|
||||
static jclass s_double_class;
|
||||
static jmethodID s_double_constructor;
|
||||
static jfieldID s_double_value_field;
|
||||
static jmethodID s_double_value_method;
|
||||
|
||||
static jclass s_integer_class;
|
||||
static jmethodID s_integer_constructor;
|
||||
static jfieldID s_integer_value_field;
|
||||
static jmethodID s_integer_value_method;
|
||||
|
||||
static jclass s_boolean_class;
|
||||
static jmethodID s_boolean_constructor;
|
||||
static jfieldID s_boolean_value_field;
|
||||
static jmethodID s_boolean_value_method;
|
||||
|
||||
static jclass s_player_input_class;
|
||||
static jmethodID s_player_input_constructor;
|
||||
@@ -103,7 +103,6 @@ static jmethodID s_clear_chat;
|
||||
static constexpr jint JNI_VERSION = JNI_VERSION_1_6;
|
||||
|
||||
namespace Common::Android {
|
||||
|
||||
JNIEnv *GetEnvForThread() {
|
||||
thread_local static struct OwnedEnv {
|
||||
OwnedEnv() {
|
||||
@@ -299,8 +298,8 @@ namespace Common::Android {
|
||||
return s_double_constructor;
|
||||
}
|
||||
|
||||
jfieldID GetDoubleValueField() {
|
||||
return s_double_value_field;
|
||||
jmethodID GetDoubleValueMethod() {
|
||||
return s_double_value_method;
|
||||
}
|
||||
|
||||
jclass GetIntegerClass() {
|
||||
@@ -311,8 +310,8 @@ namespace Common::Android {
|
||||
return s_integer_constructor;
|
||||
}
|
||||
|
||||
jfieldID GetIntegerValueField() {
|
||||
return s_integer_value_field;
|
||||
jmethodID GetIntegerValueMethod() {
|
||||
return s_integer_value_method;
|
||||
}
|
||||
|
||||
jclass GetBooleanClass() {
|
||||
@@ -323,8 +322,8 @@ namespace Common::Android {
|
||||
return s_boolean_constructor;
|
||||
}
|
||||
|
||||
jfieldID GetBooleanValueField() {
|
||||
return s_boolean_value_field;
|
||||
jmethodID GetBooleanValueMethod() {
|
||||
return s_boolean_value_method;
|
||||
}
|
||||
|
||||
jclass GetPlayerInputClass() {
|
||||
@@ -429,188 +428,6 @@ namespace Common::Android {
|
||||
|
||||
jint InitFFmpegOnLoad(JavaVM *vm);
|
||||
|
||||
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
|
||||
s_java_vm = vm;
|
||||
InitFFmpegOnLoad(vm);
|
||||
|
||||
JNIEnv *env;
|
||||
if (vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION) != JNI_OK)
|
||||
return JNI_ERR;
|
||||
|
||||
// Initialize Java classes
|
||||
const jclass native_library_class = env->FindClass("org/yuzu/yuzu_emu/NativeLibrary");
|
||||
s_native_library_class = reinterpret_cast<jclass>(env->NewGlobalRef(native_library_class));
|
||||
s_disk_cache_progress_class = reinterpret_cast<jclass>(env->NewGlobalRef(
|
||||
env->FindClass("org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress")));
|
||||
s_load_callback_stage_class = reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass(
|
||||
"org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress$LoadCallbackStage")));
|
||||
|
||||
const jclass game_dir_class = env->FindClass("org/yuzu/yuzu_emu/model/GameDir");
|
||||
s_game_dir_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_dir_class));
|
||||
s_game_dir_constructor = env->GetMethodID(game_dir_class, "<init>",
|
||||
"(Ljava/lang/String;Z)V");
|
||||
env->DeleteLocalRef(game_dir_class);
|
||||
|
||||
// Initialize methods
|
||||
s_exit_emulation_activity =
|
||||
env->GetStaticMethodID(s_native_library_class, "exitEmulationActivity", "(I)V");
|
||||
s_disk_cache_load_progress =
|
||||
env->GetStaticMethodID(s_disk_cache_progress_class, "loadProgress", "(III)V");
|
||||
s_copy_to_storage = env->GetStaticMethodID(s_native_library_class, "copyFileToStorage",
|
||||
"(Ljava/lang/String;Ljava/lang/String;)Z");
|
||||
s_file_exists = env->GetStaticMethodID(s_native_library_class, "exists",
|
||||
"(Ljava/lang/String;)Z");
|
||||
s_file_extension = env->GetStaticMethodID(s_native_library_class, "getFileExtension",
|
||||
"(Ljava/lang/String;)Ljava/lang/String;");
|
||||
s_on_emulation_started =
|
||||
env->GetStaticMethodID(s_native_library_class, "onEmulationStarted", "()V");
|
||||
s_on_emulation_stopped =
|
||||
env->GetStaticMethodID(s_native_library_class, "onEmulationStopped", "(I)V");
|
||||
s_on_program_changed =
|
||||
env->GetStaticMethodID(s_native_library_class, "onProgramChanged", "(I)V");
|
||||
|
||||
const jclass game_class = env->FindClass("org/yuzu/yuzu_emu/model/Game");
|
||||
s_game_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_class));
|
||||
s_game_constructor = env->GetMethodID(game_class, "<init>",
|
||||
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/"
|
||||
"String;Ljava/lang/String;Ljava/lang/String;Z)V");
|
||||
s_game_title_field = env->GetFieldID(game_class, "title", "Ljava/lang/String;");
|
||||
s_game_path_field = env->GetFieldID(game_class, "path", "Ljava/lang/String;");
|
||||
s_game_program_id_field = env->GetFieldID(game_class, "programId", "Ljava/lang/String;");
|
||||
s_game_developer_field = env->GetFieldID(game_class, "developer", "Ljava/lang/String;");
|
||||
s_game_version_field = env->GetFieldID(game_class, "version", "Ljava/lang/String;");
|
||||
s_game_is_homebrew_field = env->GetFieldID(game_class, "isHomebrew", "Z");
|
||||
env->DeleteLocalRef(game_class);
|
||||
|
||||
const jclass string_class = env->FindClass("java/lang/String");
|
||||
s_string_class = reinterpret_cast<jclass>(env->NewGlobalRef(string_class));
|
||||
env->DeleteLocalRef(string_class);
|
||||
|
||||
const jclass pair_class = env->FindClass("kotlin/Pair");
|
||||
s_pair_class = reinterpret_cast<jclass>(env->NewGlobalRef(pair_class));
|
||||
s_pair_constructor =
|
||||
env->GetMethodID(pair_class, "<init>", "(Ljava/lang/Object;Ljava/lang/Object;)V");
|
||||
s_pair_first_field = env->GetFieldID(pair_class, "first", "Ljava/lang/Object;");
|
||||
s_pair_second_field = env->GetFieldID(pair_class, "second", "Ljava/lang/Object;");
|
||||
env->DeleteLocalRef(pair_class);
|
||||
|
||||
const jclass overlay_control_data_class =
|
||||
env->FindClass("org/yuzu/yuzu_emu/overlay/model/OverlayControlData");
|
||||
s_overlay_control_data_class =
|
||||
reinterpret_cast<jclass>(env->NewGlobalRef(overlay_control_data_class));
|
||||
s_overlay_control_data_constructor =
|
||||
env->GetMethodID(overlay_control_data_class, "<init>",
|
||||
"(Ljava/lang/String;ZLkotlin/Pair;Lkotlin/Pair;Lkotlin/Pair;F)V");
|
||||
s_overlay_control_data_id_field =
|
||||
env->GetFieldID(overlay_control_data_class, "id", "Ljava/lang/String;");
|
||||
s_overlay_control_data_enabled_field =
|
||||
env->GetFieldID(overlay_control_data_class, "enabled", "Z");
|
||||
s_overlay_control_data_landscape_position_field =
|
||||
env->GetFieldID(overlay_control_data_class, "landscapePosition", "Lkotlin/Pair;");
|
||||
s_overlay_control_data_portrait_position_field =
|
||||
env->GetFieldID(overlay_control_data_class, "portraitPosition", "Lkotlin/Pair;");
|
||||
s_overlay_control_data_foldable_position_field =
|
||||
env->GetFieldID(overlay_control_data_class, "foldablePosition", "Lkotlin/Pair;");
|
||||
s_overlay_control_data_individual_scale_field =
|
||||
env->GetFieldID(overlay_control_data_class, "individualScale", "F");
|
||||
env->DeleteLocalRef(overlay_control_data_class);
|
||||
|
||||
const jclass patch_class = env->FindClass("org/yuzu/yuzu_emu/model/Patch");
|
||||
s_patch_class = reinterpret_cast<jclass>(env->NewGlobalRef(patch_class));
|
||||
s_patch_constructor = env->GetMethodID(
|
||||
patch_class, "<init>",
|
||||
"(ZLjava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;JI)V");
|
||||
s_patch_enabled_field = env->GetFieldID(patch_class, "enabled", "Z");
|
||||
s_patch_name_field = env->GetFieldID(patch_class, "name", "Ljava/lang/String;");
|
||||
s_patch_version_field = env->GetFieldID(patch_class, "version", "Ljava/lang/String;");
|
||||
s_patch_type_field = env->GetFieldID(patch_class, "type", "I");
|
||||
s_patch_program_id_field = env->GetFieldID(patch_class, "programId", "Ljava/lang/String;");
|
||||
s_patch_title_id_field = env->GetFieldID(patch_class, "titleId", "Ljava/lang/String;");
|
||||
env->DeleteLocalRef(patch_class);
|
||||
|
||||
const jclass double_class = env->FindClass("java/lang/Double");
|
||||
s_double_class = reinterpret_cast<jclass>(env->NewGlobalRef(double_class));
|
||||
s_double_constructor = env->GetMethodID(double_class, "<init>", "(D)V");
|
||||
s_double_value_field = env->GetFieldID(double_class, "value", "D");
|
||||
env->DeleteLocalRef(double_class);
|
||||
|
||||
const jclass int_class = env->FindClass("java/lang/Integer");
|
||||
s_integer_class = reinterpret_cast<jclass>(env->NewGlobalRef(int_class));
|
||||
s_integer_constructor = env->GetMethodID(int_class, "<init>", "(I)V");
|
||||
s_integer_value_field = env->GetFieldID(int_class, "value", "I");
|
||||
env->DeleteLocalRef(int_class);
|
||||
|
||||
const jclass boolean_class = env->FindClass("java/lang/Boolean");
|
||||
s_boolean_class = reinterpret_cast<jclass>(env->NewGlobalRef(boolean_class));
|
||||
s_boolean_constructor = env->GetMethodID(boolean_class, "<init>", "(Z)V");
|
||||
s_boolean_value_field = env->GetFieldID(boolean_class, "value", "Z");
|
||||
env->DeleteLocalRef(boolean_class);
|
||||
|
||||
const jclass player_input_class =
|
||||
env->FindClass("org/yuzu/yuzu_emu/features/input/model/PlayerInput");
|
||||
s_player_input_class = reinterpret_cast<jclass>(env->NewGlobalRef(player_input_class));
|
||||
s_player_input_constructor = env->GetMethodID(
|
||||
player_input_class, "<init>",
|
||||
"(Z[Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;ZIJJJJLjava/lang/String;Z)V");
|
||||
s_player_input_connected_field = env->GetFieldID(player_input_class, "connected", "Z");
|
||||
s_player_input_buttons_field =
|
||||
env->GetFieldID(player_input_class, "buttons", "[Ljava/lang/String;");
|
||||
s_player_input_analogs_field =
|
||||
env->GetFieldID(player_input_class, "analogs", "[Ljava/lang/String;");
|
||||
s_player_input_motions_field =
|
||||
env->GetFieldID(player_input_class, "motions", "[Ljava/lang/String;");
|
||||
s_player_input_vibration_enabled_field =
|
||||
env->GetFieldID(player_input_class, "vibrationEnabled", "Z");
|
||||
s_player_input_vibration_strength_field =
|
||||
env->GetFieldID(player_input_class, "vibrationStrength", "I");
|
||||
s_player_input_body_color_left_field =
|
||||
env->GetFieldID(player_input_class, "bodyColorLeft", "J");
|
||||
s_player_input_body_color_right_field =
|
||||
env->GetFieldID(player_input_class, "bodyColorRight", "J");
|
||||
s_player_input_button_color_left_field =
|
||||
env->GetFieldID(player_input_class, "buttonColorLeft", "J");
|
||||
s_player_input_button_color_right_field =
|
||||
env->GetFieldID(player_input_class, "buttonColorRight", "J");
|
||||
s_player_input_profile_name_field =
|
||||
env->GetFieldID(player_input_class, "profileName", "Ljava/lang/String;");
|
||||
s_player_input_use_system_vibrator_field =
|
||||
env->GetFieldID(player_input_class, "useSystemVibrator", "Z");
|
||||
env->DeleteLocalRef(player_input_class);
|
||||
|
||||
const jclass yuzu_input_device_interface =
|
||||
env->FindClass("org/yuzu/yuzu_emu/features/input/YuzuInputDevice");
|
||||
s_yuzu_input_device_interface =
|
||||
reinterpret_cast<jclass>(env->NewGlobalRef(yuzu_input_device_interface));
|
||||
s_yuzu_input_device_get_name =
|
||||
env->GetMethodID(yuzu_input_device_interface, "getName", "()Ljava/lang/String;");
|
||||
s_yuzu_input_device_get_guid =
|
||||
env->GetMethodID(yuzu_input_device_interface, "getGUID", "()Ljava/lang/String;");
|
||||
s_yuzu_input_device_get_port = env->GetMethodID(yuzu_input_device_interface, "getPort",
|
||||
"()I");
|
||||
s_yuzu_input_device_get_supports_vibration =
|
||||
env->GetMethodID(yuzu_input_device_interface, "getSupportsVibration", "()Z");
|
||||
s_yuzu_input_device_vibrate = env->GetMethodID(yuzu_input_device_interface, "vibrate",
|
||||
"(F)V");
|
||||
s_yuzu_input_device_get_axes =
|
||||
env->GetMethodID(yuzu_input_device_interface, "getAxes", "()[Ljava/lang/Integer;");
|
||||
s_yuzu_input_device_has_keys =
|
||||
env->GetMethodID(yuzu_input_device_interface, "hasKeys", "([I)[Z");
|
||||
env->DeleteLocalRef(yuzu_input_device_interface);
|
||||
s_add_netplay_message = env->GetStaticMethodID(s_native_library_class, "addNetPlayMessage",
|
||||
"(ILjava/lang/String;)V");
|
||||
s_clear_chat = env->GetStaticMethodID(s_native_library_class, "clearChat", "()V");
|
||||
|
||||
|
||||
// Initialize Android Storage
|
||||
Common::FS::Android::RegisterCallbacks(env, s_native_library_class);
|
||||
|
||||
// Initialize applets
|
||||
Common::Android::SoftwareKeyboard::InitJNI(env);
|
||||
Common::Android::WebBrowser::InitJNI(env);
|
||||
|
||||
return JNI_VERSION;
|
||||
}
|
||||
|
||||
void JNI_OnUnload(JavaVM *vm, void *reserved) {
|
||||
JNIEnv *env;
|
||||
if (vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION) != JNI_OK) {
|
||||
@@ -645,4 +462,183 @@ namespace Common::Android {
|
||||
}
|
||||
#endif
|
||||
|
||||
void Initialize(JavaVM* vm, JNIEnv *env) {
|
||||
s_java_vm = vm;
|
||||
InitFFmpegOnLoad(vm);
|
||||
|
||||
if (env->ExceptionCheck()) {
|
||||
env->ExceptionClear();
|
||||
}
|
||||
|
||||
// Initialize Java classes
|
||||
const jclass native_library_class = env->FindClass("org/yuzu/yuzu_emu/NativeLibrary");
|
||||
s_native_library_class = reinterpret_cast<jclass>(env->NewGlobalRef(native_library_class));
|
||||
s_disk_cache_progress_class = reinterpret_cast<jclass>(env->NewGlobalRef(
|
||||
env->FindClass("org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress")));
|
||||
s_load_callback_stage_class = reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass(
|
||||
"org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress$LoadCallbackStage")));
|
||||
|
||||
const jclass game_dir_class = env->FindClass("org/yuzu/yuzu_emu/model/GameDir");
|
||||
s_game_dir_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_dir_class));
|
||||
s_game_dir_constructor = env->GetMethodID(game_dir_class, "<init>",
|
||||
"(Ljava/lang/String;Z)V");
|
||||
env->DeleteLocalRef(game_dir_class);
|
||||
|
||||
// Initialize methods
|
||||
s_exit_emulation_activity =
|
||||
env->GetStaticMethodID(s_native_library_class, "exitEmulationActivity", "(I)V");
|
||||
s_disk_cache_load_progress =
|
||||
env->GetStaticMethodID(s_disk_cache_progress_class, "loadProgress", "(III)V");
|
||||
s_copy_to_storage = env->GetStaticMethodID(s_native_library_class, "copyFileToStorage",
|
||||
"(Ljava/lang/String;Ljava/lang/String;)Z");
|
||||
s_file_exists = env->GetStaticMethodID(s_native_library_class, "exists",
|
||||
"(Ljava/lang/String;)Z");
|
||||
s_file_extension = env->GetStaticMethodID(s_native_library_class, "getFileExtension",
|
||||
"(Ljava/lang/String;)Ljava/lang/String;");
|
||||
s_on_emulation_started =
|
||||
env->GetStaticMethodID(s_native_library_class, "onEmulationStarted", "()V");
|
||||
s_on_emulation_stopped =
|
||||
env->GetStaticMethodID(s_native_library_class, "onEmulationStopped", "(I)V");
|
||||
s_on_program_changed =
|
||||
env->GetStaticMethodID(s_native_library_class, "onProgramChanged", "(I)V");
|
||||
|
||||
const jclass game_class = env->FindClass("org/yuzu/yuzu_emu/model/Game");
|
||||
s_game_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_class));
|
||||
s_game_constructor = env->GetMethodID(game_class, "<init>",
|
||||
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/"
|
||||
"String;Ljava/lang/String;Ljava/lang/String;Z)V");
|
||||
s_game_title_field = env->GetFieldID(game_class, "title", "Ljava/lang/String;");
|
||||
s_game_path_field = env->GetFieldID(game_class, "path", "Ljava/lang/String;");
|
||||
s_game_program_id_field = env->GetFieldID(game_class, "programId", "Ljava/lang/String;");
|
||||
s_game_developer_field = env->GetFieldID(game_class, "developer", "Ljava/lang/String;");
|
||||
s_game_version_field = env->GetFieldID(game_class, "version", "Ljava/lang/String;");
|
||||
s_game_is_homebrew_field = env->GetFieldID(game_class, "isHomebrew", "Z");
|
||||
env->DeleteLocalRef(game_class);
|
||||
|
||||
const jclass string_class = env->FindClass("java/lang/String");
|
||||
s_string_class = reinterpret_cast<jclass>(env->NewGlobalRef(string_class));
|
||||
env->DeleteLocalRef(string_class);
|
||||
|
||||
const jclass pair_class = env->FindClass("kotlin/Pair");
|
||||
s_pair_class = reinterpret_cast<jclass>(env->NewGlobalRef(pair_class));
|
||||
s_pair_constructor =
|
||||
env->GetMethodID(pair_class, "<init>", "(Ljava/lang/Object;Ljava/lang/Object;)V");
|
||||
s_pair_first_field = env->GetFieldID(pair_class, "first", "Ljava/lang/Object;");
|
||||
s_pair_second_field = env->GetFieldID(pair_class, "second", "Ljava/lang/Object;");
|
||||
env->DeleteLocalRef(pair_class);
|
||||
|
||||
const jclass overlay_control_data_class =
|
||||
env->FindClass("org/yuzu/yuzu_emu/overlay/model/OverlayControlData");
|
||||
s_overlay_control_data_class =
|
||||
reinterpret_cast<jclass>(env->NewGlobalRef(overlay_control_data_class));
|
||||
s_overlay_control_data_constructor =
|
||||
env->GetMethodID(overlay_control_data_class, "<init>",
|
||||
"(Ljava/lang/String;ZLkotlin/Pair;Lkotlin/Pair;Lkotlin/Pair;F)V");
|
||||
s_overlay_control_data_id_field =
|
||||
env->GetFieldID(overlay_control_data_class, "id", "Ljava/lang/String;");
|
||||
s_overlay_control_data_enabled_field =
|
||||
env->GetFieldID(overlay_control_data_class, "enabled", "Z");
|
||||
s_overlay_control_data_landscape_position_field =
|
||||
env->GetFieldID(overlay_control_data_class, "landscapePosition", "Lkotlin/Pair;");
|
||||
s_overlay_control_data_portrait_position_field =
|
||||
env->GetFieldID(overlay_control_data_class, "portraitPosition", "Lkotlin/Pair;");
|
||||
s_overlay_control_data_foldable_position_field =
|
||||
env->GetFieldID(overlay_control_data_class, "foldablePosition", "Lkotlin/Pair;");
|
||||
s_overlay_control_data_individual_scale_field =
|
||||
env->GetFieldID(overlay_control_data_class, "individualScale", "F");
|
||||
env->DeleteLocalRef(overlay_control_data_class);
|
||||
|
||||
const jclass patch_class = env->FindClass("org/yuzu/yuzu_emu/model/Patch");
|
||||
s_patch_class = reinterpret_cast<jclass>(env->NewGlobalRef(patch_class));
|
||||
s_patch_constructor = env->GetMethodID(
|
||||
patch_class, "<init>",
|
||||
"(ZLjava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;JI)V");
|
||||
s_patch_enabled_field = env->GetFieldID(patch_class, "enabled", "Z");
|
||||
s_patch_name_field = env->GetFieldID(patch_class, "name", "Ljava/lang/String;");
|
||||
s_patch_version_field = env->GetFieldID(patch_class, "version", "Ljava/lang/String;");
|
||||
s_patch_type_field = env->GetFieldID(patch_class, "type", "I");
|
||||
s_patch_program_id_field = env->GetFieldID(patch_class, "programId", "Ljava/lang/String;");
|
||||
s_patch_title_id_field = env->GetFieldID(patch_class, "titleId", "Ljava/lang/String;");
|
||||
env->DeleteLocalRef(patch_class);
|
||||
|
||||
const jclass double_class = env->FindClass("java/lang/Double");
|
||||
s_double_class = reinterpret_cast<jclass>(env->NewGlobalRef(double_class));
|
||||
s_double_constructor = env->GetMethodID(double_class, "<init>", "(D)V");
|
||||
s_double_value_method = env->GetMethodID(double_class, "doubleValue", "()D");
|
||||
env->DeleteLocalRef(double_class);
|
||||
|
||||
const jclass int_class = env->FindClass("java/lang/Integer");
|
||||
s_integer_class = reinterpret_cast<jclass>(env->NewGlobalRef(int_class));
|
||||
s_integer_constructor = env->GetMethodID(int_class, "<init>", "(I)V");
|
||||
s_integer_value_method = env->GetMethodID(int_class, "intValue", "()I");
|
||||
env->DeleteLocalRef(int_class);
|
||||
|
||||
const jclass boolean_class = env->FindClass("java/lang/Boolean");
|
||||
s_boolean_class = reinterpret_cast<jclass>(env->NewGlobalRef(boolean_class));
|
||||
s_boolean_constructor = env->GetMethodID(boolean_class, "<init>", "(Z)V");
|
||||
s_boolean_value_method = env->GetMethodID(boolean_class, "booleanValue", "()Z");
|
||||
env->DeleteLocalRef(boolean_class);
|
||||
|
||||
const jclass player_input_class =
|
||||
env->FindClass("org/yuzu/yuzu_emu/features/input/model/PlayerInput");
|
||||
s_player_input_class = reinterpret_cast<jclass>(env->NewGlobalRef(player_input_class));
|
||||
s_player_input_constructor = env->GetMethodID(
|
||||
player_input_class, "<init>",
|
||||
"(Z[Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;ZIJJJJLjava/lang/String;Z)V");
|
||||
s_player_input_connected_field = env->GetFieldID(player_input_class, "connected", "Z");
|
||||
s_player_input_buttons_field =
|
||||
env->GetFieldID(player_input_class, "buttons", "[Ljava/lang/String;");
|
||||
s_player_input_analogs_field =
|
||||
env->GetFieldID(player_input_class, "analogs", "[Ljava/lang/String;");
|
||||
s_player_input_motions_field =
|
||||
env->GetFieldID(player_input_class, "motions", "[Ljava/lang/String;");
|
||||
s_player_input_vibration_enabled_field =
|
||||
env->GetFieldID(player_input_class, "vibrationEnabled", "Z");
|
||||
s_player_input_vibration_strength_field =
|
||||
env->GetFieldID(player_input_class, "vibrationStrength", "I");
|
||||
s_player_input_body_color_left_field =
|
||||
env->GetFieldID(player_input_class, "bodyColorLeft", "J");
|
||||
s_player_input_body_color_right_field =
|
||||
env->GetFieldID(player_input_class, "bodyColorRight", "J");
|
||||
s_player_input_button_color_left_field =
|
||||
env->GetFieldID(player_input_class, "buttonColorLeft", "J");
|
||||
s_player_input_button_color_right_field =
|
||||
env->GetFieldID(player_input_class, "buttonColorRight", "J");
|
||||
s_player_input_profile_name_field =
|
||||
env->GetFieldID(player_input_class, "profileName", "Ljava/lang/String;");
|
||||
s_player_input_use_system_vibrator_field =
|
||||
env->GetFieldID(player_input_class, "useSystemVibrator", "Z");
|
||||
env->DeleteLocalRef(player_input_class);
|
||||
|
||||
const jclass yuzu_input_device_interface =
|
||||
env->FindClass("org/yuzu/yuzu_emu/features/input/YuzuInputDevice");
|
||||
s_yuzu_input_device_interface =
|
||||
reinterpret_cast<jclass>(env->NewGlobalRef(yuzu_input_device_interface));
|
||||
s_yuzu_input_device_get_name =
|
||||
env->GetMethodID(yuzu_input_device_interface, "getName", "()Ljava/lang/String;");
|
||||
s_yuzu_input_device_get_guid =
|
||||
env->GetMethodID(yuzu_input_device_interface, "getGUID", "()Ljava/lang/String;");
|
||||
s_yuzu_input_device_get_port = env->GetMethodID(yuzu_input_device_interface, "getPort",
|
||||
"()I");
|
||||
s_yuzu_input_device_get_supports_vibration =
|
||||
env->GetMethodID(yuzu_input_device_interface, "getSupportsVibration", "()Z");
|
||||
s_yuzu_input_device_vibrate = env->GetMethodID(yuzu_input_device_interface, "vibrate",
|
||||
"(F)V");
|
||||
s_yuzu_input_device_get_axes =
|
||||
env->GetMethodID(yuzu_input_device_interface, "getAxes", "()[Ljava/lang/Integer;");
|
||||
s_yuzu_input_device_has_keys =
|
||||
env->GetMethodID(yuzu_input_device_interface, "hasKeys", "([I)[Z");
|
||||
env->DeleteLocalRef(yuzu_input_device_interface);
|
||||
s_add_netplay_message = env->GetStaticMethodID(s_native_library_class, "addNetPlayMessage",
|
||||
"(ILjava/lang/String;)V");
|
||||
s_clear_chat = env->GetStaticMethodID(s_native_library_class, "clearChat", "()V");
|
||||
|
||||
// Initialize Android Storage
|
||||
Common::FS::Android::RegisterCallbacks(env, s_native_library_class);
|
||||
|
||||
// Initialize applets
|
||||
Common::Android::SoftwareKeyboard::InitJNI(env);
|
||||
Common::Android::WebBrowser::InitJNI(env);
|
||||
}
|
||||
|
||||
} // namespace Common::Android
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
namespace Common::Android {
|
||||
|
||||
void Initialize(JavaVM* vm, JNIEnv *env);
|
||||
JNIEnv* GetEnvForThread();
|
||||
|
||||
/**
|
||||
@@ -80,15 +81,15 @@ jfieldID GetPatchTitleIdField();
|
||||
|
||||
jclass GetDoubleClass();
|
||||
jmethodID GetDoubleConstructor();
|
||||
jfieldID GetDoubleValueField();
|
||||
jmethodID GetDoubleValueMethod();
|
||||
|
||||
jclass GetIntegerClass();
|
||||
jmethodID GetIntegerConstructor();
|
||||
jfieldID GetIntegerValueField();
|
||||
jmethodID GetIntegerValueMethod();
|
||||
|
||||
jclass GetBooleanClass();
|
||||
jmethodID GetBooleanConstructor();
|
||||
jfieldID GetBooleanValueField();
|
||||
jmethodID GetBooleanValueMethod();
|
||||
|
||||
jclass GetPlayerInputClass();
|
||||
jmethodID GetPlayerInputConstructor();
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string>
|
||||
#include <cstdlib>
|
||||
#include <string_view>
|
||||
#ifdef _WIN32
|
||||
#include <llvm/Demangle/Demangle.h>
|
||||
|
||||
+10
-12
@@ -20,9 +20,6 @@
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#ifdef __OPENORBIS__
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define fileno _fileno
|
||||
@@ -405,27 +402,28 @@ u64 IOFile::GetSize() const {
|
||||
file_size = Android::GetSize(file_path);
|
||||
} else {
|
||||
std::error_code ec;
|
||||
|
||||
file_size = fs::file_size(file_path, ec);
|
||||
|
||||
if (ec) {
|
||||
LOG_ERROR(Common_Filesystem, "Failed to retrieve the file size of path={}, ec_message={}", PathToUTF8String(file_path), ec.message());
|
||||
LOG_ERROR(Common_Filesystem,
|
||||
"Failed to retrieve the file size of path={}, ec_message={}",
|
||||
PathToUTF8String(file_path), ec.message());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#elif defined(__OPENORBIS__)
|
||||
// TODO: implementation of fs::file_size() is buggy on PS4, why?
|
||||
// probably toolchain issue... ugh
|
||||
OrbisKernelStat st{};
|
||||
stat(file_path.c_str(), reinterpret_cast<struct stat *>(std::addressof(st)));
|
||||
auto const file_size = st.st_size;
|
||||
LOG_DEBUG(Common_Filesystem, "size for {} = {}", file_path.c_str(), file_size);
|
||||
#else
|
||||
std::error_code ec;
|
||||
|
||||
const auto file_size = fs::file_size(file_path, ec);
|
||||
|
||||
if (ec) {
|
||||
LOG_ERROR(Common_Filesystem, "Failed to retrieve the file size of path={}, ec_message={}", PathToUTF8String(file_path), ec.message());
|
||||
LOG_ERROR(Common_Filesystem, "Failed to retrieve the file size of path={}, ec_message={}",
|
||||
PathToUTF8String(file_path), ec.message());
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
return file_size;
|
||||
}
|
||||
|
||||
|
||||
@@ -174,14 +174,11 @@ bool CreateDir(const fs::path& path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Maybe this is what causes death?
|
||||
#ifndef __OPENORBIS__
|
||||
if (!Exists(path.parent_path())) {
|
||||
LOG_ERROR(Common_Filesystem, "Parent directory of path={} does not exist",
|
||||
PathToUTF8String(path));
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (IsDir(path)) {
|
||||
LOG_DEBUG(Common_Filesystem, "Filesystem object at path={} exists and is a directory",
|
||||
|
||||
+12
-11
@@ -130,10 +130,6 @@ public:
|
||||
ASSERT(!eden_path.empty());
|
||||
eden_path_cache = eden_path / CACHE_DIR;
|
||||
eden_path_config = eden_path / CONFIG_DIR;
|
||||
#elif defined(__OPENORBIS__)
|
||||
eden_path = "/data/eden";
|
||||
eden_path_cache = eden_path / CACHE_DIR;
|
||||
eden_path_config = eden_path / CONFIG_DIR;
|
||||
#else
|
||||
eden_path = GetCurrentDir() / PORTABLE_DIR;
|
||||
if (!Exists(eden_path) || !IsDir(eden_path)) {
|
||||
@@ -176,11 +172,12 @@ public:
|
||||
GenerateEdenPath(EdenPath::ShaderDir, eden_path_cache / SHADER_DIR);
|
||||
#ifdef _WIN32
|
||||
GenerateLegacyPath(EmuPath::RyujinxDir, GetAppDataRoamingDirectory() / RYUJINX_DIR);
|
||||
#elif !defined(__OPENORBIS__)
|
||||
#else
|
||||
// In Ryujinx's infinite wisdom, it places EVERYTHING in the config directory on UNIX
|
||||
// This is incredibly stupid and violates a million XDG standards, but whatever
|
||||
GenerateLegacyPath(EmuPath::RyujinxDir, GetDataDirectory("XDG_CONFIG_HOME") / RYUJINX_DIR);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -342,33 +339,36 @@ fs::path GetAppDataRoamingDirectory() {
|
||||
return fs_appdata_roaming_path;
|
||||
}
|
||||
|
||||
#elif defined(__OPENORBIS__)
|
||||
|
||||
// Not required/defined
|
||||
|
||||
#else
|
||||
|
||||
fs::path GetHomeDirectory() {
|
||||
const char* home_env_var = getenv("HOME");
|
||||
|
||||
if (home_env_var) {
|
||||
return fs::path{home_env_var};
|
||||
}
|
||||
|
||||
LOG_INFO(Common_Filesystem,
|
||||
"$HOME is not defined in the environment variables, "
|
||||
"attempting to query passwd to get the home path of the current user");
|
||||
"$HOME is not defined in the environment variables, "
|
||||
"attempting to query passwd to get the home path of the current user");
|
||||
|
||||
const auto* pw = getpwuid(getuid());
|
||||
|
||||
if (!pw) {
|
||||
LOG_ERROR(Common_Filesystem, "Failed to get the home path of the current user");
|
||||
return {};
|
||||
}
|
||||
|
||||
return fs::path{pw->pw_dir};
|
||||
}
|
||||
|
||||
fs::path GetDataDirectory(const std::string& env_name) {
|
||||
const char* data_env_var = getenv(env_name.c_str());
|
||||
|
||||
if (data_env_var) {
|
||||
return fs::path{data_env_var};
|
||||
}
|
||||
|
||||
if (env_name == "XDG_DATA_HOME") {
|
||||
return GetHomeDirectory() / ".local/share";
|
||||
} else if (env_name == "XDG_CACHE_HOME") {
|
||||
@@ -376,6 +376,7 @@ fs::path GetDataDirectory(const std::string& env_name) {
|
||||
} else if (env_name == "XDG_CONFIG_HOME") {
|
||||
return GetHomeDirectory() / ".config";
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
+10
-10
@@ -38,15 +38,15 @@
|
||||
|
||||
// FreeBSD
|
||||
#ifndef MAP_NORESERVE
|
||||
# define MAP_NORESERVE 0
|
||||
#define MAP_NORESERVE 0
|
||||
#endif
|
||||
// Solaris 11 and illumos
|
||||
#ifndef MAP_ALIGNED_SUPER
|
||||
# define MAP_ALIGNED_SUPER 0
|
||||
#define MAP_ALIGNED_SUPER 0
|
||||
#endif
|
||||
// macOS
|
||||
#ifndef MAP_ANONYMOUS
|
||||
# define MAP_ANONYMOUS MAP_ANON
|
||||
#define MAP_ANONYMOUS MAP_ANON
|
||||
#endif
|
||||
|
||||
#endif // ^^^ POSIX ^^^
|
||||
@@ -441,15 +441,13 @@ static void* ChooseVirtualBase(size_t virtual_size) {
|
||||
|
||||
#else
|
||||
|
||||
static void* ChooseVirtualBase(size_t size) {
|
||||
static void* ChooseVirtualBase(size_t virtual_size) {
|
||||
#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) || defined(__sun__) || defined(__HAIKU__) || defined(__managarm__) || defined(__AIX__)
|
||||
void* virtual_base = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_ALIGNED_SUPER, -1, 0);
|
||||
void* virtual_base = mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_ALIGNED_SUPER, -1, 0);
|
||||
if (virtual_base != MAP_FAILED)
|
||||
return virtual_base;
|
||||
return mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
|
||||
#else
|
||||
return mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
|
||||
#endif
|
||||
return mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -731,8 +729,9 @@ void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length, M
|
||||
ASSERT(length % PageAlignment == 0);
|
||||
ASSERT(virtual_offset + length <= virtual_size);
|
||||
ASSERT(host_offset + length <= backing_size);
|
||||
if (length == 0 || !virtual_base || !impl)
|
||||
if (length == 0 || !virtual_base || !impl) {
|
||||
return;
|
||||
}
|
||||
impl->Map(virtual_offset + virtual_base_offset, host_offset, length, perms);
|
||||
#endif
|
||||
}
|
||||
@@ -742,8 +741,9 @@ void HostMemory::Unmap(size_t virtual_offset, size_t length, bool separate_heap)
|
||||
ASSERT(virtual_offset % PageAlignment == 0);
|
||||
ASSERT(length % PageAlignment == 0);
|
||||
ASSERT(virtual_offset + length <= virtual_size);
|
||||
if (length == 0 || !virtual_base || !impl)
|
||||
if (length == 0 || !virtual_base || !impl) {
|
||||
return;
|
||||
}
|
||||
impl->Unmap(virtual_offset + virtual_base_offset, length);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -74,13 +74,14 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
size_t backing_size{};
|
||||
size_t virtual_size{};
|
||||
|
||||
#if !(defined(__OPENORBIS__) || defined(__managarm__))
|
||||
// Low level handler for the platform dependent memory routines
|
||||
class Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
#endif
|
||||
size_t backing_size{};
|
||||
size_t virtual_size{};
|
||||
u8* backing_base{};
|
||||
u8* virtual_base{};
|
||||
size_t virtual_base_offset{};
|
||||
|
||||
@@ -3,14 +3,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
// TODO: httplib port can't use OpenSSL because of package misconfigs on OO toolchain
|
||||
// right now the solution is to tell httplib that OpenSSL doesn't exist, but that's bad
|
||||
// and the issue is a bit more of how OpenSSL lacks some essential functionality
|
||||
// which is a bit annoying to reconfigure atm.
|
||||
#ifndef __OPENORBIS__
|
||||
#define CPPHTTPLIB_DISABLE_MACOSX_AUTOMATIC_ROOT_CERTIFICATES 1
|
||||
#define CPPHTTPLIB_OPENSSL_SUPPORT 1
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
|
||||
@@ -236,6 +236,7 @@ struct ColorConsoleBackend final : public Backend {
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifndef __OPENORBIS__
|
||||
/// @brief Backend that writes to a file passed into the constructor
|
||||
struct FileBackend final : public Backend {
|
||||
explicit FileBackend(const std::filesystem::path filename) noexcept {
|
||||
@@ -298,7 +299,7 @@ private:
|
||||
std::size_t bytes_written = 0;
|
||||
bool enabled = true;
|
||||
};
|
||||
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
/// @brief Backend that writes to Visual Studio's output window
|
||||
struct DebuggerBackend final : public Backend {
|
||||
@@ -342,8 +343,10 @@ struct Impl {
|
||||
template<typename F>
|
||||
void ForEachBackend(F&& lambda) noexcept {
|
||||
lambda(static_cast<Backend&>(color_console_backend));
|
||||
#ifndef __OPENORBIS__
|
||||
if (file_backend)
|
||||
lambda(static_cast<Backend&>(*file_backend));
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
lambda(static_cast<Backend&>(debugger_backend));
|
||||
#endif
|
||||
@@ -354,7 +357,9 @@ struct Impl {
|
||||
|
||||
Filter filter;
|
||||
ColorConsoleBackend color_console_backend{};
|
||||
#ifndef __OPENORBIS__
|
||||
std::optional<FileBackend> file_backend;
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
DebuggerBackend debugger_backend{};
|
||||
#endif
|
||||
@@ -391,10 +396,12 @@ void Initialize() {
|
||||
} else {
|
||||
logging_instance.emplace();
|
||||
logging_instance->filter.ParseFilterString(Settings::values.log_filter.GetValue());
|
||||
#ifndef __OPENORBIS__
|
||||
using namespace Common::FS;
|
||||
const auto& log_dir = GetEdenPath(EdenPath::LogDir);
|
||||
void(CreateDir(log_dir));
|
||||
logging_instance->file_backend.emplace(log_dir / LOG_FILE);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -11,7 +8,7 @@
|
||||
// clang-format on
|
||||
#else
|
||||
#include <sys/types.h>
|
||||
#if defined(__APPLE__) || (defined(__FreeBSD__) && !defined(__OPENORBIS__))
|
||||
#if defined(__APPLE__) || defined(__FreeBSD__)
|
||||
#include <sys/sysctl.h>
|
||||
#elif defined(__linux__)
|
||||
#include <sys/sysinfo.h>
|
||||
|
||||
@@ -213,7 +213,7 @@ std::optional<std::string> MakeRequest(const std::string& url, const std::string
|
||||
client->set_read_timeout(timeout_seconds);
|
||||
client->set_write_timeout(timeout_seconds);
|
||||
|
||||
#if defined(YUZU_BUNDLED_OPENSSL) && !defined(__OPENORBIS__)
|
||||
#ifdef YUZU_BUNDLED_OPENSSL
|
||||
client->load_ca_cert_store(kCert, sizeof(kCert));
|
||||
#endif
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#include "common/settings.h"
|
||||
#include "common/time_zone.h"
|
||||
|
||||
#ifdef __unix__
|
||||
#if defined(__linux__ ) && defined(ARCHITECTURE_arm64)
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
@@ -187,9 +187,12 @@ bool IsFastmemEnabled() {
|
||||
return bool(values.cpuopt_fastmem);
|
||||
else if (values.cpu_accuracy.GetValue() == CpuAccuracy::Unsafe)
|
||||
return bool(values.cpuopt_unsafe_host_mmu);
|
||||
#if defined(__unix__)
|
||||
// TODO: Support this since only 4kb systems support host MMU right now
|
||||
#if defined(__linux__) && defined(ARCHITECTURE_arm64)
|
||||
// Only 4kb systems support host MMU right now
|
||||
// TODO: Support this
|
||||
return getpagesize() == 4096;
|
||||
#elif !defined(__APPLE__) && !defined(__ANDROID__) && !defined(_WIN32) && !defined(__linux__) && !defined(__FreeBSD__)
|
||||
return false;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
|
||||
@@ -196,14 +196,7 @@ struct Values {
|
||||
linkage, false, "dump_audio_commands", Category::Audio, Specialization::Default, false};
|
||||
|
||||
// Core
|
||||
SwitchableSetting<bool> use_multi_core{linkage,
|
||||
#ifdef __OPENORBIS__
|
||||
// Re-enable once proper TLS support is added
|
||||
false,
|
||||
#else
|
||||
true,
|
||||
#endif
|
||||
"use_multi_core", Category::Core};
|
||||
SwitchableSetting<bool> use_multi_core{linkage, true, "use_multi_core", Category::Core};
|
||||
SwitchableSetting<MemoryLayout, true> memory_layout_mode{linkage,
|
||||
MemoryLayout::Memory_4Gb,
|
||||
"memory_layout_mode",
|
||||
@@ -337,7 +330,7 @@ struct Values {
|
||||
|
||||
// Renderer
|
||||
SwitchableSetting<RendererBackend, true> renderer_backend{linkage,
|
||||
#if defined(__sun__) || defined(__managarm__) || defined(__OPENORBIS__)
|
||||
#if defined(__sun__) || defined(__managarm__)
|
||||
RendererBackend::OpenGL_GLSL,
|
||||
#else
|
||||
RendererBackend::Vulkan,
|
||||
|
||||
@@ -92,15 +92,14 @@ struct EnumMetadata {
|
||||
// AudioEngine must be specified discretely due to having existing but slightly different
|
||||
// canonicalizations
|
||||
// TODO (lat9nq): Remove explicit definition of AudioEngine/sink_id
|
||||
enum class AudioEngine : u32 { Auto, Cubeb, Sdl3, Null, Oboe, Ps4, };
|
||||
enum class AudioEngine : u32 { Auto, Cubeb, Sdl3, Null, };
|
||||
template<>
|
||||
inline std::vector<std::pair<std::string_view, AudioEngine>> EnumMetadata<AudioEngine>::Canonicalizations() {
|
||||
return {
|
||||
{"auto", AudioEngine::Auto},
|
||||
{"cubeb", AudioEngine::Cubeb},
|
||||
{"sdl3", AudioEngine::Sdl3},
|
||||
{"null", AudioEngine::Null}, {"oboe", AudioEngine::Oboe},
|
||||
{"ps4", AudioEngine::Ps4},
|
||||
{"null", AudioEngine::Null},
|
||||
};
|
||||
}
|
||||
/// @brief This is just a sufficiently large number that is more than the number of other enums declared here
|
||||
@@ -114,7 +113,7 @@ inline AudioEngine EnumMetadata<AudioEngine>::GetFirst() {
|
||||
}
|
||||
template<>
|
||||
inline AudioEngine EnumMetadata<AudioEngine>::GetLast() {
|
||||
return AudioEngine::Oboe;
|
||||
return AudioEngine::Null;
|
||||
}
|
||||
|
||||
ENUM(AudioMode, Mono, Stereo, Surround);
|
||||
|
||||
+12
-175
@@ -10,197 +10,34 @@
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
#ifdef __OPENORBIS__
|
||||
#include <ranges>
|
||||
#include <csignal>
|
||||
#include <fcntl.h>
|
||||
#include <boost/container/static_vector.hpp>
|
||||
#include <orbis/SystemService.h>
|
||||
#include <orbis/libkernel.h>
|
||||
typedef void (*SceKernelExceptionHandler)(int32_t, void*);
|
||||
extern "C" int32_t sceKernelInstallExceptionHandler(int32_t signum, SceKernelExceptionHandler handler);
|
||||
#endif
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/virtual_buffer.h"
|
||||
#include "common/logging.h"
|
||||
|
||||
// PlayStation 4
|
||||
// Flag needs to be undef-ed on non PS4 since it has different semantics
|
||||
// on some platforms.
|
||||
#ifdef __OPENORBIS__
|
||||
# ifndef MAP_SYSTEM
|
||||
# define MAP_SYSTEM 0x2000
|
||||
# endif
|
||||
# ifndef MAP_VOID
|
||||
# define MAP_VOID 0x100
|
||||
# endif
|
||||
// sigaction(2) has a motherfucking bug on musl where the thing isnt even properly prefixed
|
||||
# undef sa_sigaction
|
||||
# define sa_sigaction __sa_handler.__sa_sigaction
|
||||
#endif
|
||||
|
||||
namespace Common {
|
||||
|
||||
#ifdef __OPENORBIS__
|
||||
|
||||
namespace Orbis {
|
||||
constexpr size_t ORBIS_PAGE_SIZE = 16384;
|
||||
|
||||
struct Ucontext {
|
||||
struct Sigset {
|
||||
u64 bits[2];
|
||||
} uc_sigmask;
|
||||
int field1_0x10[12];
|
||||
struct Mcontext {
|
||||
u64 mc_onstack;
|
||||
u64 mc_rdi;
|
||||
u64 mc_rsi;
|
||||
u64 mc_rdx;
|
||||
u64 mc_rcx;
|
||||
u64 mc_r8;
|
||||
u64 mc_r9;
|
||||
u64 mc_rax;
|
||||
u64 mc_rbx;
|
||||
u64 mc_rbp;
|
||||
u64 mc_r10;
|
||||
u64 mc_r11;
|
||||
u64 mc_r12;
|
||||
u64 mc_r13;
|
||||
u64 mc_r14;
|
||||
u64 mc_r15;
|
||||
int mc_trapno;
|
||||
u16 mc_fs;
|
||||
u16 mc_gs;
|
||||
u64 mc_addr;
|
||||
int mc_flags;
|
||||
u16 mc_es;
|
||||
u16 mc_ds;
|
||||
u64 mc_err;
|
||||
u64 mc_rip;
|
||||
u64 mc_cs;
|
||||
u64 mc_rflags;
|
||||
u64 mc_rsp;
|
||||
u64 mc_ss;
|
||||
u64 mc_len;
|
||||
u64 mc_fpformat;
|
||||
u64 mc_ownedfp;
|
||||
u64 mc_lbrfrom;
|
||||
u64 mc_lbrto;
|
||||
u64 mc_aux1;
|
||||
u64 mc_aux2;
|
||||
u64 mc_fpstate[104];
|
||||
u64 mc_fsbase;
|
||||
u64 mc_gsbase;
|
||||
u64 mc_spare[6];
|
||||
} uc_mcontext;
|
||||
struct Ucontext* uc_link;
|
||||
struct ExStack {
|
||||
void* ss_sp;
|
||||
std::size_t ss_size;
|
||||
int ss_flags;
|
||||
int _align;
|
||||
} uc_stack;
|
||||
int uc_flags;
|
||||
int __spare[4];
|
||||
int field7_0x4f4[3];
|
||||
};
|
||||
}
|
||||
|
||||
static boost::container::static_vector<std::pair<void*, size_t>, 16> swap_regions;
|
||||
static std::mutex evil_swap_mutex;
|
||||
extern "C" int sceKernelRemoveExceptionHandler(s32 sig_num);
|
||||
static void SwapHandler(int sig, void* raw_context) {
|
||||
std::unique_lock lk{evil_swap_mutex};
|
||||
auto& mctx = ((Orbis::Ucontext*)raw_context)->uc_mcontext;
|
||||
if (auto const it = std::ranges::find_if(swap_regions, [addr = mctx.mc_addr](auto const& e) {
|
||||
return uintptr_t(addr) >= uintptr_t(e.first) && uintptr_t(addr) < uintptr_t(e.first) + e.second;
|
||||
}); it != swap_regions.end()) {
|
||||
size_t const page_size = Orbis::ORBIS_PAGE_SIZE * 128; //128K
|
||||
size_t const page_mask = ~(page_size - 1);
|
||||
// should replace the existing mapping... ugh
|
||||
void* aligned_addr = reinterpret_cast<void*>(uintptr_t(mctx.mc_addr) & page_mask);
|
||||
void* res = mmap(aligned_addr, page_size, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
if (res == MAP_FAILED) {
|
||||
LOG_ERROR(HW_Memory, "{:#x},{} @ {}:{:#x}", mctx.mc_addr, aligned_addr, it->first, it->second);
|
||||
sceKernelRemoveExceptionHandler(SIGSEGV); // to not catch the next signal
|
||||
} else {
|
||||
LOG_DEBUG(HW_Memory, "{:#x},{} @ {}:{:#x}", mctx.mc_addr, aligned_addr, it->first, it->second);
|
||||
}
|
||||
} else {
|
||||
LOG_ERROR(HW_Memory, "fault in addr {:#x} at {:#x}", mctx.mc_addr, mctx.mc_rip); // print caller address
|
||||
sceKernelRemoveExceptionHandler(SIGSEGV); // to not catch the next signal
|
||||
}
|
||||
}
|
||||
void InitSwap() noexcept {
|
||||
sceKernelInstallExceptionHandler(SIGSEGV, &SwapHandler);
|
||||
}
|
||||
#else
|
||||
void InitSwap() noexcept {}
|
||||
#endif
|
||||
|
||||
void* AllocateMemoryPages(std::size_t size) noexcept {
|
||||
#ifdef _WIN32
|
||||
void* addr = VirtualAlloc(nullptr, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||
if (addr == nullptr) {
|
||||
void* base = VirtualAlloc(nullptr, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||
if (base == nullptr) {
|
||||
// Probably failing to reserve is less likely than failing to commit
|
||||
addr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE);
|
||||
}
|
||||
#elif defined(__OPENORBIS__)
|
||||
bool use_void_mem = true;
|
||||
void* addr = nullptr;
|
||||
if (size < 4294967296ull) {
|
||||
size_t align = Orbis::ORBIS_PAGE_SIZE;
|
||||
off_t offset;
|
||||
int32_t res;
|
||||
size = (size + align - 1) / align * align;
|
||||
if ((res = sceKernelAllocateDirectMemory(0, ORBIS_KERNEL_MAIN_DMEM_SIZE, size, align, ORBIS_KERNEL_WB_ONION, &offset)) == 0) {
|
||||
if ((res = sceKernelMapDirectMemory(&addr, size, ORBIS_KERNEL_PROT_CPU_READ | ORBIS_KERNEL_PROT_CPU_WRITE, 0, offset, size)) == 0) {
|
||||
if ((res = sceKernelMprotect(addr, size, VM_PROT_ALL)) == 0 && addr != nullptr) {
|
||||
LOG_WARNING(HW_Memory, "Using DMem for {} bytes area @ {}", size, addr);
|
||||
use_void_mem = false; //Memory properly mapped
|
||||
} else {
|
||||
sceKernelReleaseDirectMemory(offset, size);
|
||||
LOG_ERROR(HW_Memory, "{} = sceKernelMprotect({}, {})", res, offset, size);
|
||||
}
|
||||
} else {
|
||||
sceKernelReleaseDirectMemory(offset, size);
|
||||
LOG_ERROR(HW_Memory, "{} = sceKernelMapDirectMemory({}, {})", res, offset, size);
|
||||
}
|
||||
} else {
|
||||
LOG_ERROR(HW_Memory, "{} = sceKernelAllocateDirectMemory({}, {}, {})", res, size, align, offset);
|
||||
}
|
||||
}
|
||||
if (use_void_mem) {
|
||||
addr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_VOID | MAP_PRIVATE, -1, 0);
|
||||
LOG_WARNING(HW_Memory, "Using VoidMem for {} bytes area @ {}", size, addr);
|
||||
ASSERT(addr != MAP_FAILED);
|
||||
swap_regions.emplace_back(addr, size);
|
||||
base = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE);
|
||||
}
|
||||
#else
|
||||
void* addr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
ASSERT(addr != MAP_FAILED);
|
||||
void* base = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
if (base == MAP_FAILED)
|
||||
base = nullptr;
|
||||
#endif
|
||||
return addr;
|
||||
ASSERT(base);
|
||||
return base;
|
||||
}
|
||||
|
||||
void FreeMemoryPages(void* addr, [[maybe_unused]] std::size_t size) noexcept {
|
||||
if (!addr)
|
||||
void FreeMemoryPages(void* base, [[maybe_unused]] std::size_t size) noexcept {
|
||||
if (!base)
|
||||
return;
|
||||
#ifdef _WIN32
|
||||
VirtualFree(addr, 0, MEM_RELEASE);
|
||||
#elif defined(__OPENORBIS__)
|
||||
if (auto const it = std::ranges::find_if(swap_regions, [=](auto const& e) {
|
||||
return uintptr_t(addr) >= uintptr_t(e.first) && uintptr_t(addr) < uintptr_t(e.first) + e.second;
|
||||
}); it != swap_regions.end()) {
|
||||
int rc = munmap(addr, size);
|
||||
ASSERT(rc == 0);
|
||||
} else {
|
||||
sceKernelCheckedReleaseDirectMemory(off_t(addr), size_t(size));
|
||||
}
|
||||
ASSERT(VirtualFree(base, 0, MEM_RELEASE));
|
||||
#else
|
||||
int rc = munmap(addr, size);
|
||||
ASSERT(rc == 0);
|
||||
ASSERT(munmap(base, size) == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ namespace Common {
|
||||
|
||||
void* AllocateMemoryPages(std::size_t size) noexcept;
|
||||
void FreeMemoryPages(void* base, std::size_t size) noexcept;
|
||||
void InitSwap() noexcept;
|
||||
|
||||
template <typename T>
|
||||
class VirtualBuffer final {
|
||||
|
||||
@@ -199,9 +199,7 @@ void ArmDynarmic32::MakeJit(Common::PageTable* page_table) {
|
||||
config.enable_cycle_counting = !m_uses_wall_clock;
|
||||
|
||||
// Code cache size
|
||||
#if defined(__OPENORBIS__)
|
||||
config.code_cache_size = std::uint32_t(8_MiB);
|
||||
#elif defined(ARCHITECTURE_arm64) || defined(__sun__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__OpenBSD__)
|
||||
#if defined(ARCHITECTURE_arm64) || defined(__sun__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__OpenBSD__)
|
||||
config.code_cache_size = std::uint32_t(128_MiB);
|
||||
#else
|
||||
config.code_cache_size = std::uint32_t(512_MiB);
|
||||
|
||||
@@ -252,9 +252,7 @@ void ArmDynarmic64::MakeJit(Common::PageTable* page_table, std::size_t address_s
|
||||
config.enable_cycle_counting = !m_uses_wall_clock;
|
||||
|
||||
// Code cache size
|
||||
#if defined(__OPENORBIS__)
|
||||
config.code_cache_size = std::uint32_t(8_MiB);
|
||||
#elif defined(ARCHITECTURE_arm64) || defined(__sun__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__OpenBSD__)
|
||||
#if defined(ARCHITECTURE_arm64) || defined(__sun__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__OpenBSD__)
|
||||
config.code_cache_size = std::uint32_t(128_MiB);
|
||||
#else
|
||||
config.code_cache_size = std::uint32_t(512_MiB);
|
||||
|
||||
@@ -358,7 +358,7 @@ private:
|
||||
|
||||
ConnectionState(boost::asio::ip::tcp::socket&& client_socket_, async_pipe signal_pipe_, Kernel::KernelCore& kernel)
|
||||
: client_socket{std::move(client_socket_)}
|
||||
, signal_pipe{signal_pipe_}
|
||||
, signal_pipe{std::move(signal_pipe_)}
|
||||
, active_thread{kernel, nullptr}
|
||||
{}
|
||||
|
||||
|
||||
@@ -461,19 +461,8 @@ std::vector<VirtualFile> RealVfsDirectory::GetFiles() const {
|
||||
|
||||
FileTimeStampRaw RealVfsDirectory::GetFileTimeStamp(std::string_view path_) const {
|
||||
const auto full_path = FS::SanitizePath(path + '/' + std::string(path_));
|
||||
#ifdef __OPENORBIS__
|
||||
// DO NOT USE NORMAL STAT, it's bugged on OO toolchain
|
||||
OrbisKernelStat st{};
|
||||
const auto stat_result = stat(full_path.c_str(), reinterpret_cast<struct stat *>(std::addressof(st)));
|
||||
if (stat_result != 0)
|
||||
return {};
|
||||
return {
|
||||
.created{u64(st.st_ctime)},
|
||||
.accessed{u64(st.st_atime)},
|
||||
.modified{u64(st.st_mtime)},
|
||||
};
|
||||
#else
|
||||
const auto fs_path = std::filesystem::path{FS::ToU8String(full_path)};
|
||||
|
||||
#ifdef _WIN32
|
||||
struct _stat64 file_status;
|
||||
const auto stat_result = _wstat64(fs_path.c_str(), &file_status);
|
||||
@@ -481,14 +470,16 @@ FileTimeStampRaw RealVfsDirectory::GetFileTimeStamp(std::string_view path_) cons
|
||||
struct stat file_status;
|
||||
const auto stat_result = stat(fs_path.c_str(), &file_status);
|
||||
#endif
|
||||
if (stat_result != 0)
|
||||
|
||||
if (stat_result != 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return {
|
||||
.created{u64(file_status.st_ctime)},
|
||||
.accessed{u64(file_status.st_atime)},
|
||||
.modified{u64(file_status.st_mtime)},
|
||||
.created{static_cast<u64>(file_status.st_ctime)},
|
||||
.accessed{static_cast<u64>(file_status.st_atime)},
|
||||
.modified{static_cast<u64>(file_status.st_mtime)},
|
||||
};
|
||||
#endif
|
||||
}
|
||||
|
||||
std::vector<VirtualDir> RealVfsDirectory::GetSubdirectories() const {
|
||||
|
||||
@@ -1021,6 +1021,9 @@ Result KProcess::Run(KernelCore& kernel, s32 priority, size_t stack_size) {
|
||||
|
||||
// Suspend for debug, if we should.
|
||||
if (kernel.System().DebuggerEnabled()) {
|
||||
LOG_INFO(Debug_GDBStub,
|
||||
"GDB stub enabled; suspending guest process until a debugger continues execution on port {}",
|
||||
Settings::values.gdbstub_port.GetValue());
|
||||
main_thread->RequestSuspend(kernel, SuspendType::Debug);
|
||||
}
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ std::vector<u8> DownloadImage(const std::string& url_path, const std::filesystem
|
||||
cli.set_connection_timeout(std::chrono::seconds(2));
|
||||
cli.set_read_timeout(std::chrono::seconds(2));
|
||||
|
||||
#if defined(YUZU_BUNDLED_OPENSSL) && !defined(__OPENORBIS__)
|
||||
#ifdef YUZU_BUNDLED_OPENSSL
|
||||
cli.load_ca_cert_store(kCert, sizeof(kCert));
|
||||
#endif
|
||||
|
||||
|
||||
@@ -34,13 +34,12 @@ struct FontRegion {
|
||||
|
||||
// The below data is specific to shared font data dumped from Switch on f/w 2.2
|
||||
// Virtual address and offsets/sizes likely will vary by dump
|
||||
[[maybe_unused]] constexpr VAddr SHARED_FONT_MEM_VADDR = 0x00000009d3016000ULL;
|
||||
constexpr u32 EXPECTED_RESULT = 0x7f9a0218; // What we expect the decrypted bfttf first 4 bytes to be
|
||||
constexpr u32 EXPECTED_MAGIC = 0x36f81a1e; // What we expect the encrypted bfttf first 4 bytes to be
|
||||
constexpr u64 SHARED_FONT_MEM_SIZE = 0x1100000;
|
||||
[[maybe_unused]] constexpr VAddr SHARED_FONT_MEM_VADDR{0x00000009d3016000ULL};
|
||||
constexpr u32 EXPECTED_RESULT{0x7f9a0218}; // What we expect the decrypted bfttf first 4 bytes to be
|
||||
constexpr u32 EXPECTED_MAGIC{0x36f81a1e}; // What we expect the encrypted bfttf first 4 bytes to be
|
||||
constexpr u64 SHARED_FONT_MEM_SIZE{0x1100000};
|
||||
constexpr FontRegion EMPTY_REGION{0, 0};
|
||||
|
||||
#ifndef __OPENORBIS__
|
||||
static void DecryptSharedFont(const std::span<u32 const> input, std::span<u8> output, std::size_t& offset) {
|
||||
ASSERT(offset + (input.size() * sizeof(u32)) < SHARED_FONT_MEM_SIZE && "Shared fonts exceeds 17mb!");
|
||||
ASSERT(input[0] == EXPECTED_MAGIC && "Failed to derive key, unexpected magic number");
|
||||
@@ -52,7 +51,6 @@ static void DecryptSharedFont(const std::span<u32 const> input, std::span<u8> ou
|
||||
std::memcpy(output.data() + offset, transformed_font.data(), transformed_font.size() * sizeof(u32));
|
||||
offset += transformed_font.size() * sizeof(u32);
|
||||
}
|
||||
#endif
|
||||
|
||||
void DecryptSharedFontToTTF(const std::vector<u32>& input, std::vector<u8>& output) {
|
||||
ASSERT_MSG(input[0] == EXPECTED_MAGIC, "Failed to derive key, unexpected magic number");
|
||||
@@ -72,7 +70,7 @@ void EncryptSharedFont(const std::vector<u32>& input, std::vector<u8>& output, s
|
||||
const auto key = Common::swap32(EXPECTED_RESULT ^ EXPECTED_MAGIC);
|
||||
std::vector<u32> transformed_font(input.size() + 2);
|
||||
transformed_font[0] = Common::swap32(EXPECTED_MAGIC);
|
||||
transformed_font[1] = Common::swap32(u32(input.size() * sizeof(u32))) ^ key;
|
||||
transformed_font[1] = Common::swap32(static_cast<u32>(input.size() * sizeof(u32))) ^ key;
|
||||
std::transform(input.begin(), input.end(), transformed_font.begin() + 2, [key](u32 in) { return in ^ key; });
|
||||
std::memcpy(output.data() + offset, transformed_font.data(), transformed_font.size() * sizeof(u32));
|
||||
offset += transformed_font.size() * sizeof(u32);
|
||||
@@ -85,10 +83,8 @@ struct IPlatformServiceManager::Impl {
|
||||
// Automatically populated based on shared_fonts dump or system archives.
|
||||
// 6 builtin fonts + extra 2 for whatever may come after
|
||||
boost::container::static_vector<FontRegion, 8> shared_font_regions;
|
||||
#ifndef __OPENORBIS__
|
||||
/// Backing memory for the shared font data
|
||||
std::array<u8, SHARED_FONT_MEM_SIZE> shared_font;
|
||||
#endif
|
||||
};
|
||||
|
||||
IPlatformServiceManager::IPlatformServiceManager(Core::System& system_, const char* service_name_)
|
||||
@@ -115,40 +111,49 @@ IPlatformServiceManager::IPlatformServiceManager(Core::System& system_, const ch
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
|
||||
#ifndef __OPENORBIS__
|
||||
auto& fsc = system.GetFileSystemController();
|
||||
|
||||
// Attempt to load shared font data from disk
|
||||
const auto* nand = fsc.GetSystemNANDContents();
|
||||
std::size_t offset = 0;
|
||||
// Rebuild shared fonts from data ncas or synthesize
|
||||
for (auto& font : SHARED_FONTS) {
|
||||
FileSys::VirtualFile romfs;
|
||||
if (auto const nca = nand->GetEntry(u64(font.first), FileSys::ContentRecordType::Data); nca)
|
||||
const auto nca =
|
||||
nand->GetEntry(static_cast<u64>(font.first), FileSys::ContentRecordType::Data);
|
||||
if (nca) {
|
||||
romfs = nca->GetRomFS();
|
||||
if (!romfs)
|
||||
romfs = FileSys::SystemArchive::SynthesizeSystemArchive(u64(font.first));
|
||||
if (romfs) {
|
||||
if (auto const extracted_romfs = FileSys::ExtractRomFS(romfs); extracted_romfs) {
|
||||
if (auto const font_fp = extracted_romfs->GetFile(font.second); font_fp) {
|
||||
std::vector<u32> font_data_u32(font_fp->GetSize() / sizeof(u32));
|
||||
font_fp->ReadBytes<u32>(font_data_u32.data(), font_fp->GetSize());
|
||||
// We need to be BigEndian as u32s for the xor encryption
|
||||
std::transform(font_data_u32.begin(), font_data_u32.end(), font_data_u32.begin(), Common::swap32);
|
||||
// Font offset and size do not account for the header
|
||||
const FontRegion region{u32(offset + 8), u32((font_data_u32.size() * sizeof(u32)) - 8)};
|
||||
DecryptSharedFont(font_data_u32, impl->shared_font, offset);
|
||||
impl->shared_font_regions.push_back(region);
|
||||
} else {
|
||||
LOG_ERROR(Service_NS, "{:016X} has no file \"{}\"! Skipping", font.first, font.second);
|
||||
}
|
||||
} else {
|
||||
LOG_ERROR(Service_NS, "Failed to extract RomFS for {:016X}! Skipping", font.first);
|
||||
}
|
||||
} else {
|
||||
LOG_ERROR(Service_NS, "Failed to find or synthesize {:016X}! Skipping", font.first);
|
||||
}
|
||||
|
||||
if (!romfs) {
|
||||
romfs = FileSys::SystemArchive::SynthesizeSystemArchive(static_cast<u64>(font.first));
|
||||
}
|
||||
|
||||
if (!romfs) {
|
||||
LOG_ERROR(Service_NS, "Failed to find or synthesize {:016X}! Skipping", font.first);
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto extracted_romfs = FileSys::ExtractRomFS(romfs);
|
||||
if (!extracted_romfs) {
|
||||
LOG_ERROR(Service_NS, "Failed to extract RomFS for {:016X}! Skipping", font.first);
|
||||
continue;
|
||||
}
|
||||
const auto font_fp = extracted_romfs->GetFile(font.second);
|
||||
if (!font_fp) {
|
||||
LOG_ERROR(Service_NS, "{:016X} has no file \"{}\"! Skipping", font.first, font.second);
|
||||
continue;
|
||||
}
|
||||
std::vector<u32> font_data_u32(font_fp->GetSize() / sizeof(u32));
|
||||
font_fp->ReadBytes<u32>(font_data_u32.data(), font_fp->GetSize());
|
||||
// We need to be BigEndian as u32s for the xor encryption
|
||||
std::transform(font_data_u32.begin(), font_data_u32.end(), font_data_u32.begin(),
|
||||
Common::swap32);
|
||||
// Font offset and size do not account for the header
|
||||
const FontRegion region{u32(offset + 8), u32((font_data_u32.size() * sizeof(u32)) - 8)};
|
||||
DecryptSharedFont(font_data_u32, impl->shared_font, offset);
|
||||
impl->shared_font_regions.push_back(region);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
IPlatformServiceManager::~IPlatformServiceManager() = default;
|
||||
@@ -182,10 +187,8 @@ Result IPlatformServiceManager::GetSharedMemoryNativeHandle(OutCopyHandle<Kernel
|
||||
// Map backing memory for the font data
|
||||
LOG_DEBUG(Service_NS, "called");
|
||||
|
||||
#ifndef __OPENORBIS__
|
||||
// Create shared font memory object
|
||||
std::memcpy(kernel.GetFontSharedMem().GetPointer(), impl->shared_font.data(), impl->shared_font.size());
|
||||
#endif
|
||||
|
||||
// FIXME: this shouldn't belong to the kernel
|
||||
*out_shared_memory_native_handle = &kernel.GetFontSharedMem();
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2017 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -63,11 +60,7 @@ private:
|
||||
std::size_t current_index{0};
|
||||
/// Stores an hour of historical frametime data useful for processing and tracking performance
|
||||
/// regressions with code changes.
|
||||
#ifdef __OPENORBIS__
|
||||
std::array<double, 60> perf_history{};
|
||||
#else
|
||||
std::array<double, 216000> perf_history{};
|
||||
#endif
|
||||
|
||||
/// Point when the cumulative counters were reset
|
||||
Clock::time_point reset_point = Clock::now();
|
||||
|
||||
@@ -66,16 +66,12 @@ public:
|
||||
auto const stack_size = std::max<size_t>(SIGSTKSZ, 2 * 1024 * 1024);
|
||||
signal_stack_memory = std::make_unique<uint8_t[]>(stack_size);
|
||||
|
||||
#ifdef __OPENORBIS__
|
||||
fmt::print(stderr, "no fastmem on PS4\n");
|
||||
supports_fast_mem = false;
|
||||
#else
|
||||
stack_t signal_stack{};
|
||||
signal_stack.ss_sp = signal_stack_memory.get();
|
||||
signal_stack.ss_size = stack_size;
|
||||
signal_stack.ss_flags = 0;
|
||||
if (sigaltstack(&signal_stack, nullptr) != 0) {
|
||||
fmt::print(stderr, "POSIX SigHandler: init failure at sigaltstack\n");
|
||||
fmt::print(stderr, "dynarmic: POSIX SigHandler: init failure at sigaltstack\n");
|
||||
supports_fast_mem = false;
|
||||
return;
|
||||
}
|
||||
@@ -86,17 +82,16 @@ public:
|
||||
sa.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_RESTART;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
if (sigaction(SIGSEGV, &sa, &old_sa_segv) != 0) {
|
||||
fmt::print(stderr, "POSIX SigHandler: could not set SIGSEGV handler\n");
|
||||
fmt::print(stderr, "dynarmic: POSIX SigHandler: could not set SIGSEGV handler\n");
|
||||
supports_fast_mem = false;
|
||||
return;
|
||||
}
|
||||
#if defined(__APPLE__)
|
||||
if (sigaction(SIGBUS, &sa, &old_sa_bus) != 0) {
|
||||
fmt::print(stderr, "POSIX SigHandler: could not set SIGBUS handler\n");
|
||||
fmt::print(stderr, "dynarmic: POSIX SigHandler: could not set SIGBUS handler\n");
|
||||
supports_fast_mem = false;
|
||||
return;
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -150,9 +145,6 @@ void SigHandler::SigAction(int sig, siginfo_t* info, void* raw_context) {
|
||||
}
|
||||
LOG_ERROR(Core, "Unhandled {} at {:#018x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_PC);
|
||||
|
||||
#ifdef __OPENORBIS__
|
||||
// No fastmem
|
||||
#else
|
||||
struct sigaction* retry_sa = sig == SIGSEGV ? &sig_handler->old_sa_segv : &sig_handler->old_sa_bus;
|
||||
if (retry_sa->sa_flags & SA_SIGINFO) {
|
||||
retry_sa->sa_sigaction(sig, info, raw_context);
|
||||
@@ -166,7 +158,6 @@ void SigHandler::SigAction(int sig, siginfo_t* info, void* raw_context) {
|
||||
return;
|
||||
}
|
||||
retry_sa->sa_handler(sig);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
@@ -67,14 +67,8 @@ public:
|
||||
const void* code_ptr = nullptr;
|
||||
};
|
||||
static_assert(sizeof(FastDispatchEntry) == 0x10);
|
||||
#ifdef __OPENORBIS__
|
||||
static constexpr u64 fast_dispatch_table_mask = 0xFF0;
|
||||
static constexpr size_t fast_dispatch_table_size = 0x100;
|
||||
#else
|
||||
static constexpr u64 fast_dispatch_table_mask = 0xFFFF0;
|
||||
static constexpr size_t fast_dispatch_table_size = 0x10000;
|
||||
#endif
|
||||
|
||||
void ClearFastDispatchTable();
|
||||
void GenFastmemFallbacks();
|
||||
void GenTerminalHandlers();
|
||||
|
||||
@@ -60,13 +60,8 @@ public:
|
||||
const void* code_ptr = nullptr;
|
||||
};
|
||||
static_assert(sizeof(FastDispatchEntry) == 0x10);
|
||||
#ifdef __OPENORBIS__
|
||||
static constexpr u64 fast_dispatch_table_mask = 0xFF0;
|
||||
static constexpr size_t fast_dispatch_table_size = 0x100;
|
||||
#else
|
||||
static constexpr u64 fast_dispatch_table_mask = 0xFFFFF0;
|
||||
static constexpr size_t fast_dispatch_table_size = 0x100000;
|
||||
#endif
|
||||
|
||||
void ClearFastDispatchTable();
|
||||
void GenMemory128Accessors();
|
||||
|
||||
@@ -41,10 +41,9 @@ EmitContext::EmitContext(RegAlloc& reg_alloc, IR::Block& block, boost::container
|
||||
|
||||
EmitContext::~EmitContext() = default;
|
||||
|
||||
EmitX64::EmitX64(BlockOfCode& code) : code(code) {
|
||||
#ifndef __OPENORBIS__
|
||||
EmitX64::EmitX64(BlockOfCode& code)
|
||||
: code(code) {
|
||||
exception_handler.Register(code);
|
||||
#endif
|
||||
}
|
||||
|
||||
EmitX64::~EmitX64() = default;
|
||||
|
||||
@@ -88,7 +88,7 @@
|
||||
# define CTX_Q(i) (fpctx->vregs[i])
|
||||
# define CTX_FPSR (fpctx->fpsr)
|
||||
# define CTX_FPCR (fpctx->fpcr)
|
||||
# elif defined(__FreeBSD__) || defined(__DragonFly__)
|
||||
# elif defined(__FreeBSD__)
|
||||
# define CTX_PC (mctx.mc_gpregs.gp_elr)
|
||||
# define CTX_SP (mctx.mc_gpregs.gp_sp)
|
||||
# define CTX_LR (mctx.mc_gpregs.gp_lr)
|
||||
@@ -131,9 +131,6 @@
|
||||
# elif defined(__managarm__)
|
||||
# define CTX_PC (mctx.__pollution(gregs)[REG_RIP])
|
||||
# define CTX_SP (mctx.__pollution(gregs)[REG_RSP])
|
||||
# elif defined(__OPENORBIS__)
|
||||
# define CTX_RIP (mctx.gregs[REG_RIP])
|
||||
# define CTX_RSP (mctx.gregs[REG_RSP])
|
||||
# else
|
||||
# error "unknown platform"
|
||||
# endif
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#include "dynarmic/backend/x64/abi.h"
|
||||
#include "dynarmic/backend/x64/hostloc.h"
|
||||
#include "dynarmic/common/spin_lock.h"
|
||||
#include "xbyak/xbyak.h"
|
||||
|
||||
#ifdef DYNARMIC_ENABLE_NO_EXECUTE_SUPPORT
|
||||
static const auto default_cg_mode = Xbyak::DontSetProtectRWE;
|
||||
@@ -79,7 +78,7 @@ namespace {
|
||||
struct SpinLockImpl {
|
||||
void Initialize() noexcept;
|
||||
static void GlobalInitialize() noexcept;
|
||||
Xbyak::CodeGenerator code = Xbyak::CodeGenerator(Xbyak::inner::getPageSize(), default_cg_mode);
|
||||
Xbyak::CodeGenerator code = Xbyak::CodeGenerator(4096, default_cg_mode);
|
||||
void (*lock)(volatile int*) = nullptr;
|
||||
void (*unlock)(volatile int*) = nullptr;
|
||||
};
|
||||
|
||||
@@ -134,8 +134,3 @@ target_compile_options(dynarmic_tests PRIVATE ${DYNARMIC_CXX_FLAGS})
|
||||
target_compile_definitions(dynarmic_tests PRIVATE FMT_USE_USER_DEFINED_LITERALS=1)
|
||||
|
||||
add_test(NAME dynarmic_tests COMMAND dynarmic_tests --durations yes)
|
||||
|
||||
if (OPENORBIS)
|
||||
target_link_libraries(dynarmic_tests PRIVATE SceVideoOut SceAudioOut ScePad SceSystemService ps4sup)
|
||||
create_ps4_eboot(dynarmic_tests dynarmic_tests IV0000-BREW00090_00-DYNARMICTS000000)
|
||||
endif()
|
||||
|
||||
@@ -114,7 +114,7 @@ std::set<s32> Android::GetDeviceAxes(JNIEnv* env, jobject& j_device) const {
|
||||
std::set<s32> axes;
|
||||
for (int i = 0; i < env->GetArrayLength(j_axes); ++i) {
|
||||
jobject axis = env->GetObjectArrayElement(j_axes, i);
|
||||
axes.insert(env->GetIntField(axis, Common::Android::GetIntegerValueField()));
|
||||
axes.insert(env->CallIntMethod(axis, Common::Android::GetIntegerValueMethod()));
|
||||
}
|
||||
return axes;
|
||||
}
|
||||
|
||||
@@ -1033,20 +1033,27 @@ ButtonBindings SDLDriver::GetDefaultButtonBinding(
|
||||
};
|
||||
}
|
||||
|
||||
ButtonMapping SDLDriver::GetSingleControllerMapping(const std::shared_ptr<SDLJoystick>& joystick, const ButtonBindings& switch_to_sdl_button, const ZButtonBindings& switch_to_sdl_axis) const {
|
||||
ButtonMapping SDLDriver::GetSingleControllerMapping(
|
||||
const std::shared_ptr<SDLJoystick>& joystick, const ButtonBindings& switch_to_sdl_button,
|
||||
const ZButtonBindings& switch_to_sdl_axis) const {
|
||||
ButtonMapping mapping;
|
||||
mapping.reserve(switch_to_sdl_button.size() + switch_to_sdl_axis.size());
|
||||
if (auto* controller = joystick->GetSDLGameController(); controller) {
|
||||
auto* controller = joystick->GetSDLGameController();
|
||||
const auto bindings = GetBindings(controller);
|
||||
|
||||
for (const auto& [switch_button, sdl_button] : switch_to_sdl_button) {
|
||||
const auto binding = GetBindingForButton(bindings, sdl_button);
|
||||
mapping.insert_or_assign(switch_button, BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
|
||||
mapping.insert_or_assign(
|
||||
switch_button,
|
||||
BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
|
||||
}
|
||||
for (const auto& [switch_button, sdl_axis] : switch_to_sdl_axis) {
|
||||
const auto binding = GetBindingForAxis(bindings, sdl_axis);
|
||||
mapping.insert_or_assign(switch_button, BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
|
||||
}
|
||||
mapping.insert_or_assign(
|
||||
switch_button,
|
||||
BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
|
||||
}
|
||||
|
||||
return mapping;
|
||||
}
|
||||
|
||||
|
||||
@@ -82,10 +82,7 @@ struct InputSubsystem::Impl {
|
||||
#ifdef ENABLE_LIBUSB
|
||||
RegisterEngine("gcpad", gcadapter);
|
||||
#endif
|
||||
#ifndef __OPENORBIS__
|
||||
// TODO: Issue in PS4, crash for UDP_client
|
||||
RegisterEngine("cemuhookudp", udp_client);
|
||||
#endif
|
||||
RegisterEngine("tas", tas_input);
|
||||
RegisterEngine("camera", camera);
|
||||
#ifdef __ANDROID__
|
||||
@@ -119,9 +116,7 @@ struct InputSubsystem::Impl {
|
||||
#ifdef ENABLE_LIBUSB
|
||||
UnregisterEngine(gcadapter);
|
||||
#endif
|
||||
#ifndef __OPENORBIS__
|
||||
UnregisterEngine(udp_client);
|
||||
#endif
|
||||
UnregisterEngine(tas_input);
|
||||
UnregisterEngine(camera);
|
||||
#ifdef __ANDROID__
|
||||
@@ -157,10 +152,8 @@ struct InputSubsystem::Impl {
|
||||
auto gcadapter_devices = gcadapter->GetInputDevices();
|
||||
devices.insert(devices.end(), gcadapter_devices.begin(), gcadapter_devices.end());
|
||||
#endif
|
||||
#ifndef __OPENORBIS__
|
||||
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());
|
||||
@@ -193,11 +186,9 @@ struct InputSubsystem::Impl {
|
||||
return gcadapter;
|
||||
}
|
||||
#endif
|
||||
#ifndef __OPENORBIS__
|
||||
if (engine == udp_client->GetEngineName()) {
|
||||
return udp_client;
|
||||
}
|
||||
#endif
|
||||
#ifdef HAVE_SDL3
|
||||
if (engine == sdl->GetEngineName()) {
|
||||
return sdl;
|
||||
@@ -280,11 +271,9 @@ struct InputSubsystem::Impl {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
#ifndef __OPENORBIS__
|
||||
if (engine == udp_client->GetEngineName()) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
if (engine == tas_input->GetEngineName()) {
|
||||
return true;
|
||||
}
|
||||
@@ -311,9 +300,7 @@ struct InputSubsystem::Impl {
|
||||
#ifdef ENABLE_LIBUSB
|
||||
gcadapter->BeginConfiguration();
|
||||
#endif
|
||||
#ifndef __OPENORBIS__
|
||||
udp_client->BeginConfiguration();
|
||||
#endif
|
||||
#ifdef HAVE_SDL3
|
||||
sdl->BeginConfiguration();
|
||||
joycon->BeginConfiguration();
|
||||
@@ -329,9 +316,7 @@ struct InputSubsystem::Impl {
|
||||
#ifdef ENABLE_LIBUSB
|
||||
gcadapter->EndConfiguration();
|
||||
#endif
|
||||
#ifndef __OPENORBIS__
|
||||
udp_client->EndConfiguration();
|
||||
#endif
|
||||
#ifdef HAVE_SDL3
|
||||
sdl->EndConfiguration();
|
||||
joycon->EndConfiguration();
|
||||
@@ -356,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 __OPENORBIS__
|
||||
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;
|
||||
@@ -487,9 +470,7 @@ bool InputSubsystem::IsStickInverted(const Common::ParamPackage& params) const {
|
||||
}
|
||||
|
||||
void InputSubsystem::ReloadInputDevices() {
|
||||
#ifndef __OPENORBIS__
|
||||
impl->udp_client.get()->ReloadSockets();
|
||||
#endif
|
||||
}
|
||||
|
||||
void InputSubsystem::BeginMapping(Polling::InputType type) {
|
||||
|
||||
@@ -160,13 +160,7 @@ struct Values {
|
||||
Setting<bool> enable_discord_presence{linkage, false, "enable_discord_presence", Category::Ui};
|
||||
|
||||
// logging
|
||||
Setting<bool> show_console{linkage,
|
||||
#ifdef __OPENORBIS__
|
||||
true,
|
||||
#else
|
||||
false,
|
||||
#endif
|
||||
"showConsole", Category::Ui};
|
||||
Setting<bool> show_console{linkage, false, "showConsole", Category::Ui};
|
||||
|
||||
// Screenshots
|
||||
Setting<bool> enable_screenshot_save_as{linkage, true, "enable_screenshot_save_as",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -29,7 +32,7 @@ struct FuncTraits<ReturnType_ (*)(Args...)> {
|
||||
};
|
||||
|
||||
template <auto func, typename... Args>
|
||||
void SetDefinition(EmitContext& ctx, IR::Inst* inst, Args... args) {
|
||||
[[maybe_unused]] void SetDefinition(EmitContext& ctx, IR::Inst* inst, Args... args) {
|
||||
inst->SetDefinition<Id>(func(ctx, std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
|
||||
@@ -492,6 +492,9 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct
|
||||
if (ctx.uses_nonuniform_storage_texel_buffer) {
|
||||
ctx.AddCapability(spv::Capability::StorageTexelBufferArrayNonUniformIndexing);
|
||||
}
|
||||
if (ctx.uses_nonuniform_storage_buffer) {
|
||||
ctx.AddCapability(spv::Capability::StorageBufferArrayNonUniformIndexing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <bit>
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv.h"
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
|
||||
@@ -23,29 +21,13 @@ Id SharedPointer(EmitContext& ctx, Id offset, u32 index_offset = 0) {
|
||||
: ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, index);
|
||||
}
|
||||
|
||||
Id StorageIndex(EmitContext& ctx, const IR::Value& offset, size_t element_size) {
|
||||
if (offset.IsImmediate()) {
|
||||
const u32 imm_offset{static_cast<u32>(offset.U32() / element_size)};
|
||||
return ctx.Const(imm_offset);
|
||||
}
|
||||
const u32 shift{static_cast<u32>(std::countr_zero(element_size))};
|
||||
const Id index{ctx.Def(offset)};
|
||||
if (shift == 0) {
|
||||
return index;
|
||||
}
|
||||
const Id shift_id{ctx.Const(shift)};
|
||||
return ctx.OpShiftRightLogical(ctx.U32[1], index, shift_id);
|
||||
}
|
||||
|
||||
Id StoragePointer(EmitContext& ctx, const StorageTypeDefinition& type_def,
|
||||
Id StorageDefinitions::*member_ptr, const IR::Value& binding,
|
||||
const IR::Value& offset, size_t element_size) {
|
||||
if (!binding.IsImmediate()) {
|
||||
throw NotImplementedException("Dynamic storage buffer indexing");
|
||||
}
|
||||
const Id ssbo{ctx.ssbos[binding.U32()].*member_ptr};
|
||||
const Id index{StorageIndex(ctx, offset, element_size)};
|
||||
return ctx.OpAccessChain(type_def.element, ssbo, ctx.u32_zero_value, index);
|
||||
return ctx.StoragePointer(binding.U32(), ctx.Def(offset), type_def, static_cast<u32>(element_size), member_ptr);
|
||||
}
|
||||
|
||||
std::pair<Id, Id> AtomicArgs(EmitContext& ctx) {
|
||||
@@ -216,16 +198,14 @@ Id EmitStorageAtomicUMax32(EmitContext& ctx, const IR::Value& binding, const IR:
|
||||
|
||||
Id EmitStorageAtomicInc32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
const Id ssbo{ctx.ssbos[binding.U32()].U32};
|
||||
const Id base_index{StorageIndex(ctx, offset, sizeof(u32))};
|
||||
return ctx.OpFunctionCall(ctx.U32[1], ctx.increment_cas_ssbo, base_index, value, ssbo);
|
||||
const Id pointer{StoragePointer(ctx, ctx.storage_types.U32, &StorageDefinitions::U32, binding, offset, sizeof(u32))};
|
||||
return ctx.OpFunctionCall(ctx.U32[1], ctx.increment_cas_ssbo, pointer, value);
|
||||
}
|
||||
|
||||
Id EmitStorageAtomicDec32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
const Id ssbo{ctx.ssbos[binding.U32()].U32};
|
||||
const Id base_index{StorageIndex(ctx, offset, sizeof(u32))};
|
||||
return ctx.OpFunctionCall(ctx.U32[1], ctx.decrement_cas_ssbo, base_index, value, ssbo);
|
||||
const Id pointer{StoragePointer(ctx, ctx.storage_types.U32, &StorageDefinitions::U32, binding, offset, sizeof(u32))};
|
||||
return ctx.OpFunctionCall(ctx.U32[1], ctx.decrement_cas_ssbo, pointer, value);
|
||||
}
|
||||
|
||||
Id EmitStorageAtomicAnd32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
@@ -364,56 +344,49 @@ Id EmitStorageAtomicExchange32x2(EmitContext& ctx, const IR::Value& binding,
|
||||
|
||||
Id EmitStorageAtomicAddF32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
const Id ssbo{ctx.ssbos[binding.U32()].U32};
|
||||
const Id base_index{StorageIndex(ctx, offset, sizeof(u32))};
|
||||
return ctx.OpFunctionCall(ctx.F32[1], ctx.f32_add_cas, base_index, value, ssbo);
|
||||
const Id pointer{StoragePointer(ctx, ctx.storage_types.U32, &StorageDefinitions::U32, binding, offset, sizeof(u32))};
|
||||
return ctx.OpFunctionCall(ctx.F32[1], ctx.f32_add_cas, pointer, value);
|
||||
}
|
||||
|
||||
Id EmitStorageAtomicAddF16x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
const Id ssbo{ctx.ssbos[binding.U32()].U32};
|
||||
const Id base_index{StorageIndex(ctx, offset, sizeof(u32))};
|
||||
const Id result{ctx.OpFunctionCall(ctx.F16[2], ctx.f16x2_add_cas, base_index, value, ssbo)};
|
||||
const Id pointer{StoragePointer(ctx, ctx.storage_types.U32, &StorageDefinitions::U32, binding, offset, sizeof(u32))};
|
||||
const Id result{ctx.OpFunctionCall(ctx.F16[2], ctx.f16x2_add_cas, pointer, value)};
|
||||
return ctx.OpBitcast(ctx.U32[1], result);
|
||||
}
|
||||
|
||||
Id EmitStorageAtomicAddF32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
const Id ssbo{ctx.ssbos[binding.U32()].U32};
|
||||
const Id base_index{StorageIndex(ctx, offset, sizeof(u32))};
|
||||
const Id result{ctx.OpFunctionCall(ctx.F32[2], ctx.f32x2_add_cas, base_index, value, ssbo)};
|
||||
const Id pointer{StoragePointer(ctx, ctx.storage_types.U32, &StorageDefinitions::U32, binding, offset, sizeof(u32))};
|
||||
const Id result{ctx.OpFunctionCall(ctx.F32[2], ctx.f32x2_add_cas, pointer, value)};
|
||||
return ctx.OpPackHalf2x16(ctx.U32[1], result);
|
||||
}
|
||||
|
||||
Id EmitStorageAtomicMinF16x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
const Id ssbo{ctx.ssbos[binding.U32()].U32};
|
||||
const Id base_index{StorageIndex(ctx, offset, sizeof(u32))};
|
||||
const Id result{ctx.OpFunctionCall(ctx.F16[2], ctx.f16x2_min_cas, base_index, value, ssbo)};
|
||||
const Id pointer{StoragePointer(ctx, ctx.storage_types.U32, &StorageDefinitions::U32, binding, offset, sizeof(u32))};
|
||||
const Id result{ctx.OpFunctionCall(ctx.F16[2], ctx.f16x2_min_cas, pointer, value)};
|
||||
return ctx.OpBitcast(ctx.U32[1], result);
|
||||
}
|
||||
|
||||
Id EmitStorageAtomicMinF32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
const Id ssbo{ctx.ssbos[binding.U32()].U32};
|
||||
const Id base_index{StorageIndex(ctx, offset, sizeof(u32))};
|
||||
const Id result{ctx.OpFunctionCall(ctx.F32[2], ctx.f32x2_min_cas, base_index, value, ssbo)};
|
||||
const Id pointer{StoragePointer(ctx, ctx.storage_types.U32, &StorageDefinitions::U32, binding, offset, sizeof(u32))};
|
||||
const Id result{ctx.OpFunctionCall(ctx.F32[2], ctx.f32x2_min_cas, pointer, value)};
|
||||
return ctx.OpPackHalf2x16(ctx.U32[1], result);
|
||||
}
|
||||
|
||||
Id EmitStorageAtomicMaxF16x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
const Id ssbo{ctx.ssbos[binding.U32()].U32};
|
||||
const Id base_index{StorageIndex(ctx, offset, sizeof(u32))};
|
||||
const Id result{ctx.OpFunctionCall(ctx.F16[2], ctx.f16x2_max_cas, base_index, value, ssbo)};
|
||||
const Id pointer{StoragePointer(ctx, ctx.storage_types.U32, &StorageDefinitions::U32, binding, offset, sizeof(u32))};
|
||||
const Id result{ctx.OpFunctionCall(ctx.F16[2], ctx.f16x2_max_cas, pointer, value)};
|
||||
return ctx.OpBitcast(ctx.U32[1], result);
|
||||
}
|
||||
|
||||
Id EmitStorageAtomicMaxF32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
Id value) {
|
||||
const Id ssbo{ctx.ssbos[binding.U32()].U32};
|
||||
const Id base_index{StorageIndex(ctx, offset, sizeof(u32))};
|
||||
const Id result{ctx.OpFunctionCall(ctx.F32[2], ctx.f32x2_max_cas, base_index, value, ssbo)};
|
||||
const Id pointer{StoragePointer(ctx, ctx.storage_types.U32, &StorageDefinitions::U32, binding, offset, sizeof(u32))};
|
||||
const Id result{ctx.OpFunctionCall(ctx.F32[2], ctx.f32x2_max_cas, pointer, value)};
|
||||
return ctx.OpPackHalf2x16(ctx.U32[1], result);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,46 +4,33 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <bit>
|
||||
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv.h"
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
namespace {
|
||||
Id StorageIndex(EmitContext& ctx, const IR::Value& offset, size_t element_size,
|
||||
u32 index_offset = 0) {
|
||||
if (offset.IsImmediate()) {
|
||||
const u32 imm_offset{static_cast<u32>(offset.U32() / element_size) + index_offset};
|
||||
return ctx.Const(imm_offset);
|
||||
}
|
||||
const u32 shift{static_cast<u32>(std::countr_zero(element_size))};
|
||||
Id index{ctx.Def(offset)};
|
||||
if (shift != 0) {
|
||||
const Id shift_id{ctx.Const(shift)};
|
||||
index = ctx.OpShiftRightLogical(ctx.U32[1], index, shift_id);
|
||||
}
|
||||
Id StorageByteOffset(EmitContext& ctx, const IR::Value& offset, size_t element_size, u32 index_offset) {
|
||||
Id byte_offset{ctx.Def(offset)};
|
||||
if (index_offset != 0) {
|
||||
index = ctx.OpIAdd(ctx.U32[1], index, ctx.Const(index_offset));
|
||||
byte_offset = ctx.OpIAdd(ctx.U32[1], byte_offset, ctx.Const(static_cast<u32>(index_offset * element_size)));
|
||||
}
|
||||
return index;
|
||||
return byte_offset;
|
||||
}
|
||||
|
||||
Id StoragePointer(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
|
||||
const StorageTypeDefinition& type_def, size_t element_size,
|
||||
Id StorageDefinitions::*member_ptr, u32 index_offset = 0) {
|
||||
Id StorageDefinitions::* member_ptr, u32 index_offset = 0) {
|
||||
if (!binding.IsImmediate()) {
|
||||
throw NotImplementedException("Dynamic storage buffer indexing");
|
||||
}
|
||||
const Id ssbo{ctx.ssbos[binding.U32()].*member_ptr};
|
||||
const Id index{StorageIndex(ctx, offset, element_size, index_offset)};
|
||||
return ctx.OpAccessChain(type_def.element, ssbo, ctx.u32_zero_value, index);
|
||||
const Id byte_offset{StorageByteOffset(ctx, offset, element_size, index_offset)};
|
||||
return ctx.StoragePointer(binding.U32(), byte_offset, type_def, static_cast<u32>(element_size), member_ptr);
|
||||
}
|
||||
|
||||
Id LoadStorage(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, Id result_type,
|
||||
const StorageTypeDefinition& type_def, size_t element_size,
|
||||
Id StorageDefinitions::*member_ptr, u32 index_offset = 0) {
|
||||
Id StorageDefinitions::* member_ptr, u32 index_offset = 0) {
|
||||
const Id pointer{
|
||||
StoragePointer(ctx, binding, offset, type_def, element_size, member_ptr, index_offset)};
|
||||
return ctx.OpLoad(result_type, pointer);
|
||||
@@ -57,7 +44,7 @@ Id LoadStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& of
|
||||
|
||||
void WriteStorage(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, Id value,
|
||||
const StorageTypeDefinition& type_def, size_t element_size,
|
||||
Id StorageDefinitions::*member_ptr, u32 index_offset = 0) {
|
||||
Id StorageDefinitions::* member_ptr, u32 index_offset = 0) {
|
||||
const Id pointer{
|
||||
StoragePointer(ctx, binding, offset, type_def, element_size, member_ptr, index_offset)};
|
||||
ctx.OpStore(pointer, value);
|
||||
|
||||
@@ -299,7 +299,7 @@ void DefineConstBuffers(EmitContext& ctx, const Info& info, Id UniformDefinition
|
||||
}
|
||||
|
||||
void DefineSsbos(EmitContext& ctx, StorageTypeDefinition& type_def,
|
||||
Id StorageDefinitions::*member_type, const Info& info, u32 binding, Id type,
|
||||
Id StorageDefinitions::* member_type, const Info& info, u32 binding, Id type,
|
||||
u32 stride) {
|
||||
const Id array_type{ctx.TypeRuntimeArray(type)};
|
||||
ctx.Decorate(array_type, spv::Decoration::ArrayStride, stride);
|
||||
@@ -309,23 +309,27 @@ void DefineSsbos(EmitContext& ctx, StorageTypeDefinition& type_def,
|
||||
ctx.MemberDecorate(struct_type, 0, spv::Decoration::Offset, 0U);
|
||||
|
||||
const Id struct_pointer{ctx.TypePointer(spv::StorageClass::StorageBuffer, struct_type)};
|
||||
type_def.array = struct_pointer;
|
||||
type_def.element = ctx.TypePointer(spv::StorageClass::StorageBuffer, type);
|
||||
|
||||
u32 index{};
|
||||
for (const StorageBufferDescriptor& desc : info.storage_buffers_descriptors) {
|
||||
const Id id{ctx.AddGlobalVariable(struct_pointer, spv::StorageClass::StorageBuffer)};
|
||||
const Id variable_type{[&] {
|
||||
if (desc.count == 1) {
|
||||
return struct_pointer;
|
||||
}
|
||||
const Id descriptor_array{ctx.TypeArray(struct_type, ctx.Const(desc.count))};
|
||||
return ctx.TypePointer(spv::StorageClass::StorageBuffer, descriptor_array);
|
||||
}()};
|
||||
const Id id{ctx.AddGlobalVariable(variable_type, spv::StorageClass::StorageBuffer)};
|
||||
ctx.Decorate(id, spv::Decoration::Binding, binding);
|
||||
ctx.Decorate(id, spv::Decoration::DescriptorSet, 0U);
|
||||
ctx.Name(id, fmt::format("ssbo{}", index));
|
||||
if (ctx.profile.supported_spirv >= 0x00010400) {
|
||||
ctx.interfaces.push_back(id);
|
||||
}
|
||||
for (size_t i = 0; i < desc.count; ++i) {
|
||||
ctx.ssbos[index + i].*member_type = id;
|
||||
}
|
||||
index += desc.count;
|
||||
binding += desc.count;
|
||||
ctx.ssbos[index].*member_type = id;
|
||||
++index;
|
||||
++binding;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -368,8 +372,7 @@ Id CasFunction(EmitContext& ctx, Operation operation, Id value_type) {
|
||||
return func;
|
||||
}
|
||||
|
||||
Id CasLoop(EmitContext& ctx, Operation operation, Id array_pointer, Id element_pointer,
|
||||
Id value_type, Id memory_type, spv::Scope scope) {
|
||||
Id CasLoop(EmitContext& ctx, Operation operation, Id element_pointer, Id value_type, Id memory_type, spv::Scope scope) {
|
||||
const bool is_shared{scope == spv::Scope::Workgroup};
|
||||
const bool is_struct{!is_shared || ctx.uses_explicit_workgroup_layout};
|
||||
const Id cas_func{CasFunction(ctx, operation, value_type)};
|
||||
@@ -379,14 +382,12 @@ Id CasLoop(EmitContext& ctx, Operation operation, Id array_pointer, Id element_p
|
||||
const Id loop_header{ctx.OpLabel()};
|
||||
const Id continue_block{ctx.OpLabel()};
|
||||
const Id merge_block{ctx.OpLabel()};
|
||||
const Id func_type{is_shared
|
||||
? ctx.TypeFunction(value_type, ctx.U32[1], value_type)
|
||||
: ctx.TypeFunction(value_type, ctx.U32[1], value_type, array_pointer)};
|
||||
const Id func_type{is_shared ? ctx.TypeFunction(value_type, ctx.U32[1], value_type)
|
||||
: ctx.TypeFunction(value_type, element_pointer, value_type)};
|
||||
|
||||
const Id func{ctx.OpFunction(value_type, spv::FunctionControlMask::MaskNone, func_type)};
|
||||
const Id index{ctx.OpFunctionParameter(ctx.U32[1])};
|
||||
const Id address{ctx.OpFunctionParameter(is_shared ? ctx.U32[1] : element_pointer)};
|
||||
const Id op_b{ctx.OpFunctionParameter(value_type)};
|
||||
const Id base{is_shared ? ctx.shared_memory_u32 : ctx.OpFunctionParameter(array_pointer)};
|
||||
ctx.AddLabel();
|
||||
ctx.OpBranch(loop_header);
|
||||
ctx.AddLabel(loop_header);
|
||||
@@ -395,8 +396,13 @@ Id CasLoop(EmitContext& ctx, Operation operation, Id array_pointer, Id element_p
|
||||
ctx.OpBranch(continue_block);
|
||||
|
||||
ctx.AddLabel(continue_block);
|
||||
const Id word_pointer{is_struct ? ctx.OpAccessChain(element_pointer, base, zero, index)
|
||||
: ctx.OpAccessChain(element_pointer, base, index)};
|
||||
const Id word_pointer{[&] {
|
||||
if (!is_shared) {
|
||||
return address;
|
||||
}
|
||||
return is_struct ? ctx.OpAccessChain(element_pointer, ctx.shared_memory_u32, zero, address)
|
||||
: ctx.OpAccessChain(element_pointer, ctx.shared_memory_u32, address);
|
||||
}()};
|
||||
if (value_type.value == ctx.F32[2].value) {
|
||||
const Id u32_value{ctx.OpLoad(ctx.U32[1], word_pointer)};
|
||||
const Id value{ctx.OpUnpackHalf2x16(ctx.F32[2], u32_value)};
|
||||
@@ -480,6 +486,7 @@ EmitContext::EmitContext(const Profile& profile_, const RuntimeInfo& runtime_inf
|
||||
DefineSharedMemoryFunctions(program);
|
||||
DefineConstantBuffers(program.info, uniform_binding);
|
||||
DefineConstantBufferIndirectFunctions(program.info);
|
||||
DefineStorageBufferMappings(program.info, storage_binding);
|
||||
DefineStorageBuffers(program.info, storage_binding);
|
||||
DefineTextureBuffers(program.info, texture_binding);
|
||||
DefineImageBuffers(program.info, image_binding);
|
||||
@@ -532,6 +539,36 @@ Id EmitContext::BitOffset16(const IR::Value& offset) {
|
||||
return OpBitwiseAnd(U32[1], OpShiftLeftLogical(U32[1], Def(offset), Const(3u)), Const(16u));
|
||||
}
|
||||
|
||||
Id EmitContext::StoragePointer(u32 binding, Id byte_offset, const StorageTypeDefinition& type_def,
|
||||
u32 element_size, Id StorageDefinitions::* member_ptr) {
|
||||
const Id ssbo{ssbos[binding].*member_ptr};
|
||||
const u32 segment_count{storage_buffer_mapping_counts[binding]};
|
||||
if (segment_count <= 1) {
|
||||
const Id index{
|
||||
element_size == 1
|
||||
? byte_offset
|
||||
: OpShiftRightLogical(U32[1], byte_offset, Const(static_cast<u32>(std::countr_zero(element_size))))};
|
||||
return OpAccessChain(type_def.element, ssbo, u32_zero_value, index);
|
||||
}
|
||||
|
||||
const Id mapped{OpFunctionCall(U32[2], storage_buffer_map_func,
|
||||
Const(storage_buffer_mapping_bases[binding]),
|
||||
Const(segment_count), byte_offset)};
|
||||
const Id segment{OpCompositeExtract(U32[1], mapped, 0U)};
|
||||
const Id local_offset{OpCompositeExtract(U32[1], mapped, 1U)};
|
||||
const Id index{
|
||||
element_size == 1
|
||||
? local_offset
|
||||
: OpShiftRightLogical(U32[1], local_offset, Const(static_cast<u32>(std::countr_zero(element_size))))};
|
||||
Decorate(segment, spv::Decoration::NonUniform);
|
||||
non_uniform_ids.insert(segment.value);
|
||||
const Id pointer{OpAccessChain(type_def.element, ssbo, segment, u32_zero_value, index)};
|
||||
Decorate(pointer, spv::Decoration::NonUniform);
|
||||
non_uniform_ids.insert(pointer.value);
|
||||
uses_nonuniform_storage_buffer = true;
|
||||
return pointer;
|
||||
}
|
||||
|
||||
void EmitContext::DefineCommonTypes(const Info& info) {
|
||||
void_id = TypeVoid();
|
||||
|
||||
@@ -696,12 +733,10 @@ void EmitContext::DefineSharedMemory(const IR::Program& program) {
|
||||
|
||||
void EmitContext::DefineSharedMemoryFunctions(const IR::Program& program) {
|
||||
if (program.info.uses_shared_increment) {
|
||||
increment_cas_shared = CasLoop(*this, Operation::Increment, shared_memory_u32_type,
|
||||
shared_u32, U32[1], U32[1], spv::Scope::Workgroup);
|
||||
increment_cas_shared = CasLoop(*this, Operation::Increment, shared_u32, U32[1], U32[1], spv::Scope::Workgroup);
|
||||
}
|
||||
if (program.info.uses_shared_decrement) {
|
||||
decrement_cas_shared = CasLoop(*this, Operation::Decrement, shared_memory_u32_type,
|
||||
shared_u32, U32[1], U32[1], spv::Scope::Workgroup);
|
||||
decrement_cas_shared = CasLoop(*this, Operation::Decrement, shared_u32, U32[1], U32[1], spv::Scope::Workgroup);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -945,8 +980,7 @@ void EmitContext::DefineGlobalMemoryFunctions(const Info& info) {
|
||||
}
|
||||
using DefPtr = Id StorageDefinitions::*;
|
||||
const Id zero{u32_zero_value};
|
||||
const auto define_body{[&](DefPtr ssbo_member, Id addr, Id element_pointer, u32 shift,
|
||||
auto&& callback) {
|
||||
const auto define_body{[&](DefPtr ssbo_member, Id addr, const StorageTypeDefinition& type_def, u32 shift, auto&& callback) {
|
||||
AddLabel();
|
||||
const size_t num_buffers{info.storage_buffers_descriptors.size()};
|
||||
for (size_t index = 0; index < num_buffers; ++index) {
|
||||
@@ -973,30 +1007,28 @@ void EmitContext::DefineGlobalMemoryFunctions(const Info& info) {
|
||||
OpSelectionMerge(else_label, spv::SelectionControlMask::MaskNone);
|
||||
OpBranchConditional(cond, then_label, else_label);
|
||||
AddLabel(then_label);
|
||||
const Id ssbo_id{ssbos[index].*ssbo_member};
|
||||
const Id ssbo_offset{OpUConvert(U32[1], OpISub(U64, addr, ssbo_addr))};
|
||||
const Id ssbo_index{OpShiftRightLogical(U32[1], ssbo_offset, Const(shift))};
|
||||
const Id ssbo_pointer{OpAccessChain(element_pointer, ssbo_id, zero, ssbo_index)};
|
||||
const Id ssbo_pointer{StoragePointer(static_cast<u32>(index), ssbo_offset, type_def, 1U << shift, ssbo_member)};
|
||||
callback(ssbo_pointer);
|
||||
AddLabel(else_label);
|
||||
}
|
||||
}};
|
||||
const auto define_load{[&](DefPtr ssbo_member, Id element_pointer, Id type, u32 shift) {
|
||||
const Id function_type{TypeFunction(type, U64)};
|
||||
const Id func_id{OpFunction(type, spv::FunctionControlMask::MaskNone, function_type)};
|
||||
const Id addr{OpFunctionParameter(U64)};
|
||||
define_body(ssbo_member, addr, element_pointer, shift,
|
||||
[&](Id ssbo_pointer) { OpReturnValue(OpLoad(type, ssbo_pointer)); });
|
||||
OpReturnValue(ConstantNull(type));
|
||||
OpFunctionEnd();
|
||||
return func_id;
|
||||
}};
|
||||
const auto define_write{[&](DefPtr ssbo_member, Id element_pointer, Id type, u32 shift) {
|
||||
const auto define_load{
|
||||
[&](DefPtr ssbo_member, const StorageTypeDefinition& type_def, Id type, u32 shift) {
|
||||
const Id function_type{TypeFunction(type, U64)};
|
||||
const Id func_id{OpFunction(type, spv::FunctionControlMask::MaskNone, function_type)};
|
||||
const Id addr{OpFunctionParameter(U64)};
|
||||
define_body(ssbo_member, addr, type_def, shift, [&](Id ssbo_pointer) { OpReturnValue(OpLoad(type, ssbo_pointer)); });
|
||||
OpReturnValue(ConstantNull(type));
|
||||
OpFunctionEnd();
|
||||
return func_id;
|
||||
}};
|
||||
const auto define_write{[&](DefPtr ssbo_member, const StorageTypeDefinition& type_def, Id type, u32 shift) {
|
||||
const Id function_type{TypeFunction(void_id, U64, type)};
|
||||
const Id func_id{OpFunction(void_id, spv::FunctionControlMask::MaskNone, function_type)};
|
||||
const Id addr{OpFunctionParameter(U64)};
|
||||
const Id data{OpFunctionParameter(type)};
|
||||
define_body(ssbo_member, addr, element_pointer, shift, [&](Id ssbo_pointer) {
|
||||
define_body(ssbo_member, addr, type_def, shift, [&](Id ssbo_pointer) {
|
||||
OpStore(ssbo_pointer, data);
|
||||
OpReturn();
|
||||
});
|
||||
@@ -1006,10 +1038,9 @@ void EmitContext::DefineGlobalMemoryFunctions(const Info& info) {
|
||||
}};
|
||||
const auto define{
|
||||
[&](DefPtr ssbo_member, const StorageTypeDefinition& type_def, Id type, size_t size) {
|
||||
const Id element_type{type_def.element};
|
||||
const u32 shift{static_cast<u32>(std::countr_zero(size))};
|
||||
const Id load_func{define_load(ssbo_member, element_type, type, shift)};
|
||||
const Id write_func{define_write(ssbo_member, element_type, type, shift)};
|
||||
const Id load_func{define_load(ssbo_member, type_def, type, shift)};
|
||||
const Id write_func{define_write(ssbo_member, type_def, type, shift)};
|
||||
return std::make_pair(load_func, write_func);
|
||||
}};
|
||||
std::tie(load_global_func_u32, write_global_func_u32) =
|
||||
@@ -1228,6 +1259,79 @@ void EmitContext::DefineConstantBufferIndirectFunctions(const Info& info) {
|
||||
}
|
||||
}
|
||||
|
||||
void EmitContext::DefineStorageBufferMappings(const Info& info, u32& binding) {
|
||||
if (!UsesStorageBufferMappings(info)) {
|
||||
return;
|
||||
}
|
||||
ASSERT(profile.support_storage_buffer_array_nonuniform_indexing);
|
||||
AddExtension("SPV_KHR_storage_buffer_storage_class");
|
||||
|
||||
const u32 num_entries{NumDescriptors(info.storage_buffers_descriptors)};
|
||||
const Id array_type{TypeArray(U32[1], Const(num_entries))};
|
||||
Decorate(array_type, spv::Decoration::ArrayStride, sizeof(u32));
|
||||
const Id struct_type{TypeStruct(array_type)};
|
||||
Decorate(struct_type, spv::Decoration::Block);
|
||||
MemberName(struct_type, 0, "segment_sizes");
|
||||
MemberDecorate(struct_type, 0, spv::Decoration::Offset, 0U);
|
||||
const Id pointer_type{TypePointer(spv::StorageClass::StorageBuffer, struct_type)};
|
||||
storage_buffer_mapping_u32 = TypePointer(spv::StorageClass::StorageBuffer, U32[1]);
|
||||
storage_buffer_mapping = AddGlobalVariable(pointer_type, spv::StorageClass::StorageBuffer);
|
||||
Decorate(storage_buffer_mapping, spv::Decoration::Binding, binding++);
|
||||
Decorate(storage_buffer_mapping, spv::Decoration::DescriptorSet, 0U);
|
||||
Name(storage_buffer_mapping, "storage_buffer_mapping");
|
||||
//Starting with version 1.4... (https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html)
|
||||
if (profile.supported_spirv >= 0x00010400) {
|
||||
interfaces.push_back(storage_buffer_mapping);
|
||||
}
|
||||
|
||||
u32 mapping_base{};
|
||||
for (u32 index = 0; index < info.storage_buffers_descriptors.size(); ++index) {
|
||||
const StorageBufferDescriptor& desc = info.storage_buffers_descriptors[index];
|
||||
storage_buffer_mapping_bases[index] = mapping_base;
|
||||
storage_buffer_mapping_counts[index] = desc.count;
|
||||
mapping_base += desc.count;
|
||||
}
|
||||
|
||||
const Id function_type{TypeFunction(U32[2], U32[1], U32[1], U32[1])};
|
||||
storage_buffer_map_func = OpFunction(U32[2], spv::FunctionControlMask::MaskNone, function_type);
|
||||
const Id base{OpFunctionParameter(U32[1])};
|
||||
const Id count{OpFunctionParameter(U32[1])};
|
||||
const Id byte_offset{OpFunctionParameter(U32[1])};
|
||||
const Id index_pointer_type{TypePointer(spv::StorageClass::Function, U32[1])};
|
||||
const Id loop_header{OpLabel()};
|
||||
const Id continue_block{OpLabel()};
|
||||
const Id merge_block{OpLabel()};
|
||||
AddLabel();
|
||||
const Id segment_var{AddLocalVariable(index_pointer_type, spv::StorageClass::Function)};
|
||||
const Id offset_var{AddLocalVariable(index_pointer_type, spv::StorageClass::Function)};
|
||||
OpStore(segment_var, u32_zero_value);
|
||||
OpStore(offset_var, byte_offset);
|
||||
OpBranch(loop_header);
|
||||
|
||||
AddLabel(loop_header);
|
||||
const Id segment{OpLoad(U32[1], segment_var)};
|
||||
const Id local_offset{OpLoad(U32[1], offset_var)};
|
||||
const Id mapping_index{OpIAdd(U32[1], base, segment)};
|
||||
const Id size_pointer{OpAccessChain(storage_buffer_mapping_u32, storage_buffer_mapping, u32_zero_value, mapping_index)};
|
||||
const Id segment_size{OpLoad(U32[1], size_pointer)};
|
||||
const Id fits{OpULessThan(U1, local_offset, segment_size)};
|
||||
const Id last_segment{OpISub(U32[1], count, Const(1U))};
|
||||
const Id is_last{OpIEqual(U1, segment, last_segment)};
|
||||
const Id found{OpLogicalOr(U1, fits, is_last)};
|
||||
OpLoopMerge(merge_block, continue_block, spv::LoopControlMask::MaskNone);
|
||||
OpBranchConditional(found, merge_block, continue_block);
|
||||
|
||||
AddLabel(continue_block);
|
||||
OpStore(offset_var, OpISub(U32[1], local_offset, segment_size));
|
||||
OpStore(segment_var, OpIAdd(U32[1], segment, Const(1U)));
|
||||
OpBranch(loop_header);
|
||||
|
||||
AddLabel(merge_block);
|
||||
OpReturnValue(OpCompositeConstruct(U32[2], segment, local_offset));
|
||||
OpFunctionEnd();
|
||||
Name(storage_buffer_map_func, "map_storage_buffer");
|
||||
}
|
||||
|
||||
void EmitContext::DefineStorageBuffers(const Info& info, u32& binding) {
|
||||
if (info.storage_buffers_descriptors.empty()) {
|
||||
return;
|
||||
@@ -1271,9 +1375,7 @@ void EmitContext::DefineStorageBuffers(const Info& info, u32& binding) {
|
||||
DefineSsbos(*this, storage_types.U32x4, &StorageDefinitions::U32x4, info, binding, U32[4],
|
||||
sizeof(u32[4]));
|
||||
}
|
||||
for (const StorageBufferDescriptor& desc : info.storage_buffers_descriptors) {
|
||||
binding += desc.count;
|
||||
}
|
||||
binding += static_cast<u32>(info.storage_buffers_descriptors.size());
|
||||
const bool needs_function{
|
||||
info.uses_global_increment || info.uses_global_decrement || info.uses_atomic_f32_add ||
|
||||
info.uses_atomic_f16x2_add || info.uses_atomic_f16x2_min || info.uses_atomic_f16x2_max ||
|
||||
@@ -1282,40 +1384,31 @@ void EmitContext::DefineStorageBuffers(const Info& info, u32& binding) {
|
||||
AddCapability(spv::Capability::VariablePointersStorageBuffer);
|
||||
}
|
||||
if (info.uses_global_increment) {
|
||||
increment_cas_ssbo = CasLoop(*this, Operation::Increment, storage_types.U32.array,
|
||||
storage_types.U32.element, U32[1], U32[1], spv::Scope::Device);
|
||||
increment_cas_ssbo = CasLoop(*this, Operation::Increment, storage_types.U32.element, U32[1], U32[1], spv::Scope::Device);
|
||||
}
|
||||
if (info.uses_global_decrement) {
|
||||
decrement_cas_ssbo = CasLoop(*this, Operation::Decrement, storage_types.U32.array,
|
||||
storage_types.U32.element, U32[1], U32[1], spv::Scope::Device);
|
||||
decrement_cas_ssbo = CasLoop(*this, Operation::Decrement, storage_types.U32.element, U32[1], U32[1], spv::Scope::Device);
|
||||
}
|
||||
if (info.uses_atomic_f32_add) {
|
||||
f32_add_cas = CasLoop(*this, Operation::FPAdd, storage_types.U32.array,
|
||||
storage_types.U32.element, F32[1], U32[1], spv::Scope::Device);
|
||||
f32_add_cas = CasLoop(*this, Operation::FPAdd, storage_types.U32.element, F32[1], U32[1], spv::Scope::Device);
|
||||
}
|
||||
if (info.uses_atomic_f16x2_add) {
|
||||
f16x2_add_cas = CasLoop(*this, Operation::FPAdd, storage_types.U32.array,
|
||||
storage_types.U32.element, F16[2], F16[2], spv::Scope::Device);
|
||||
f16x2_add_cas = CasLoop(*this, Operation::FPAdd, storage_types.U32.element, F16[2], F16[2], spv::Scope::Device);
|
||||
}
|
||||
if (info.uses_atomic_f16x2_min) {
|
||||
f16x2_min_cas = CasLoop(*this, Operation::FPMin, storage_types.U32.array,
|
||||
storage_types.U32.element, F16[2], F16[2], spv::Scope::Device);
|
||||
f16x2_min_cas = CasLoop(*this, Operation::FPMin, storage_types.U32.element, F16[2], F16[2], spv::Scope::Device);
|
||||
}
|
||||
if (info.uses_atomic_f16x2_max) {
|
||||
f16x2_max_cas = CasLoop(*this, Operation::FPMax, storage_types.U32.array,
|
||||
storage_types.U32.element, F16[2], F16[2], spv::Scope::Device);
|
||||
f16x2_max_cas = CasLoop(*this, Operation::FPMax, storage_types.U32.element, F16[2], F16[2], spv::Scope::Device);
|
||||
}
|
||||
if (info.uses_atomic_f32x2_add) {
|
||||
f32x2_add_cas = CasLoop(*this, Operation::FPAdd, storage_types.U32.array,
|
||||
storage_types.U32.element, F32[2], F32[2], spv::Scope::Device);
|
||||
f32x2_add_cas = CasLoop(*this, Operation::FPAdd, storage_types.U32.element, F32[2], F32[2], spv::Scope::Device);
|
||||
}
|
||||
if (info.uses_atomic_f32x2_min) {
|
||||
f32x2_min_cas = CasLoop(*this, Operation::FPMin, storage_types.U32.array,
|
||||
storage_types.U32.element, F32[2], F32[2], spv::Scope::Device);
|
||||
f32x2_min_cas = CasLoop(*this, Operation::FPMin, storage_types.U32.element, F32[2], F32[2], spv::Scope::Device);
|
||||
}
|
||||
if (info.uses_atomic_f32x2_max) {
|
||||
f32x2_max_cas = CasLoop(*this, Operation::FPMax, storage_types.U32.array,
|
||||
storage_types.U32.element, F32[2], F32[2], spv::Scope::Device);
|
||||
f32x2_max_cas = CasLoop(*this, Operation::FPMax, storage_types.U32.element, F32[2], F32[2], spv::Scope::Device);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -114,7 +114,6 @@ struct UniformDefinitions {
|
||||
};
|
||||
|
||||
struct StorageTypeDefinition {
|
||||
Id array{};
|
||||
Id element{};
|
||||
};
|
||||
|
||||
@@ -173,6 +172,8 @@ public:
|
||||
[[nodiscard]] Id BitOffset8(const IR::Value& offset);
|
||||
[[nodiscard]] Id BitOffset16(const IR::Value& offset);
|
||||
|
||||
[[nodiscard]] Id StoragePointer(u32 binding, Id byte_offset, const StorageTypeDefinition& type_def, u32 element_size, Id StorageDefinitions::*member_ptr);
|
||||
|
||||
Id Const(u32 value) {
|
||||
return Constant(U32[1], value);
|
||||
}
|
||||
@@ -257,6 +258,11 @@ public:
|
||||
|
||||
std::array<UniformDefinitions, Info::MAX_CBUFS> cbufs{};
|
||||
std::array<StorageDefinitions, Info::MAX_SSBOS> ssbos{};
|
||||
std::array<u32, Info::MAX_SSBOS> storage_buffer_mapping_bases{};
|
||||
std::array<u32, Info::MAX_SSBOS> storage_buffer_mapping_counts{};
|
||||
Id storage_buffer_mapping{};
|
||||
Id storage_buffer_mapping_u32{};
|
||||
Id storage_buffer_map_func{};
|
||||
std::vector<TextureBufferDefinition> texture_buffers;
|
||||
std::vector<ImageBufferDefinition> image_buffers;
|
||||
std::vector<TextureDefinition> textures;
|
||||
@@ -376,6 +382,7 @@ public:
|
||||
bool uses_nonuniform_storage_image{};
|
||||
bool uses_nonuniform_uniform_texel_buffer{};
|
||||
bool uses_nonuniform_storage_texel_buffer{};
|
||||
bool uses_nonuniform_storage_buffer{};
|
||||
|
||||
private:
|
||||
void DefineCommonTypes(const Info& info);
|
||||
@@ -386,6 +393,7 @@ private:
|
||||
void DefineSharedMemoryFunctions(const IR::Program& program);
|
||||
void DefineConstantBuffers(const Info& info, u32& binding);
|
||||
void DefineConstantBufferIndirectFunctions(const Info& info);
|
||||
void DefineStorageBufferMappings(const Info& info, u32& binding);
|
||||
void DefineStorageBuffers(const Info& info, u32& binding);
|
||||
void DefineTextureBuffers(const Info& info, u32& binding);
|
||||
void DefineImageBuffers(const Info& info, u32& binding);
|
||||
|
||||
@@ -132,6 +132,14 @@ void AddNVNStorageBuffers(IR::Program& program) {
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureStorageBufferMappings(IR::Program& program, const HostTranslateInfo& host_info) {
|
||||
// https://docs.vulkan.org/guide/latest/descriptor_arrays.html
|
||||
// will be needed: descriptor array elements represent physical spans of one guest virtual buffer.
|
||||
for (StorageBufferDescriptor& desc : program.info.storage_buffers_descriptors) {
|
||||
desc.count = host_info.storage_buffer_segment_count;
|
||||
}
|
||||
}
|
||||
|
||||
using IR::IsLegacyAttribute; //rescoped to attribute.h to make it visible in load_store_attribute.cpp IPA
|
||||
|
||||
std::map<IR::Attribute, IR::Attribute> GenerateLegacyToGenericMappings(
|
||||
@@ -299,6 +307,7 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
|
||||
Optimization::PositionPass(env, program);
|
||||
|
||||
Optimization::GlobalMemoryToStorageBufferPass(program, normalized_host_info);
|
||||
ConfigureStorageBufferMappings(program, normalized_host_info);
|
||||
Optimization::TexturePass(env, program, normalized_host_info);
|
||||
|
||||
if (Settings::values.resolution_info.active || Settings::values.rescale_hack.GetValue()) {
|
||||
@@ -314,6 +323,7 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
|
||||
|
||||
CollectInterpolationInfo(env, program);
|
||||
AddNVNStorageBuffers(program);
|
||||
ConfigureStorageBufferMappings(program, normalized_host_info);
|
||||
return program;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Shader {
|
||||
@@ -19,6 +20,7 @@ struct HostTranslateInfo {
|
||||
|
||||
u64 min_ssbo_alignment{}; ///< Minimum alignment supported by the device for SSBOs
|
||||
u32 max_per_stage_descriptor_sampled_images{}; ///< maximum sampled descriptors per stage
|
||||
u32 max_per_stage_descriptor_storage_buffers{}; ///< maximum storage descriptors per stage
|
||||
u32 max_per_stage_resources{}; ///< maximum resources per stage
|
||||
u32 max_descriptor_set_samplers{};
|
||||
u32 max_descriptor_set_uniform_buffers{};
|
||||
@@ -38,12 +40,14 @@ struct HostTranslateInfo {
|
||||
///< passthrough shaders
|
||||
bool support_conditional_barrier{}; ///< True when the device supports barriers in conditional
|
||||
///< control flow
|
||||
u32 storage_buffer_segment_count{1}; ///< Physical ranges available to each guest SSBO
|
||||
|
||||
void ApplyDescriptorLimitPolicy() noexcept {
|
||||
if (min_ssbo_alignment == 0) {
|
||||
min_ssbo_alignment = 1;
|
||||
}
|
||||
ApplyDescriptorLimitFallback(max_per_stage_descriptor_sampled_images);
|
||||
ApplyDescriptorLimitFallback(max_per_stage_descriptor_storage_buffers);
|
||||
ApplyDescriptorLimitFallback(max_per_stage_resources);
|
||||
ApplyDescriptorLimitFallback(max_descriptor_set_samplers);
|
||||
ApplyDescriptorLimitFallback(max_descriptor_set_uniform_buffers);
|
||||
@@ -53,6 +57,7 @@ struct HostTranslateInfo {
|
||||
ApplyDescriptorLimitFallback(max_descriptor_set_sampled_images);
|
||||
ApplyDescriptorLimitFallback(max_descriptor_set_storage_images);
|
||||
ApplyDescriptorLimitFallback(max_descriptor_set_input_attachements);
|
||||
storage_buffer_segment_count = (std::max)(storage_buffer_segment_count, 1U);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -64,6 +64,7 @@ struct Profile {
|
||||
bool support_storage_image_array_nonuniform_indexing{};
|
||||
bool support_uniform_texel_buffer_array_nonuniform_indexing{};
|
||||
bool support_storage_texel_buffer_array_nonuniform_indexing{};
|
||||
bool support_storage_buffer_array_nonuniform_indexing{};
|
||||
|
||||
bool warp_size_potentially_larger_than_guest{};
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <bitset>
|
||||
#include <map>
|
||||
@@ -340,6 +341,10 @@ struct Info {
|
||||
ImageDescriptors image_descriptors;
|
||||
};
|
||||
|
||||
[[nodiscard]] inline bool UsesStorageBufferMappings(const Info& info) noexcept {
|
||||
return std::ranges::any_of(info.storage_buffers_descriptors, [](const auto& desc) { return desc.count > 1; });
|
||||
}
|
||||
|
||||
template <typename Descriptors>
|
||||
u32 NumDescriptors(const Descriptors& descriptors) {
|
||||
u32 num{};
|
||||
|
||||
@@ -37,8 +37,3 @@ if (NOT MSVC)
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-unused-parameter>
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-missing-field-initializers>)
|
||||
endif()
|
||||
|
||||
if (OPENORBIS)
|
||||
target_link_libraries(tests PRIVATE SceVideoOut SceAudioOut ScePad SceSystemService ps4sup)
|
||||
create_ps4_eboot(tests tests IV0000-BREW00090_00-DYNARMICTS000000)
|
||||
endif()
|
||||
|
||||
@@ -209,74 +209,3 @@ TEST_CASE("HostMemory: Partial sparse middle unmap and check bindings", "[common
|
||||
REQUIRE(ptr[0x0000] == 19);
|
||||
REQUIRE(ptr[0x3fff] == 12);
|
||||
}
|
||||
|
||||
TEST_CASE("HostMemory: Nieche allocation", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
REQUIRE(mem.BackingBasePointer() != nullptr);
|
||||
mem.Map(0x0000, 0, 0x20000, PERMS, HEAP);
|
||||
mem.Unmap(0x0000, 0x4000, HEAP);
|
||||
mem.Map(0x1000, 0, 0x2000, PERMS, HEAP);
|
||||
mem.Map(0x3000, 0, 0x1000, PERMS, HEAP);
|
||||
mem.Map(0, 0, 0x1000, PERMS, HEAP);
|
||||
}
|
||||
|
||||
TEST_CASE("HostMemory: Full unmap", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
REQUIRE(mem.BackingBasePointer() != nullptr);
|
||||
mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
|
||||
mem.Unmap(0x8000, 0x4000, HEAP);
|
||||
mem.Map(0x6000, 0, 0x16000, PERMS, HEAP);
|
||||
}
|
||||
|
||||
TEST_CASE("HostMemory: Right out of bounds unmap", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
REQUIRE(mem.BackingBasePointer() != nullptr);
|
||||
mem.Map(0x0000, 0, 0x4000, PERMS, HEAP);
|
||||
mem.Unmap(0x2000, 0x4000, HEAP);
|
||||
mem.Map(0x2000, 0x80000, 0x4000, PERMS, HEAP);
|
||||
}
|
||||
|
||||
TEST_CASE("HostMemory: Left out of bounds unmap", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
REQUIRE(mem.BackingBasePointer() != nullptr);
|
||||
mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
|
||||
mem.Unmap(0x6000, 0x4000, HEAP);
|
||||
mem.Map(0x8000, 0, 0x2000, PERMS, HEAP);
|
||||
}
|
||||
|
||||
TEST_CASE("HostMemory: Multiple placeholder unmap", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
REQUIRE(mem.BackingBasePointer() != nullptr);
|
||||
mem.Map(0x0000, 0, 0x4000, PERMS, HEAP);
|
||||
mem.Map(0x4000, 0, 0x1b000, PERMS, HEAP);
|
||||
mem.Unmap(0x3000, 0x1c000, HEAP);
|
||||
mem.Map(0x3000, 0, 0x20000, PERMS, HEAP);
|
||||
}
|
||||
|
||||
TEST_CASE("HostMemory: Unmap between placeholders", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
REQUIRE(mem.BackingBasePointer() != nullptr);
|
||||
mem.Map(0x0000, 0, 0x4000, PERMS, HEAP);
|
||||
mem.Map(0x4000, 0, 0x4000, PERMS, HEAP);
|
||||
mem.Unmap(0x2000, 0x4000, HEAP);
|
||||
mem.Map(0x2000, 0, 0x4000, PERMS, HEAP);
|
||||
}
|
||||
|
||||
TEST_CASE("HostMemory: Unmap to origin", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
REQUIRE(mem.BackingBasePointer() != nullptr);
|
||||
mem.Map(0x4000, 0, 0x4000, PERMS, HEAP);
|
||||
mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
|
||||
mem.Unmap(0x4000, 0x4000, HEAP);
|
||||
mem.Map(0, 0, 0x4000, PERMS, HEAP);
|
||||
mem.Map(0x4000, 0, 0x4000, PERMS, HEAP);
|
||||
}
|
||||
|
||||
TEST_CASE("HostMemory: Unmap to right", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
REQUIRE(mem.BackingBasePointer() != nullptr);
|
||||
mem.Map(0x4000, 0, 0x4000, PERMS, HEAP);
|
||||
mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
|
||||
mem.Unmap(0x8000, 0x4000, HEAP);
|
||||
mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
|
||||
}
|
||||
|
||||
@@ -15,8 +15,7 @@
|
||||
#include "input_common/drivers/udp_client.h"
|
||||
#include "input_common/helpers/udp_protocol.h"
|
||||
|
||||
// PS4 doesn't support cemuhook
|
||||
#ifndef __OPENORBIS__
|
||||
|
||||
class FakeCemuhookServer {
|
||||
public:
|
||||
FakeCemuhookServer()
|
||||
@@ -133,4 +132,3 @@ TEST_CASE("CalibrationConfigurationJob completed", "[input_common]") {
|
||||
REQUIRE(max_x == 200);
|
||||
REQUIRE(max_y == 200);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <numeric>
|
||||
|
||||
@@ -423,7 +425,7 @@ void BufferCache<P>::UnbindGraphicsStorageBuffers(size_t stage) {
|
||||
|
||||
template <class P>
|
||||
bool BufferCache<P>::BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index, u32 cbuf_index,
|
||||
u32 cbuf_offset, bool is_written) {
|
||||
u32 cbuf_offset, bool is_written, u32 descriptor_count) {
|
||||
const bool already_enabled =
|
||||
((channel_state->enabled_storage_buffers[stage] >> ssbo_index) & 1U) != 0;
|
||||
if constexpr (requires { runtime.ShouldLimitDynamicStorageBuffers(); }) {
|
||||
@@ -448,8 +450,8 @@ bool BufferCache<P>::BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index,
|
||||
const auto& cbufs = maxwell3d->state.shader_stages[stage];
|
||||
const GPUVAddr ssbo_addr = cbufs.const_buffers[cbuf_index].address + cbuf_offset;
|
||||
channel_state->storage_buffers[stage][ssbo_index] =
|
||||
StorageBufferBinding(ssbo_addr, cbuf_index, is_written);
|
||||
return (channel_state->storage_buffers[stage][ssbo_index].buffer_id != NULL_BUFFER_ID);
|
||||
StorageBufferBinding(ssbo_addr, cbuf_index, is_written, descriptor_count);
|
||||
return channel_state->storage_buffers[stage][ssbo_index].gpu_addr != 0;
|
||||
}
|
||||
|
||||
template <class P>
|
||||
@@ -487,7 +489,7 @@ void BufferCache<P>::UnbindComputeStorageBuffers() {
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::BindComputeStorageBuffer(size_t ssbo_index, u32 cbuf_index, u32 cbuf_offset,
|
||||
bool is_written) {
|
||||
bool is_written, u32 descriptor_count) {
|
||||
if (ssbo_index >= channel_state->compute_storage_buffers.size()) [[unlikely]] {
|
||||
LOG_ERROR(HW_GPU, "Storage buffer index {} exceeds maximum storage buffer count",
|
||||
ssbo_index);
|
||||
@@ -524,7 +526,7 @@ void BufferCache<P>::BindComputeStorageBuffer(size_t ssbo_index, u32 cbuf_index,
|
||||
const auto& cbufs = launch_desc.const_buffer_config;
|
||||
const GPUVAddr ssbo_addr = cbufs[cbuf_index].Address() + cbuf_offset;
|
||||
channel_state->compute_storage_buffers[ssbo_index] =
|
||||
StorageBufferBinding(ssbo_addr, cbuf_index, is_written);
|
||||
StorageBufferBinding(ssbo_addr, cbuf_index, is_written, descriptor_count);
|
||||
}
|
||||
|
||||
template <class P>
|
||||
@@ -1000,27 +1002,52 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::BindHostGraphicsStorageBuffers(size_t stage) {
|
||||
boost::container::small_vector<u32, NUM_STORAGE_BUFFERS> segment_sizes;
|
||||
bool uses_mapping{};
|
||||
ForEachEnabledBit(channel_state->enabled_storage_buffers[stage], [&](u32 index) {
|
||||
const StorageBufferBindingInfo& binding = channel_state->storage_buffers[stage][index];
|
||||
uses_mapping |= binding.descriptor_count > 1;
|
||||
for (u32 segment = 0; segment < binding.descriptor_count; ++segment) {
|
||||
segment_sizes.push_back(segment < binding.segments.size() ? binding.segments[segment].size : 0);
|
||||
}
|
||||
});
|
||||
if (uses_mapping) {
|
||||
const u32 mapping_size = static_cast<u32>(segment_sizes.size() * sizeof(u32));
|
||||
if constexpr (!IS_OPENGL) {
|
||||
const std::span<u8> mapped = runtime.BindMappedStorageBuffer(mapping_size);
|
||||
std::memcpy(mapped.data(), segment_sizes.data(), mapping_size);
|
||||
}
|
||||
}
|
||||
|
||||
u32 binding_index = 0;
|
||||
ForEachEnabledBit(channel_state->enabled_storage_buffers[stage], [&](u32 index) {
|
||||
const Binding& binding = channel_state->storage_buffers[stage][index];
|
||||
Buffer& buffer = slot_buffers[binding.buffer_id];
|
||||
TouchBuffer(buffer, binding.buffer_id);
|
||||
const u32 size = binding.size;
|
||||
SynchronizeBuffer(buffer, binding.device_addr, size);
|
||||
|
||||
const u32 offset = buffer.Offset(binding.device_addr);
|
||||
buffer.MarkUsage(offset, size);
|
||||
const StorageBufferBindingInfo& storage = channel_state->storage_buffers[stage][index];
|
||||
const bool is_written = ((channel_state->written_storage_buffers[stage] >> index) & 1) != 0;
|
||||
|
||||
if (is_written) {
|
||||
MarkWrittenBuffer(binding.buffer_id, binding.device_addr, size);
|
||||
}
|
||||
|
||||
if constexpr (NEEDS_BIND_STORAGE_INDEX) {
|
||||
runtime.BindStorageBuffer(stage, binding_index, buffer, offset, size, is_written);
|
||||
++binding_index;
|
||||
} else {
|
||||
runtime.BindStorageBuffer(buffer, offset, size, is_written);
|
||||
for (u32 segment = 0; segment < storage.descriptor_count; ++segment) {
|
||||
Buffer* buffer = &slot_buffers[NULL_BUFFER_ID];
|
||||
u32 offset{};
|
||||
u32 size{IS_OPENGL ? 0U : static_cast<u32>(sizeof(u32))};
|
||||
const bool is_actual_segment = segment < storage.segments.size();
|
||||
// shall be safe enough if the segment is not actual, use the last available segment or nullptr if none exist.
|
||||
const Binding* binding = is_actual_segment ? &storage.segments[segment] : storage.segments.empty() ? nullptr : &storage.segments.back();
|
||||
if (binding) {
|
||||
buffer = &slot_buffers[binding->buffer_id];
|
||||
size = binding->size;
|
||||
offset = buffer->Offset(binding->device_addr);
|
||||
if (is_actual_segment) {
|
||||
TouchBuffer(*buffer, binding->buffer_id);
|
||||
SynchronizeBuffer(*buffer, binding->device_addr, size);
|
||||
buffer->MarkUsage(offset, size);
|
||||
if (is_written) {
|
||||
MarkWrittenBuffer(binding->buffer_id, binding->device_addr, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
if constexpr (NEEDS_BIND_STORAGE_INDEX) {
|
||||
runtime.BindStorageBuffer(stage, binding_index++, *buffer, offset, size, is_written);
|
||||
} else {
|
||||
runtime.BindStorageBuffer(*buffer, offset, size, is_written);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1136,28 +1163,53 @@ void BufferCache<P>::BindHostComputeUniformBuffers() {
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::BindHostComputeStorageBuffers() {
|
||||
boost::container::small_vector<u32, NUM_STORAGE_BUFFERS> segment_sizes;
|
||||
bool uses_mapping{};
|
||||
ForEachEnabledBit(channel_state->enabled_compute_storage_buffers, [&](u32 index) {
|
||||
const StorageBufferBindingInfo& binding = channel_state->compute_storage_buffers[index];
|
||||
uses_mapping |= binding.descriptor_count > 1;
|
||||
for (u32 segment = 0; segment < binding.descriptor_count; ++segment) {
|
||||
segment_sizes.push_back(segment < binding.segments.size() ? binding.segments[segment].size : 0);
|
||||
}
|
||||
});
|
||||
if (uses_mapping) {
|
||||
const u32 mapping_size = static_cast<u32>(segment_sizes.size() * sizeof(u32));
|
||||
if constexpr (!IS_OPENGL) {
|
||||
const std::span<u8> mapped = runtime.BindMappedStorageBuffer(mapping_size);
|
||||
std::memcpy(mapped.data(), segment_sizes.data(), mapping_size);
|
||||
}
|
||||
}
|
||||
|
||||
u32 binding_index = 0;
|
||||
ForEachEnabledBit(channel_state->enabled_compute_storage_buffers, [&](u32 index) {
|
||||
const Binding& binding = channel_state->compute_storage_buffers[index];
|
||||
Buffer& buffer = slot_buffers[binding.buffer_id];
|
||||
TouchBuffer(buffer, binding.buffer_id);
|
||||
const u32 size = binding.size;
|
||||
SynchronizeBuffer(buffer, binding.device_addr, size);
|
||||
|
||||
const u32 offset = buffer.Offset(binding.device_addr);
|
||||
buffer.MarkUsage(offset, size);
|
||||
const StorageBufferBindingInfo& storage = channel_state->compute_storage_buffers[index];
|
||||
const bool is_written =
|
||||
((channel_state->written_compute_storage_buffers >> index) & 1) != 0;
|
||||
|
||||
if (is_written) {
|
||||
MarkWrittenBuffer(binding.buffer_id, binding.device_addr, size);
|
||||
}
|
||||
|
||||
if constexpr (NEEDS_BIND_STORAGE_INDEX) {
|
||||
runtime.BindComputeStorageBuffer(binding_index, buffer, offset, size, is_written);
|
||||
++binding_index;
|
||||
} else {
|
||||
runtime.BindStorageBuffer(buffer, offset, size, is_written);
|
||||
for (u32 segment = 0; segment < storage.descriptor_count; ++segment) {
|
||||
Buffer* buffer = &slot_buffers[NULL_BUFFER_ID];
|
||||
u32 offset{};
|
||||
u32 size{IS_OPENGL ? 0U : static_cast<u32>(sizeof(u32))};
|
||||
const bool is_actual_segment = segment < storage.segments.size();
|
||||
//same fallback logic
|
||||
const Binding* binding = is_actual_segment ? &storage.segments[segment] : storage.segments.empty() ? nullptr : &storage.segments.back();
|
||||
if (binding) {
|
||||
buffer = &slot_buffers[binding->buffer_id];
|
||||
size = binding->size;
|
||||
offset = buffer->Offset(binding->device_addr);
|
||||
if (is_actual_segment) {
|
||||
TouchBuffer(*buffer, binding->buffer_id);
|
||||
SynchronizeBuffer(*buffer, binding->device_addr, size);
|
||||
buffer->MarkUsage(offset, size);
|
||||
if (is_written) {
|
||||
MarkWrittenBuffer(binding->buffer_id, binding->device_addr, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
if constexpr (NEEDS_BIND_STORAGE_INDEX) {
|
||||
runtime.BindComputeStorageBuffer(binding_index++, *buffer, offset, size, is_written);
|
||||
} else {
|
||||
runtime.BindStorageBuffer(*buffer, offset, size, is_written);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1350,10 +1402,7 @@ void BufferCache<P>::UpdateUniformBuffers(size_t stage) {
|
||||
template <class P>
|
||||
void BufferCache<P>::UpdateStorageBuffers(size_t stage) {
|
||||
ForEachEnabledBit(channel_state->enabled_storage_buffers[stage], [&](u32 index) {
|
||||
// Resolve buffer
|
||||
Binding& binding = channel_state->storage_buffers[stage][index];
|
||||
const BufferId buffer_id = FindBuffer(binding.device_addr, binding.size);
|
||||
binding.buffer_id = buffer_id;
|
||||
UpdateStorageBuffer(channel_state->storage_buffers[stage][index]);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1414,12 +1463,54 @@ void BufferCache<P>::UpdateComputeUniformBuffers() {
|
||||
template <class P>
|
||||
void BufferCache<P>::UpdateComputeStorageBuffers() {
|
||||
ForEachEnabledBit(channel_state->enabled_compute_storage_buffers, [&](u32 index) {
|
||||
// Resolve buffer
|
||||
Binding& binding = channel_state->compute_storage_buffers[index];
|
||||
binding.buffer_id = FindBuffer(binding.device_addr, binding.size);
|
||||
UpdateStorageBuffer(channel_state->compute_storage_buffers[index]);
|
||||
});
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::UpdateStorageBuffer(StorageBufferBindingInfo& binding) {
|
||||
binding.segments.clear();
|
||||
if (binding.gpu_addr == 0 || binding.size == 0) { return;}
|
||||
if (binding.descriptor_count == 1) {
|
||||
// for safety gotta preserve the legacy path on possible hosts without storage-buffer descriptor indexing.
|
||||
const std::optional<DAddr> device_addr = gpu_memory->GpuToCpuAddress(binding.gpu_addr);
|
||||
if (device_addr) {
|
||||
binding.segments.push_back(Binding{
|
||||
.device_addr = *device_addr,
|
||||
.size = binding.size,
|
||||
.buffer_id = FindBuffer(*device_addr, binding.size),
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const auto ranges = gpu_memory->GetSubmappedRange(binding.gpu_addr, binding.size);
|
||||
const size_t mapped_size =
|
||||
std::accumulate(ranges.begin(), ranges.end(), size_t{}, [](size_t total, const auto& range) { return total + range.second; });
|
||||
if (mapped_size != binding.size) {
|
||||
LOG_ERROR(HW_GPU, "Storage buffer range {:#x}+{:#x} is not fully mapped", binding.gpu_addr, binding.size);
|
||||
return;
|
||||
}
|
||||
if (ranges.size() > binding.descriptor_count) {
|
||||
LOG_ERROR(HW_GPU, "Storage buffer range {:#x}+{:#x} has {} physical segments, exceeding host capacity {}",
|
||||
binding.gpu_addr, binding.size, ranges.size(), binding.descriptor_count);
|
||||
return;
|
||||
}
|
||||
for (const auto& [gpu_addr, size] : ranges) {
|
||||
const std::optional<DAddr> device_addr = gpu_memory->GpuToCpuAddress(gpu_addr);
|
||||
if (!device_addr || size > (std::numeric_limits<u32>::max)()) {
|
||||
binding.segments.clear();
|
||||
return;
|
||||
}
|
||||
const u32 segment_size = static_cast<u32>(size);
|
||||
binding.segments.push_back(Binding{
|
||||
.device_addr = *device_addr,
|
||||
.size = segment_size,
|
||||
.buffer_id = FindBuffer(*device_addr, segment_size),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::UpdateComputeTextureBuffers() {
|
||||
ForEachEnabledBit(channel_state->enabled_compute_texture_buffers, [&](u32 index) {
|
||||
@@ -1841,6 +1932,11 @@ void BufferCache<P>::DeleteBuffer(BufferId buffer_id, bool do_not_mark) {
|
||||
const auto replace = [scalar_replace](std::span<Binding> bindings) {
|
||||
std::ranges::for_each(bindings, scalar_replace);
|
||||
};
|
||||
const auto storage_replace = [scalar_replace](std::span<StorageBufferBindingInfo> bindings) {
|
||||
for (StorageBufferBindingInfo& binding : bindings) {
|
||||
std::ranges::for_each(binding.segments, scalar_replace);
|
||||
}
|
||||
};
|
||||
|
||||
if (channel_state->index_buffer.buffer_id == buffer_id) {
|
||||
channel_state->index_buffer.buffer_id = BufferId{};
|
||||
@@ -1856,10 +1952,10 @@ void BufferCache<P>::DeleteBuffer(BufferId buffer_id, bool do_not_mark) {
|
||||
}
|
||||
}
|
||||
std::ranges::for_each(channel_state->uniform_buffers, replace);
|
||||
std::ranges::for_each(channel_state->storage_buffers, replace);
|
||||
std::ranges::for_each(channel_state->storage_buffers, storage_replace);
|
||||
replace(channel_state->transform_feedback_buffers);
|
||||
replace(channel_state->compute_uniform_buffers);
|
||||
replace(channel_state->compute_storage_buffers);
|
||||
storage_replace(channel_state->compute_storage_buffers);
|
||||
|
||||
// Mark the whole buffer as CPU written to stop tracking CPU writes
|
||||
if (!do_not_mark) {
|
||||
@@ -1896,12 +1992,14 @@ void BufferCache<P>::DeleteBuffer(BufferId buffer_id, bool do_not_mark) {
|
||||
}
|
||||
|
||||
template <class P>
|
||||
Binding BufferCache<P>::StorageBufferBinding(GPUVAddr ssbo_addr, u32 cbuf_index,
|
||||
bool is_written) const {
|
||||
StorageBufferBindingInfo BufferCache<P>::StorageBufferBinding(GPUVAddr ssbo_addr, u32 cbuf_index,
|
||||
bool is_written, u32 descriptor_count) const {
|
||||
// time to get rid of these null bindings
|
||||
ASSERT(descriptor_count > 0); // shant happen
|
||||
const GPUVAddr gpu_addr = gpu_memory->Read<u64>(ssbo_addr);
|
||||
|
||||
if (gpu_addr == 0) {
|
||||
return NULL_BINDING;
|
||||
return {.descriptor_count = descriptor_count};
|
||||
}
|
||||
|
||||
const auto size = [&]() {
|
||||
@@ -1923,21 +2021,17 @@ Binding BufferCache<P>::StorageBufferBinding(GPUVAddr ssbo_addr, u32 cbuf_index,
|
||||
const GPUVAddr aligned_gpu_addr = Common::AlignDown(gpu_addr, alignment);
|
||||
const u32 aligned_size = static_cast<u32>(gpu_addr - aligned_gpu_addr) + size;
|
||||
|
||||
const std::optional<DAddr> aligned_device_addr = gpu_memory->GpuToCpuAddress(aligned_gpu_addr);
|
||||
if (!aligned_device_addr || size == 0) {
|
||||
if (!gpu_memory->GpuToCpuAddress(aligned_gpu_addr) || size == 0) {
|
||||
LOG_DEBUG(HW_GPU, "Failed to find storage buffer for cbuf index {}", cbuf_index);
|
||||
return NULL_BINDING;
|
||||
return {.descriptor_count = descriptor_count};
|
||||
}
|
||||
const std::optional<DAddr> device_addr = gpu_memory->GpuToCpuAddress(gpu_addr);
|
||||
ASSERT_MSG(device_addr, "Unaligned storage buffer address not found for cbuf index {}",
|
||||
cbuf_index);
|
||||
// The end address used for size calculation does not need to be aligned
|
||||
const DAddr cpu_end = Common::AlignUp(*device_addr + size, Core::DEVICE_PAGESIZE);
|
||||
const GPUVAddr gpu_end = Common::AlignUp(gpu_addr + size, Core::DEVICE_PAGESIZE);
|
||||
|
||||
const Binding binding{
|
||||
.device_addr = *aligned_device_addr,
|
||||
.size = is_written ? aligned_size : static_cast<u32>(cpu_end - *aligned_device_addr),
|
||||
.buffer_id = BufferId{},
|
||||
const StorageBufferBindingInfo binding{
|
||||
.gpu_addr = aligned_gpu_addr,
|
||||
.size = is_written ? aligned_size : static_cast<u32>(gpu_end - aligned_gpu_addr),
|
||||
.descriptor_count = descriptor_count,
|
||||
};
|
||||
return binding;
|
||||
}
|
||||
|
||||
@@ -89,6 +89,15 @@ struct TextureBufferBinding : Binding {
|
||||
PixelFormat format;
|
||||
};
|
||||
|
||||
struct StorageBufferBindingInfo {
|
||||
// another good one: guest SSBO is a virtual interval and may span discontiguous device-memory ranges.
|
||||
// exact case of missing character frames (high sample lane)
|
||||
GPUVAddr gpu_addr{};
|
||||
u32 size{};
|
||||
u32 descriptor_count{1};
|
||||
boost::container::small_vector<Binding, 1> segments;
|
||||
};
|
||||
|
||||
static constexpr Binding NULL_BINDING{
|
||||
.device_addr = 0,
|
||||
.size = 0,
|
||||
@@ -115,14 +124,15 @@ public:
|
||||
Binding index_buffer;
|
||||
std::array<Binding, NUM_VERTEX_BUFFERS> vertex_buffers;
|
||||
std::array<std::array<Binding, NUM_GRAPHICS_UNIFORM_BUFFERS>, NUM_STAGES> uniform_buffers;
|
||||
std::array<std::array<Binding, NUM_STORAGE_BUFFERS>, NUM_STAGES> storage_buffers;
|
||||
std::array<std::array<StorageBufferBindingInfo, NUM_STORAGE_BUFFERS>, NUM_STAGES>
|
||||
storage_buffers;
|
||||
std::array<std::array<TextureBufferBinding, NUM_TEXTURE_BUFFERS>, NUM_STAGES> texture_buffers;
|
||||
std::array<Binding, NUM_TRANSFORM_FEEDBACK_BUFFERS> transform_feedback_buffers;
|
||||
Binding count_buffer_binding;
|
||||
Binding indirect_buffer_binding;
|
||||
|
||||
std::array<Binding, NUM_COMPUTE_UNIFORM_BUFFERS> compute_uniform_buffers;
|
||||
std::array<Binding, NUM_STORAGE_BUFFERS> compute_storage_buffers;
|
||||
std::array<StorageBufferBindingInfo, NUM_STORAGE_BUFFERS> compute_storage_buffers;
|
||||
std::array<TextureBufferBinding, NUM_TEXTURE_BUFFERS> compute_texture_buffers;
|
||||
|
||||
std::array<u32, NUM_STAGES> enabled_uniform_buffer_masks{};
|
||||
@@ -249,7 +259,7 @@ public:
|
||||
void UnbindGraphicsStorageBuffers(size_t stage);
|
||||
|
||||
bool BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index, u32 cbuf_index, u32 cbuf_offset,
|
||||
bool is_written);
|
||||
bool is_written, u32 descriptor_count = 1);
|
||||
|
||||
void UnbindGraphicsTextureBuffers(size_t stage);
|
||||
|
||||
@@ -259,7 +269,7 @@ public:
|
||||
void UnbindComputeStorageBuffers();
|
||||
|
||||
void BindComputeStorageBuffer(size_t ssbo_index, u32 cbuf_index, u32 cbuf_offset,
|
||||
bool is_written);
|
||||
bool is_written, u32 descriptor_count = 1);
|
||||
|
||||
void UnbindComputeTextureBuffers();
|
||||
|
||||
@@ -400,6 +410,8 @@ private:
|
||||
|
||||
void UpdateStorageBuffers(size_t stage);
|
||||
|
||||
void UpdateStorageBuffer(StorageBufferBindingInfo& binding);
|
||||
|
||||
void UpdateTextureBuffers(size_t stage);
|
||||
|
||||
void UpdateTransformFeedbackBuffers();
|
||||
@@ -449,8 +461,9 @@ private:
|
||||
|
||||
void DeleteBuffer(BufferId buffer_id, bool do_not_mark = false);
|
||||
|
||||
[[nodiscard]] Binding StorageBufferBinding(GPUVAddr ssbo_addr, u32 cbuf_index,
|
||||
bool is_written) const;
|
||||
[[nodiscard]] StorageBufferBindingInfo StorageBufferBinding(GPUVAddr ssbo_addr, u32 cbuf_index,
|
||||
bool is_written,
|
||||
u32 descriptor_count) const;
|
||||
|
||||
[[nodiscard]] TextureBufferBinding GetTextureBufferBinding(GPUVAddr gpu_addr, u32 size,
|
||||
PixelFormat format);
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <optional>
|
||||
|
||||
@@ -136,6 +137,7 @@ inline void WriteDescriptorBuffer(const Device& device, const DescriptorBufferLa
|
||||
|
||||
[[nodiscard]] inline u32 NumDescriptorEntries(const Shader::Info& info) {
|
||||
return Shader::NumDescriptors(info.constant_buffer_descriptors) +
|
||||
static_cast<u32>(Shader::UsesStorageBufferMappings(info)) +
|
||||
Shader::NumDescriptors(info.storage_buffers_descriptors) +
|
||||
Shader::NumDescriptors(info.texture_buffer_descriptors) +
|
||||
Shader::NumDescriptors(info.image_buffer_descriptors) +
|
||||
@@ -268,6 +270,12 @@ public:
|
||||
is_compute |= (stage & VK_SHADER_STAGE_COMPUTE_BIT) != 0;
|
||||
|
||||
Add(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, stage, info.constant_buffer_descriptors);
|
||||
// for extra implicit storage-buffer binding required by mapped-storage-buffer support
|
||||
if (Shader::UsesStorageBufferMappings(info)) {
|
||||
struct Descriptor { u32 count; };
|
||||
const std::array descriptors{Descriptor{1}};
|
||||
Add(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, stage, descriptors);
|
||||
}
|
||||
Add(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, stage, info.storage_buffers_descriptors);
|
||||
Add(VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, stage, info.texture_buffer_descriptors);
|
||||
Add(VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, stage, info.image_buffer_descriptors);
|
||||
|
||||
@@ -695,7 +695,7 @@ vk::Buffer BufferCacheRuntime::CreateNullBuffer() {
|
||||
.flags = 0,
|
||||
.size = 4,
|
||||
.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT |
|
||||
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT,
|
||||
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
|
||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||
.queueFamilyIndexCount = 0,
|
||||
.pQueueFamilyIndices = nullptr,
|
||||
|
||||
@@ -155,6 +155,13 @@ public:
|
||||
return ref.mapped_span;
|
||||
}
|
||||
|
||||
// compute/graphics new binding
|
||||
std::span<u8> BindMappedStorageBuffer(u32 size) {
|
||||
const StagingBufferRef ref = staging_pool.Request(size, MemoryUsage::Upload);
|
||||
guest_descriptor_queue.AddBuffer(ref.buffer, ref.device_address, static_cast<u32>(ref.offset), size);
|
||||
return ref.mapped_span;
|
||||
}
|
||||
|
||||
void BindUniformBuffer(const Buffer& buffer, u32 offset, u32 size) {
|
||||
BindBuffer(buffer, offset, size);
|
||||
}
|
||||
|
||||
@@ -157,9 +157,7 @@ bool ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute,
|
||||
buffer_cache.UnbindComputeStorageBuffers();
|
||||
size_t ssbo_index{};
|
||||
for (const auto& desc : info.storage_buffers_descriptors) {
|
||||
ASSERT(desc.count == 1);
|
||||
buffer_cache.BindComputeStorageBuffer(ssbo_index, desc.cbuf_index, desc.cbuf_offset,
|
||||
desc.is_written);
|
||||
buffer_cache.BindComputeStorageBuffer(ssbo_index, desc.cbuf_index, desc.cbuf_offset, desc.is_written, desc.count);
|
||||
++ssbo_index;
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ static DescriptorBankInfo MakeBankInfo(std::span<const Shader::Info> infos) {
|
||||
DescriptorBankInfo bank;
|
||||
for (const Shader::Info& info : infos) {
|
||||
bank.uniform_buffers += Accumulate(info.constant_buffer_descriptors);
|
||||
bank.storage_buffers += Accumulate(info.storage_buffers_descriptors);
|
||||
bank.storage_buffers += Accumulate(info.storage_buffers_descriptors) + static_cast<u32>(Shader::UsesStorageBufferMappings(info));
|
||||
bank.texture_buffers += Accumulate(info.texture_buffer_descriptors);
|
||||
bank.image_buffers += Accumulate(info.image_buffer_descriptors);
|
||||
bank.textures += Accumulate(info.texture_descriptors);
|
||||
|
||||
@@ -367,9 +367,8 @@ bool GraphicsPipeline::ConfigureImpl(bool is_indexed) {
|
||||
if constexpr (Spec::has_storage_buffers) {
|
||||
size_t ssbo_index{};
|
||||
for (const auto& desc : info.storage_buffers_descriptors) {
|
||||
ASSERT(desc.count == 1);
|
||||
buffer_cache.BindGraphicsStorageBuffer(stage, ssbo_index, desc.cbuf_index,
|
||||
desc.cbuf_offset, desc.is_written);
|
||||
desc.cbuf_offset, desc.is_written, desc.count);
|
||||
++ssbo_index;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,12 @@ using VideoCommon::FileEnvironment;
|
||||
using VideoCommon::GenericEnvironment;
|
||||
using VideoCommon::GraphicsEnvironment;
|
||||
|
||||
constexpr u32 CACHE_VERSION = 18;
|
||||
// SPIR-V descriptor arrays require a fixed pipeline-layout count.
|
||||
// Exploration ceiling; buffer-cache telemetry records the actual physical-range demand.
|
||||
// Keep this modest because every mapped SSBO binds the full fixed array on each update.
|
||||
constexpr u32 MAX_MAPPED_STORAGE_BUFFER_DESCRIPTORS = 32;
|
||||
|
||||
constexpr u32 CACHE_VERSION = 19;
|
||||
constexpr size_t VULKAN_CACHE_FLUSH_PIPELINES = 128;
|
||||
constexpr size_t VULKAN_CACHE_FLUSH_MIN_SECONDS = 30;
|
||||
constexpr std::array<char, 8> VULKAN_CACHE_MAGIC_NUMBER{'y', 'u', 'z', 'u', 'v', 'k', 'c', 'h'};
|
||||
@@ -432,6 +437,8 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
|
||||
device.IsUniformTexelBufferArrayNonUniformIndexingSupported(),
|
||||
.support_storage_texel_buffer_array_nonuniform_indexing =
|
||||
device.IsStorageTexelBufferArrayNonUniformIndexingSupported(),
|
||||
.support_storage_buffer_array_nonuniform_indexing =
|
||||
device.IsStorageBufferArrayNonUniformIndexingSupported(),
|
||||
|
||||
.warp_size_potentially_larger_than_guest = device.IsWarpSizePotentiallyBiggerThanGuest(),
|
||||
|
||||
@@ -460,6 +467,7 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
|
||||
host_info = Shader::HostTranslateInfo{
|
||||
.min_ssbo_alignment = device.GetStorageBufferAlignment(),
|
||||
.max_per_stage_descriptor_sampled_images = device.GetMaxPerStageDescriptorSampledImages(),
|
||||
.max_per_stage_descriptor_storage_buffers = device.GetMaxPerStageDescriptorStorageBuffers(),
|
||||
.max_per_stage_resources = device.GetMaxPerStageResources(),
|
||||
.max_descriptor_set_samplers = device.GetMaxDescriptorSetSamplers(),
|
||||
.max_descriptor_set_uniform_buffers = device.GetMaxDescriptorSetUniformBuffers(),
|
||||
@@ -479,6 +487,20 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
|
||||
.support_viewport_index_layer = device.IsExtShaderViewportIndexLayerSupported(),
|
||||
.support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(),
|
||||
.support_conditional_barrier = device.SupportsConditionalBarriers(),
|
||||
.storage_buffer_segment_count = [&] {
|
||||
if (!device.IsStorageBufferArrayNonUniformIndexingSupported()) {
|
||||
return 1U;
|
||||
}
|
||||
constexpr u32 MaxGraphicsStages = static_cast<u32>(Maxwell::MaxShaderStage);
|
||||
const auto reserve = [](u32 limit, u32 count) {
|
||||
return limit > count ? limit - count : 0U;
|
||||
};
|
||||
const u32 per_stage = reserve(device.GetMaxPerStageDescriptorStorageBuffers(), 1) / static_cast<u32>(Shader::Info::MAX_SSBOS);
|
||||
const u32 per_set = reserve(device.GetMaxDescriptorSetStorageBuffers(), MaxGraphicsStages) / (static_cast<u32>(Shader::Info::MAX_SSBOS) * MaxGraphicsStages);
|
||||
const u32 resources = reserve(device.GetMaxPerStageResources(), 1) / (static_cast<u32>(Shader::Info::MAX_SSBOS) * 2);
|
||||
// ensure at least one storage buffer segment is available per stage. max is still arbitrary
|
||||
return (std::max)(1U, (std::min)({MAX_MAPPED_STORAGE_BUFFER_DESCRIPTORS, per_stage, per_set, resources}));
|
||||
}(),
|
||||
};
|
||||
host_info.ApplyDescriptorLimitPolicy();
|
||||
|
||||
|
||||
@@ -33,11 +33,7 @@ constexpr VkDeviceSize MAX_ALIGNMENT = 256;
|
||||
// Windows ones however, can intake bigger buffers and generally do not OOM.
|
||||
// - GTX 960 on Windows will not OOM with 256mib
|
||||
// - GT 1030 on ^NIX will OOM with 256mib
|
||||
// With Orbis we need to be insanely chary with our allocations
|
||||
// anything over 64MB is prone to crash the GPU, I'm sorry.
|
||||
#if defined(__OPENORBIS__)
|
||||
constexpr VkDeviceSize MAX_STREAM_BUFFER_SIZE = 32_MiB;
|
||||
#elif defined(__FreeBSD__)
|
||||
#if defined(__FreeBSD__)
|
||||
constexpr VkDeviceSize MAX_STREAM_BUFFER_SIZE = 128_MiB;
|
||||
#else
|
||||
constexpr VkDeviceSize MAX_STREAM_BUFFER_SIZE = 256_MiB;
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
#define VK_USE_PLATFORM_ANDROID_KHR
|
||||
#elif defined(__HAIKU__)
|
||||
#define VK_USE_PLATFORM_XCB_KHR
|
||||
#elif defined(__OPENORBIS__)
|
||||
// No fucking vulkan on the PlayStation 4
|
||||
#else
|
||||
#define VK_USE_PLATFORM_XLIB_KHR
|
||||
#define VK_USE_PLATFORM_WAYLAND_KHR
|
||||
|
||||
@@ -708,7 +708,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
descriptor_indexing.shaderUniformTexelBufferArrayDynamicIndexing = false;
|
||||
descriptor_indexing.shaderStorageTexelBufferArrayDynamicIndexing = false;
|
||||
descriptor_indexing.shaderUniformBufferArrayNonUniformIndexing = false;
|
||||
descriptor_indexing.shaderStorageBufferArrayNonUniformIndexing = false;
|
||||
descriptor_indexing.shaderInputAttachmentArrayNonUniformIndexing = false;
|
||||
descriptor_indexing.descriptorBindingUniformBufferUpdateAfterBind = false;
|
||||
descriptor_indexing.descriptorBindingSampledImageUpdateAfterBind = false;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user