Compare commits

..

32 commits

Author SHA1 Message Date
Bix
2dce08dd81 [Android] Change App background to white]
All checks were successful
eden-license / license-header (pull_request) Successful in 30s
2025-09-04 21:51:12 +01:00
Bixthefin
6d59b514e3 [Android] Change app_name to Eden Legacy 2025-09-04 21:51:12 +01:00
Bixthefin
f278256e06 [android] Update applicationId to legacy 2025-09-04 21:51:12 +01:00
Bix
85f9d0338e revert cd394fc40f
revert revert [android] Snapdragon 865 patches (#23)

revert [android] Snapdragon 865 patches (#23)

Co-authored-by: Aleksandr Popovich <alekpopo@pm.me>
Reviewed-on: https://git.bixed.xyz/Bix/eden/pulls/23

Reverted due to heavy performance hits on Android with higher specifications, will be adjusted to be included in a specific build for older A6XX devices, as 855, 860, 865, 870, meanwhile it does fix critical issues with certain games crashing due to memory and VRAM usage, hits performance on SoC that can do it without this special flags.
2025-09-04 21:51:04 +01:00
Bix
0c20f76eca Added "Legacy " to App name on android.
Signed-off-by: Bix <bix@bixed.xyz>
2025-09-04 21:49:15 +01:00
bbcd8aded6
Revert "[heap_tracker] Use ankerl map instead of rb tree (#249)" (#382)
This reverts commit c9a3baab5d.

this commit caused issues in ender magnolia or something, need to make
sure I didn't mess up the revert

Reviewed-on: #382
Reviewed-by: Shinmegumi <shinmegumi@eden-emu.dev>
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@outlook.com>
2025-09-04 16:04:42 +02:00
2bc792e211
[cmake] fix yuzu_cmd, bundled overrides (#381)
Fixes yuzu_cmd not linking to vma (just link to vma for now, but should
be linked to video_core maybe?)

also fixes the weird precedence of bundled packages esp w.r.t json where
an effectively garbage value was passed into the BUNDLED_PACKAGE
argument (was forced to on)

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

Reviewed-on: #381
2025-09-03 04:36:21 +02:00
e7560183fa
[android] minor ui tweaks + translations (#326)
CHANGES:
fix drawer pause/unpause sync (upon leaving/returning to app)
add quick toggle controller overlay to drawer (for players with multiple gear style)
added translation for emulation_hide_overlay
changed Show overlay to Show controller in all langs
added missing translations for values-de

WHAT TO TEST:
sync of pause/resume when you leave eden (screenshot, home, alt tab, etc).
show controller toggle: if it works it works.

Co-authored-by: Allison Cunha <allisonbzk@gmail.com>
Reviewed-on: #326
Reviewed-by: crueter <crueter@eden-emu.dev>
Co-authored-by: xbzk <xbzk@eden-emu.dev>
Co-committed-by: xbzk <xbzk@eden-emu.dev>
2025-09-03 03:55:19 +02:00
84fadd1506
[cmake] fix unordered-dense deps (#380)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
Co-authored-by: crueter <crueter@eden-emu.dev>
Reviewed-on: #380
Reviewed-by: crueter <crueter@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-09-02 03:25:27 +02:00
Bix
be7a3e1e86
[Hotfix] Update recommended driver from T21 to T22 (#379)
Help crueters workload.
Signed-off-by: Bix <bix@bixed.xyz>

Reviewed-on: #379
Reviewed-by: crueter <crueter@eden-emu.dev>
Co-authored-by: Bix <bix@bixed.xyz>
Co-committed-by: Bix <bix@bixed.xyz>
2025-09-01 22:25:26 +02:00
6aa8be1da8
[cmake] fix gh dependencies (#377)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: #377
Reviewed-by: crueter <crueter@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-09-01 21:14:54 +02:00
innix
e28b0d2590
[android]: Force app to use the displays max set refresh rate (#373)
Since Android 15, google automatically forces "games" to be 60 hrz. This ensures the display's max refresh rate is actually used. Tested on a Google Pixel 7 Pro with Android 16

Co-authored-by: innix <dev@innix.space>
Reviewed-on: #373
Co-committed-by: innix <dev@innix.space>
2025-09-01 14:18:30 +02:00
innix
6fcfe7f4f3
[macOS, compat] Allow games to boot in MacOS (#372)
This fixes the crashes on game launch caused by MacOS not being present in host_manager.cpp and enables primitiveRestart for MoltenVK to suppress a bunch of errors given in the log about  MoltenVK requiring primitiveRestart. Fixes an crash when switching kingdoms in Mario Odyssey as well

EDS is forced to 0, otherwise games do not show graphics

Note: For now only dynarmicc is working, performance will be slow
Reviewed-on: #372
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: MaranBr <maranbr@outlook.com>
Co-authored-by: innix <dev@innix.space>
Co-committed-by: innix <dev@innix.space>
2025-09-01 09:23:03 +02:00
e60fd4b68b
[VMA] Phase 3:- Hand all allocation & binding to VMA (#362)
This patch completely removes the Custom Sub allocator with VMA and delegates everything to the VMA.
Overall, the patch integrates VMA and simplifies memory management.
Once these changes pass the testing, it will be used as a base for further improvement.
Note to testers, test for stability and performance.

Co-authored-by: crueter <crueter@eden-emu.dev>
Reviewed-on: #362
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@outlook.com>
Co-authored-by: wildcard <wildcard@eden-emu.dev>
Co-committed-by: wildcard <wildcard@eden-emu.dev>
2025-09-01 00:20:03 +02:00
10c76568b8
[common, fs] include missing header introduced on #330 (#370)
Signed-off-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Reviewed-on: #370
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Co-authored-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Co-committed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
2025-08-31 08:40:46 +02:00
8dba6a2cb4
[gpu/NVDRV] Finalize, improve AllocObjCtx (#333)
Improves object allocation per channel, only allowing max amount of 6 objects contexts per channel.
Previously objects were stored in a heap allocated vector which is sub-optimal for performance reasons.
The new implementation instead uses a stack based array with a O(1) approach.
This should boost performance in games which heavily rely on object context creation.

Co-authored-by: MaranBr <maranbr@outlook.com>
Reviewed-on: #333
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Co-authored-by: SDK-Chan <sdkchan@eden-emu.dev>
Co-committed-by: SDK-Chan <sdkchan@eden-emu.dev>
2025-08-31 07:32:54 +02:00
4b5a8e0621
[cmake] changed app id from org.eden_emu.eden to dev.eden_emu.eden (#237)
it is better to match app id with website domain

Reviewed-on: #237
Reviewed-by: crueter <crueter@eden-emu.dev>
Co-authored-by: Guo Yunhe <i@guoyunhe.me>
Co-committed-by: Guo Yunhe <i@guoyunhe.me>
2025-08-31 04:56:23 +02:00
39e27bc954
[android] fix intent-auto-driver-install (#369)
Resolving drivers based on the artifact name was too buggy and inconsistent, this PR improves it. Well, I like to think it does

Reviewed-on: #369
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Co-authored-by: Producdevity <y.gherbi.dev@gmail.com>
Co-committed-by: Producdevity <y.gherbi.dev@gmail.com>
2025-08-31 03:33:54 +02:00
21c77bdcac
[cmake] fix ffmpeg libdrm on macos (#367)
Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: #367
Reviewed-by: Shinmegumi <shinmegumi@eden-emu.dev>
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
2025-08-31 03:10:34 +02:00
1c3ca17cfb
[dynarmic] fix annoying gcc/clang error (#365)
caused qt creator to crash somehow geg

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

Reviewed-on: #365
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@outlook.com>
2025-08-31 00:12:06 +02:00
7ca197d900
[qt, compat] fix freedesktop stuffs on Solaris/OpenBSD (#360)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: #360
Reviewed-by: crueter <crueter@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-08-30 23:08:04 +02:00
3b4c1beb0c
[desktop] only warn on firmware for qlaunch/games (#363)
- only warns about too new/missing for home menu
- only warns about missing for games that need it (mk8dx)

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

Reviewed-on: #363
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: MaranBr <maranbr@outlook.com>
2025-08-30 20:32:28 +02:00
76de9d6c8c
[cmake, compat] fix solaris boost build once and for all (#364)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: #364
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: crueter <crueter@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-08-30 20:32:21 +02:00
ab015bc730
[VK] Fix asserts with incorrect memory allocations (#357)
This fixes many assertions with incorrect memory allocations. Regression introduced in PR 334.

Co-authored-by: JPikachu <jpikachu.eden@gmail.com>
Co-authored-by: MaranBr <maranbr@outlook.com>
Reviewed-on: #357
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: crueter <crueter@eden-emu.dev>
Co-authored-by: JPikachu <jpikachu@eden-emu.dev>
Co-committed-by: JPikachu <jpikachu@eden-emu.dev>
2025-08-30 19:35:53 +02:00
f005f6a3ab
[compat] fix freebsd mmap virtual base (#354)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: #354
Reviewed-by: Shinmegumi <shinmegumi@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-08-30 17:03:56 +02:00
47b703067e
[settings] fix unreachable code warning in fastmem bool (#347)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: #347
Reviewed-by: Shinmegumi <shinmegumi@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-08-30 15:17:30 +02:00
03b4f57364
[cmake] fix nx_tzdb msvc link error (tmp) (#356)
This is an incredibly stupid and nonsensical bug that I have no way of possibly explaining. This is a temporary workaround until I can reproduce it and figure it out.

Otherwise MSVC linker crashes during final link phase. thanks microsoft

Signed-off-by: crueter <crueter@crueter.xyz>
Reviewed-on: #356
Reviewed-by: Shinmegumi <shinmegumi@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Co-authored-by: crueter <crueter@crueter.xyz>
Co-committed-by: crueter <crueter@crueter.xyz>
2025-08-30 06:27:30 +02:00
57fbdd516e
[host_memory] Fix a bunch of memory errors on Windows (#303)
This fixes a bunch of memory errors that could happen on Windows. Possibly regression introduced on PR 187.

Reviewed-on: #303
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Co-authored-by: MaranBr <maranbr@outlook.com>
Co-committed-by: MaranBr <maranbr@outlook.com>
2025-08-29 14:20:22 +02:00
f07309afd2
[ffmpeg] proper drm inclusion (#328)
* this fixes build on ubuntu 25.04

Signed-off-by: Caio Oliveira <caiooliveirafarias0@gmail.com>

Reviewed-on: #328
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Co-authored-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Co-committed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
2025-08-29 01:49:20 +02:00
cf689a7a49
[cmake] properly invalidate tzdb cache; require matching version (#346)
Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: #346
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2025-08-29 01:35:01 +02:00
702a2beb7c
[cmake] refactor: cpmfile, deps prefetch, force system and more (#322)
CPM Dependencies are now managed in a singular json file, where each can be properly prefetched at-will via `tools/cpm-fetch.sh <packages...>`, or all at once via `tools/cpm-fetch-all.sh`.

Adds docs for CPMUtil as well.

Also adds `<package>_FORCE_{BUNDLED,SYSTEM}` overrides

Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: #322
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2025-08-29 00:18:02 +02:00
d709771d67
[core] Unsafe toggles cannot be changed at runtime (#344)
Some checks failed
eden-license / license-header (pull_request) Failing after 27s
Reviewed-on: #344
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: crueter <crueter@eden-emu.dev>
Co-authored-by: MaranBr <maranbr@outlook.com>
Co-committed-by: MaranBr <maranbr@outlook.com>
2025-08-29 00:07:21 +02:00
97 changed files with 2541 additions and 1351 deletions

View file

@ -1,6 +1,6 @@
AppRun AppRun
eden.desktop eden.desktop
org.eden_emu.eden.desktop dev.eden_emu.eden.desktop
shared/bin/eden shared/bin/eden
shared/lib/lib.path shared/lib/lib.path
shared/lib/ld-linux-x86-64.so.2 shared/lib/ld-linux-x86-64.so.2

View file

@ -59,15 +59,15 @@ VERSION="$(echo "$EDEN_TAG")"
mkdir -p ./AppDir mkdir -p ./AppDir
cd ./AppDir cd ./AppDir
cp ../dist/org.eden_emu.eden.desktop . cp ../dist/dev.eden_emu.eden.desktop .
cp ../dist/org.eden_emu.eden.svg . cp ../dist/dev.eden_emu.eden.svg .
ln -sf ./org.eden_emu.eden.svg ./.DirIcon ln -sf ./dev.eden_emu.eden.svg ./.DirIcon
UPINFO='gh-releases-zsync|eden-emulator|Releases|latest|*.AppImage.zsync' UPINFO='gh-releases-zsync|eden-emulator|Releases|latest|*.AppImage.zsync'
if [ "$DEVEL" = 'true' ]; then if [ "$DEVEL" = 'true' ]; then
sed -i 's|Name=Eden|Name=Eden Nightly|' ./org.eden_emu.eden.desktop sed -i 's|Name=Eden|Name=Eden Nightly|' ./dev.eden_emu.eden.desktop
UPINFO="$(echo "$UPINFO" | sed 's|Releases|nightly|')" UPINFO="$(echo "$UPINFO" | sed 's|Releases|nightly|')"
fi fi

View file

@ -6,7 +6,7 @@
which png2icns || [ which yay && yay libicns ] || exit which png2icns || [ which yay && yay libicns ] || exit
which magick || exit which magick || exit
export EDEN_SVG_ICO="dist/org.eden_emu.eden.svg" export EDEN_SVG_ICO="dist/dev.eden_emu.eden.svg"
svgo --multipass $EDEN_SVG_ICO svgo --multipass $EDEN_SVG_ICO
magick -density 256x256 -background transparent $EDEN_SVG_ICO \ magick -density 256x256 -background transparent $EDEN_SVG_ICO \

1
.gitignore vendored
View file

@ -52,3 +52,4 @@ Thumbs.db
eden-windows-msvc eden-windows-msvc
artifacts artifacts
*.AppImage* *.AppImage*
/install*

View file

@ -0,0 +1,22 @@
From e59d30b7b12e1d04cc2fc9c6219e35bda447c17e Mon Sep 17 00:00:00 2001
From: Lizzie <159065448+Lizzie841@users.noreply.github.com>
Date: Fri, 16 May 2025 04:12:13 +0100
Subject: [PATCH] Update CMakeLists.txt
---
CMakeLists.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b5f4c4f..c5c6f31 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -24,7 +24,7 @@ target_include_directories(
target_compile_features(unordered_dense INTERFACE cxx_std_17)
-if(_unordered_dense_is_toplevel_project)
+if(_unordered_dense_is_toplevel_project OR UNORDERED_DENSE_INSTALL)
# locations are provided by GNUInstallDirs
install(
TARGETS unordered_dense

View file

@ -99,7 +99,6 @@ option(FORCE_DOWNLOAD_WIN_BUNDLES "Forcefully download bundled Windows dependenc
if (YUZU_USE_CPM AND ENABLE_SDL2) if (YUZU_USE_CPM AND ENABLE_SDL2)
option(YUZU_USE_BUNDLED_SDL2 "Download bundled SDL2 build" "${MSVC}") option(YUZU_USE_BUNDLED_SDL2 "Download bundled SDL2 build" "${MSVC}")
CMAKE_DEPENDENT_OPTION(FORCE_DOWNLOAD_SDL2 "Forcefully download all bundled SDL2 builds (useful for CI)" OFF "YUZU_USE_BUNDLED_SDL2" OFF)
endif() endif()
CMAKE_DEPENDENT_OPTION(YUZU_ROOM "Enable dedicated room functionality" ON "NOT ANDROID" OFF) CMAKE_DEPENDENT_OPTION(YUZU_ROOM "Enable dedicated room functionality" ON "NOT ANDROID" OFF)
@ -110,11 +109,7 @@ CMAKE_DEPENDENT_OPTION(YUZU_CMD "Compile the eden-cli executable" ON "NOT ANDROI
CMAKE_DEPENDENT_OPTION(YUZU_CRASH_DUMPS "Compile crash dump (Minidump) support" OFF "WIN32 OR LINUX" OFF) CMAKE_DEPENDENT_OPTION(YUZU_CRASH_DUMPS "Compile crash dump (Minidump) support" OFF "WIN32 OR LINUX" OFF)
if (PLATFORM_FREEBSD) option(YUZU_CHECK_SUBMODULES "Check if submodules are present" ${EXT_DEFAULT})
option(YUZU_CHECK_SUBMODULES "Check if submodules are present" OFF)
else()
option(YUZU_CHECK_SUBMODULES "Check if submodules are present" ON)
endif()
option(YUZU_ENABLE_LTO "Enable link-time optimization" OFF) option(YUZU_ENABLE_LTO "Enable link-time optimization" OFF)
@ -143,9 +138,8 @@ endif()
option(ENABLE_OPENSSL "Enable OpenSSL backend for ISslConnection" ${DEFAULT_ENABLE_OPENSSL}) option(ENABLE_OPENSSL "Enable OpenSSL backend for ISslConnection" ${DEFAULT_ENABLE_OPENSSL})
if (YUZU_USE_CPM AND ENABLE_OPENSSL) if (ENABLE_OPENSSL)
CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_OPENSSL "Download bundled OpenSSL build" "${MSVC}" "NOT ANDROID" ON) CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_OPENSSL "Download bundled OpenSSL build" "${MSVC}" "NOT ANDROID" ON)
CMAKE_DEPENDENT_OPTION(FORCE_DOWNLOAD_OPENSSL "Forcefully download all bundled OpenSSL builds (useful for CI)" OFF "YUZU_USE_BUNDLED_OPENSSL" OFF)
endif() endif()
if (ANDROID AND YUZU_DOWNLOAD_ANDROID_VVL) if (ANDROID AND YUZU_DOWNLOAD_ANDROID_VVL)
@ -180,12 +174,12 @@ if (YUZU_USE_PRECOMPILED_HEADERS)
set(YUZU_USE_PRECOMPILED_HEADERS OFF CACHE BOOL "" FORCE) set(YUZU_USE_PRECOMPILED_HEADERS OFF CACHE BOOL "" FORCE)
endif() endif()
endif() endif()
if (YUZU_USE_PRECOMPILED_HEADERS) if (YUZU_USE_PRECOMPILED_HEADERS)
message(STATUS "Using Precompiled Headers.") message(STATUS "Using Precompiled Headers.")
set(CMAKE_PCH_INSTANTIATE_TEMPLATES ON) set(CMAKE_PCH_INSTANTIATE_TEMPLATES ON)
endif() endif()
# Default to a Release build # Default to a Release build
get_property(IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) get_property(IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if (NOT IS_MULTI_CONFIG AND NOT CMAKE_BUILD_TYPE) if (NOT IS_MULTI_CONFIG AND NOT CMAKE_BUILD_TYPE)
@ -246,20 +240,24 @@ endfunction()
if(EXISTS ${PROJECT_SOURCE_DIR}/.gitmodules AND YUZU_CHECK_SUBMODULES) if(EXISTS ${PROJECT_SOURCE_DIR}/.gitmodules AND YUZU_CHECK_SUBMODULES)
check_submodules_present() check_submodules_present()
endif() endif()
configure_file(${PROJECT_SOURCE_DIR}/dist/compatibility_list/compatibility_list.qrc configure_file(${PROJECT_SOURCE_DIR}/dist/compatibility_list/compatibility_list.qrc
${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
COPYONLY) COPYONLY)
if (EXISTS ${PROJECT_SOURCE_DIR}/dist/compatibility_list/compatibility_list.json) if (EXISTS ${PROJECT_SOURCE_DIR}/dist/compatibility_list/compatibility_list.json)
configure_file("${PROJECT_SOURCE_DIR}/dist/compatibility_list/compatibility_list.json" configure_file("${PROJECT_SOURCE_DIR}/dist/compatibility_list/compatibility_list.json"
"${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json" "${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json"
COPYONLY) COPYONLY)
endif() endif()
if (ENABLE_COMPATIBILITY_LIST_DOWNLOAD AND NOT EXISTS ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json) if (ENABLE_COMPATIBILITY_LIST_DOWNLOAD AND NOT EXISTS ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
message(STATUS "Downloading compatibility list for yuzu...") message(STATUS "Downloading compatibility list for yuzu...")
file(DOWNLOAD file(DOWNLOAD
https://api.yuzu-emu.org/gamedb/ https://api.yuzu-emu.org/gamedb/
"${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json" SHOW_PROGRESS) "${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json" SHOW_PROGRESS)
endif() endif()
if (NOT EXISTS ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json) if (NOT EXISTS ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
file(WRITE ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json "") file(WRITE ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json "")
endif() endif()
@ -303,8 +301,15 @@ if (NOT DEFINED ARCHITECTURE)
set(ARCHITECTURE_GENERIC 1) set(ARCHITECTURE_GENERIC 1)
add_definitions(-DARCHITECTURE_GENERIC=1) add_definitions(-DARCHITECTURE_GENERIC=1)
endif() endif()
message(STATUS "Target architecture: ${ARCHITECTURE}") message(STATUS "Target architecture: ${ARCHITECTURE}")
if (MSVC AND ARCHITECTURE_x86)
message(FATAL_ERROR "Attempting to build with the x86 environment is not supported. \
This can typically happen if you used the Developer Command Prompt from the start menu;\
instead, run vcvars64.bat directly, located at C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Auxiliary/Build/vcvars64.bat")
endif()
if (UNIX) if (UNIX)
add_definitions(-DYUZU_UNIX=1) add_definitions(-DYUZU_UNIX=1)
endif() endif()
@ -367,6 +372,15 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
include(CPMUtil) include(CPMUtil)
# openssl funniness
if (ENABLE_OPENSSL)
if (YUZU_USE_BUNDLED_OPENSSL)
AddJsonPackage(openssl)
endif()
find_package(OpenSSL 1.1.1 REQUIRED)
endif()
if (YUZU_USE_CPM) if (YUZU_USE_CPM)
message(STATUS "Fetching needed dependencies with CPM") message(STATUS "Fetching needed dependencies with CPM")
@ -375,36 +389,9 @@ if (YUZU_USE_CPM)
# TODO(crueter): renderdoc? # TODO(crueter): renderdoc?
# openssl funniness
if (ENABLE_OPENSSL)
if (YUZU_USE_BUNDLED_OPENSSL)
AddCIPackage(
PACKAGE OpenSSL
NAME openssl
REPO crueter-ci/OpenSSL
VERSION 3.5.2
MIN_VERSION 1.1.1
FORCE_DOWNLOAD ${FORCE_DOWNLOAD_OPENSSL}
)
endif()
find_package(OpenSSL 1.1.1 REQUIRED)
endif()
# boost # boost
set(BOOST_INCLUDE_LIBRARIES algorithm icl pool container heap asio headers process filesystem crc variant) set(BOOST_INCLUDE_LIBRARIES algorithm icl pool container heap asio headers process filesystem crc variant)
AddPackage( AddJsonPackage(boost)
NAME Boost
REPO boostorg/boost
TAG boost-1.88.0
ARTIFACT boost-1.88.0-cmake.7z
HASH e5b049e5b61964480ca816395f63f95621e66cb9bcf616a8b10e441e0e69f129e22443acb11e77bc1e8170f8e4171b9b7719891efc43699782bfcd4b3a365f01
GIT_VERSION 1.88.0
VERSION 1.57
EXCLUDE_FROM_ALL ON
)
# really annoying thing where boost::headers doesn't work with cpm # really annoying thing where boost::headers doesn't work with cpm
# TODO(crueter) investigate # TODO(crueter) investigate
@ -419,6 +406,12 @@ if (YUZU_USE_CPM)
if (NOT MSVC) if (NOT MSVC)
# boost sucks # boost sucks
# Solaris (and probably other NIXes) need explicit pthread definition
if (PLATFORM_SUN)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthreads")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthreads")
endif()
target_compile_options(boost_heap INTERFACE -Wno-shadow) target_compile_options(boost_heap INTERFACE -Wno-shadow)
target_compile_options(boost_icl INTERFACE -Wno-shadow) target_compile_options(boost_icl INTERFACE -Wno-shadow)
target_compile_options(boost_asio INTERFACE -Wno-conversion -Wno-implicit-fallthrough) target_compile_options(boost_asio INTERFACE -Wno-conversion -Wno-implicit-fallthrough)
@ -426,128 +419,98 @@ if (YUZU_USE_CPM)
endif() endif()
# fmt # fmt
AddPackage( AddJsonPackage(fmt)
NAME fmt
REPO fmtlib/fmt
SHA 40626af88b
HASH d59f06c24339f223de4ec2afeba1c67b5835a0f350a1ffa86242a72fc3e616a6b8b21798355428d4200c75287308b66634619ffa0b52ba5bd74cc01772ea1a8a
VERSION 8
OPTIONS
"FMT_INSTALL OFF"
EXCLUDE_FROM_ALL ON
)
# lz4 # lz4
AddPackage( AddJsonPackage(lz4)
NAME lz4
REPO lz4/lz4
SHA ebb370ca83
HASH 43600e87b35256005c0f2498fa56a77de6783937ba4cfce38c099f27c03188d097863e8a50c5779ca0a7c63c29c4f7ed0ae526ec798c1fd2e3736861b62e0a37
SOURCE_SUBDIR build/cmake
EXCLUDE_FROM_ALL ON
)
if (lz4_ADDED) if (lz4_ADDED)
add_library(lz4::lz4 ALIAS lz4_static) add_library(lz4::lz4 ALIAS lz4_static)
endif() endif()
# nlohmann # nlohmann
AddPackage( AddJsonPackage(nlohmann)
NAME nlohmann_json
REPO nlohmann/json
SHA 55f93686c0
HASH b739749b066800e21154506ea150d2c5cbce8a45344177f46f884547a1399d26753166fd0df8135269ce28cf223552b1b65cd625b88c844d54753f2434900486
VERSION 3.8
EXCLUDE_FROM_ALL ON
)
# zlib # zlib
AddPackage( AddJsonPackage(zlib)
NAME ZLIB
REPO madler/zlib
SHA 51b7f2abda
HASH 16eaf1f3752489d12fd9ce30f7b5f7cbd5cb8ff53d617005a9847ae72d937f65e01e68be747f62d7ac19fd0c9aeba9956e60f16d6b465c5fdc2f3d08b4db2e6c
VERSION 1.2
OPTIONS
"ZLIB_BUILD_SHARED OFF"
"ZLIB_INSTALL OFF"
EXCLUDE_FROM_ALL ON
)
if (ZLIB_ADDED) if (ZLIB_ADDED)
add_library(ZLIB::ZLIB ALIAS zlibstatic) add_library(ZLIB::ZLIB ALIAS zlibstatic)
endif() endif()
# zstd # zstd
AddPackage( AddJsonPackage(zstd)
NAME zstd
REPO facebook/zstd if (zstd_ADDED)
SHA f8745da6ff add_library(zstd::zstd ALIAS libzstd_static)
HASH 3037007f990040fe32573b46f9bef8762fd5dbeeb07ffffcbfeba51ec98167edae39bb9c87f9299efcd61c4e467c5e84f7c19f0df7799bc1fc04864a278792ee endif()
VERSION 1.5
SOURCE_SUBDIR build/cmake
OPTIONS
"ZSTD_BUILD_SHARED OFF"
EXCLUDE_FROM_ALL ON
)
# Catch2 # Catch2
if (YUZU_TESTS OR DYNARMIC_TESTS) if (YUZU_TESTS OR DYNARMIC_TESTS)
AddPackage( AddJsonPackage(catch2)
NAME Catch2
REPO catchorg/Catch2
SHA 644821ce28
HASH f8795f98acf2c02c0db8e734cc866d5caebab4b4a306e93598b97cb3c0c728dafe8283dce27ffe8d42460e5ae7302f3f32e7e274a7f991b73511ac88eef21b1f
VERSION 3.0.1
EXCLUDE_FROM_ALL ON
)
endif() endif()
# ENet # ENet
AddPackage( AddJsonPackage(enet)
NAME enet
REPO lsalzman/enet
SHA 2662c0de09
VERSION 1.3
HASH 3de1beb4fa3d6b1e03eda8dd1e7580694f854af3ed3975dcdabfdcdf76b97f322b9734d35ea7f185855bb490d957842b938b26da4dd2dfded509390f8d2794dd
FIND_PACKAGE_ARGUMENTS "MODULE"
EXCLUDE_FROM_ALL ON
)
if (enet_ADDED) if (enet_ADDED)
target_include_directories(enet INTERFACE ${enet_SOURCE_DIR}/include) target_include_directories(enet INTERFACE ${enet_SOURCE_DIR}/include)
endif() endif()
# Opus # Opus
AddPackage( AddJsonPackage(opus)
NAME Opus else()
VERSION 1.3 # Enforce the search mode of non-required packages for better and shorter failure messages
REPO "xiph/opus" find_package(fmt 8 REQUIRED)
SHA 5ded705cf4 find_package(LLVM MODULE COMPONENTS Demangle)
HASH 0dc89e58ddda1f3bc6a7037963994770c5806c10e66f5cc55c59286fc76d0544fe4eca7626772b888fd719f434bc8a92f792bdb350c807968b2ac14cfc04b203 find_package(nlohmann_json 3.8 REQUIRED)
FIND_PACKAGE_ARGUMENTS "MODULE" find_package(lz4 REQUIRED)
OPTIONS find_package(RenderDoc MODULE)
"OPUS_BUILD_TESTING OFF" find_package(stb MODULE)
"OPUS_BUILD_PROGRAMS OFF" find_package(enet 1.3 MODULE REQUIRED)
"OPUS_INSTALL_PKG_CONFIG_MODULE OFF" find_package(Opus 1.3 MODULE REQUIRED)
"OPUS_INSTALL_CMAKE_CONFIG_MODULE OFF" find_package(ZLIB 1.2 REQUIRED)
EXCLUDE_FROM_ALL ON find_package(zstd 1.5 REQUIRED MODULE)
)
if(ENABLE_CUBEB) if (YUZU_TESTS)
AddPackage( find_package(Catch2 3.0.1 REQUIRED)
NAME cubeb endif()
REPO "mozilla/cubeb"
SHA fa02160712 if (CMAKE_SYSTEM_NAME STREQUAL "Linux" OR ANDROID)
HASH 82d808356752e4064de48c8fecbe7856715ade1e76b53937116bf07129fc1cc5b3de5e4b408de3cd000187ba8dc32ca4109661cb7e0355a52e54bd81b9be1c61 find_package(gamemode 1.7 MODULE)
FIND_PACKAGE_ARGUMENTS "CONFIG" # not sure this works outside of gentoo endif()
OPTIONS
"USE_SANITIZERS OFF" if (ENABLE_OPENSSL)
"BUILD_TESTS OFF" find_package(OpenSSL 1.1.1 REQUIRED)
"BUILD_TOOLS OFF" endif()
"BUNDLE_SPEEX ON" endif()
EXCLUDE_FROM_ALL ON
) if(NOT TARGET Boost::headers)
AddJsonPackage(boost_headers)
endif()
if (ENABLE_LIBUSB)
if (PLATFORM_FREEBSD)
find_package(libusb MODULE)
else()
find_package(libusb 1.0.24 MODULE)
endif()
endif()
# DiscordRPC
if (USE_DISCORD_PRESENCE)
AddJsonPackage(discord-rpc)
target_include_directories(discord-rpc INTERFACE ${discord-rpc_SOURCE_DIR}/include)
add_library(DiscordRPC::discord-rpc ALIAS discord-rpc)
endif()
# SimpleIni
AddJsonPackage(simpleini)
# Most linux distros don't package cubeb, so enable regardless of cpm settings
if(ENABLE_CUBEB)
AddJsonPackage(cubeb)
if (cubeb_ADDED) if (cubeb_ADDED)
if (NOT MSVC) if (NOT MSVC)
@ -570,97 +533,11 @@ if (YUZU_USE_CPM)
) )
endif() endif()
endif() endif()
endif()
else()
# Enforce the search mode of non-required packages for better and shorter failure messages
find_package(fmt 8 REQUIRED)
find_package(LLVM MODULE COMPONENTS Demangle)
find_package(nlohmann_json 3.8 REQUIRED)
find_package(lz4 REQUIRED)
find_package(RenderDoc MODULE)
find_package(stb MODULE)
find_package(enet 1.3 MODULE REQUIRED)
find_package(Opus 1.3 MODULE REQUIRED)
find_package(ZLIB 1.2 REQUIRED)
find_package(zstd 1.5 REQUIRED)
if (ENABLE_CUBEB)
find_package(cubeb CONFIG)
endif()
if (YUZU_TESTS)
find_package(Catch2 3.0.1 REQUIRED)
endif()
if (CMAKE_SYSTEM_NAME STREQUAL "Linux" OR ANDROID)
find_package(gamemode 1.7 MODULE)
endif()
if (ENABLE_OPENSSL)
find_package(OpenSSL 1.1.1 REQUIRED)
endif()
endif()
if(NOT TARGET Boost::headers)
AddPackage(
NAME boost_headers
REPO "boostorg/headers"
SHA 0456900fad
HASH 50cd75dcdfc5f082225cdace058f47b4fb114a47585f7aee1d22236a910a80b667186254c214fa2fcebac67ae6d37ba4b6e695e1faea8affd6fd42a03cf996e3
BUNDLED_PACKAGE ON
EXCLUDE_FROM_ALL ON
)
endif()
if (ENABLE_LIBUSB)
if (PLATFORM_FREEBSD)
find_package(libusb MODULE)
else()
find_package(libusb 1.0.24 MODULE)
endif()
endif()
# DiscordRPC
if (USE_DISCORD_PRESENCE)
AddPackage(
NAME discord-rpc
REPO "discord/discord-rpc"
SHA 963aa9f3e5
HASH 386e1344e9a666d730f2d335ee3aef1fd05b1039febefd51aa751b705009cc764411397f3ca08dffd46205c72f75b235c870c737b2091a4ed0c3b061f5919bde
OPTIONS
"BUILD_EXAMPLES OFF"
PATCHES
${CMAKE_SOURCE_DIR}/.patch/discord-rpc/0001-cmake-version.patch
${CMAKE_SOURCE_DIR}/.patch/discord-rpc/0002-no-clang-format.patch
${CMAKE_SOURCE_DIR}/.patch/discord-rpc/0003-fix-cpp17.patch
EXCLUDE_FROM_ALL ON
)
target_include_directories(discord-rpc INTERFACE ${discord-rpc_SOURCE_DIR}/include)
add_library(DiscordRPC::discord-rpc ALIAS discord-rpc)
endif()
# SimpleIni
AddPackage(
NAME SimpleIni
REPO brofield/simpleini
SHA 09c21bda1d
HASH 99779ca9b6e040d36558cadf484f9ffdab5b47bcc8fc72e4d33639d1d60c0ceb4410d335ba445d72a4324e455167fd6769d99b459943aa135bec085dff2d4b7c
FIND_PACKAGE_ARGUMENTS "MODULE"
EXCLUDE_FROM_ALL ON
)
# TODO(crueter): Work around this
if (NOT YUZU_USE_EXTERNAL_VULKAN_SPIRV_TOOLS)
find_package(PkgConfig REQUIRED)
pkg_check_modules(SPIRV-Tools REQUIRED SPIRV-Tools)
endif() endif()
# find SDL2 exports a bunch of variables that are needed, so its easier to do this outside of the YUZU_find_package # find SDL2 exports a bunch of variables that are needed, so its easier to do this outside of the YUZU_find_package
if (ENABLE_SDL2) if (ENABLE_SDL2)
# this was hard to get right, but ultimately I decided to make it so that FORCE_DOWNLOAD_SDL2 also downloads the if (YUZU_USE_EXTERNAL_SDL2)
# external one. Really silly behavior imo but in the interest of getting something out there I'm leaving it for now
if (YUZU_USE_EXTERNAL_SDL2 OR FORCE_DOWNLOAD_SDL2)
message(STATUS "Using SDL2 from externals.") message(STATUS "Using SDL2 from externals.")
if (NOT WIN32) if (NOT WIN32)
# Yuzu itself needs: Atomic Audio Events Joystick Haptic Sensor Threads Timers # Yuzu itself needs: Atomic Audio Events Joystick Haptic Sensor Threads Timers
@ -683,37 +560,14 @@ if (ENABLE_SDL2)
endif() endif()
if ("${YUZU_SYSTEM_PROFILE}" STREQUAL "steamdeck") if ("${YUZU_SYSTEM_PROFILE}" STREQUAL "steamdeck")
set(SDL_HASH cc016b0046)
set(SDL_PIPEWIRE OFF) # build errors out with this on set(SDL_PIPEWIRE OFF) # build errors out with this on
set(SDL_SHA512SUM 34d5ef58da6a4f9efa6689c82f67badcbd741f5a4f562a9c2c30828fa839830fb07681c5dc6a7851520e261c8405a416ac0a2c2513b51984fb3b4fa4dcb3e20b) AddJsonPackage("sdl2_steamdeck")
else() else()
set(SDL_HASH 54772f345a) AddJsonPackage("sdl2_generic")
set(SDL_SHA512SUM 2a68a0e01c390043aa9d9df63d8a20a52076c88bb460ac4e0f33194ca7d9bc8fadbbcc04e7506872ac4b6354a73fbc267c036f82200da59465789b87c7d9e3a4)
endif() endif()
elseif (YUZU_USE_BUNDLED_SDL2)
AddPackage(
NAME SDL2
REPO "libsdl-org/SDL"
SHA ${SDL_HASH}
HASH ${SDL_SHA512SUM}
KEY ${YUZU_SYSTEM_PROFILE}
BUNDLED_PACKAGE ON
EXCLUDE_FROM_ALL ON
)
endif()
if (YUZU_USE_BUNDLED_SDL2)
message(STATUS "Using bundled SDL2") message(STATUS "Using bundled SDL2")
AddCIPackage( AddJsonPackage(sdl2)
PACKAGE SDL2
NAME SDL2
REPO crueter-ci/SDL2
VERSION 2.32.8
MIN_VERSION 2.26.4
CMAKE_FILENAME sdl2
FORCE_DOWNLOAD ${FORCE_DOWNLOAD_SDL2}
TARGET "SDL2::SDL2"
)
endif() endif()
find_package(SDL2 2.26.4 REQUIRED) find_package(SDL2 2.26.4 REQUIRED)
@ -750,6 +604,7 @@ add_subdirectory(externals)
find_package(VulkanHeaders) find_package(VulkanHeaders)
find_package(VulkanUtilityLibraries) find_package(VulkanUtilityLibraries)
find_package(VulkanMemoryAllocator) find_package(VulkanMemoryAllocator)
find_package(SPIRV-Tools)
if (ENABLE_WEB_SERVICE) if (ENABLE_WEB_SERVICE)
find_package(httplib) find_package(httplib)
@ -1003,14 +858,14 @@ endif()
# https://specifications.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-latest.html # https://specifications.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-latest.html
# https://www.freedesktop.org/software/appstream/docs/ # https://www.freedesktop.org/software/appstream/docs/
if(ENABLE_QT AND UNIX AND NOT APPLE) if(ENABLE_QT AND UNIX AND NOT APPLE)
install(FILES "dist/org.eden_emu.eden.desktop" install(FILES "dist/dev.eden_emu.eden.desktop"
DESTINATION "share/applications") DESTINATION "share/applications")
install(FILES "dist/org.eden_emu.eden.svg" install(FILES "dist/dev.eden_emu.eden.svg"
DESTINATION "share/icons/hicolor/scalable/apps") DESTINATION "share/icons/hicolor/scalable/apps")
# TODO: these files need to be updated. # TODO: these files need to be updated.
install(FILES "dist/org.eden_emu.eden.xml" install(FILES "dist/dev.eden_emu.eden.xml"
DESTINATION "share/mime/packages") DESTINATION "share/mime/packages")
install(FILES "dist/org.eden_emu.eden.metainfo.xml" install(FILES "dist/dev.eden_emu.eden.metainfo.xml"
DESTINATION "share/metainfo") DESTINATION "share/metainfo")
endif() endif()

View file

@ -1,28 +1,220 @@
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project # SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: Copyright 2025 crueter
# SPDX-License-Identifier: GPL-3.0-or-later
# Created-By: crueter # Created-By: crueter
# Docs will come at a later date, mostly this is to just reduce boilerplate # Docs will come at a later date, mostly this is to just reduce boilerplate
# and some cmake magic to allow for runtime viewing of dependency versions # and some cmake magic to allow for runtime viewing of dependency versions
include(CMakeDependentOption) # Future crueter: Wow this was a lie and a half, at this point I might as well make my own CPN
# haha just kidding... unless?
if (MSVC OR ANDROID) if (MSVC OR ANDROID)
set(SYSTEM_DEFAULT OFF) set(BUNDLED_DEFAULT OFF)
else() else()
set(SYSTEM_DEFAULT ON) set(BUNDLED_DEFAULT ON)
endif() endif()
CMAKE_DEPENDENT_OPTION(CPMUTIL_DEFAULT_SYSTEM option(CPMUTIL_FORCE_BUNDLED
"Allow usage of system packages for CPM dependencies" ${SYSTEM_DEFAULT} "Force bundled packages for all CPM depdendencies" ${BUNDLED_DEFAULT})
"NOT ANDROID" OFF)
option(CPMUTIL_FORCE_SYSTEM
"Force system packages for all CPM dependencies (NOT RECOMMENDED)" OFF)
cmake_minimum_required(VERSION 3.22) cmake_minimum_required(VERSION 3.22)
include(CPM) include(CPM)
# TODO(crueter): Better solution for separate cpmfiles e.g. per-directory
set(CPMUTIL_JSON_FILE "${CMAKE_CURRENT_SOURCE_DIR}/cpmfile.json" CACHE STRING "Location of cpmfile.json")
if (EXISTS ${CPMUTIL_JSON_FILE})
file(READ ${CPMUTIL_JSON_FILE} CPMFILE_CONTENT)
else()
message(WARNING "[CPMUtil] cpmfile ${CPMUTIL_JSON_FILE} does not exist, AddJsonPackage will be a no-op")
endif()
# utility
function(cpm_utils_message level name message) function(cpm_utils_message level name message)
message(${level} "[CPMUtil] ${name}: ${message}") message(${level} "[CPMUtil] ${name}: ${message}")
endfunction() endfunction()
# utility
function(array_to_list array length out)
math(EXPR range "${length} - 1")
foreach(IDX RANGE ${range})
string(JSON _element GET "${array}" "${IDX}")
list(APPEND NEW_LIST ${_element})
endforeach()
set("${out}" "${NEW_LIST}" PARENT_SCOPE)
endfunction()
# utility
function(get_json_element object out member default)
string(JSON out_type ERROR_VARIABLE err TYPE "${object}" ${member})
if (err)
set("${out}" "${default}" PARENT_SCOPE)
return()
endif()
string(JSON outvar GET "${object}" ${member})
if (out_type STREQUAL "ARRAY")
string(JSON _len LENGTH "${object}" ${member})
# array_to_list("${outvar}" ${_len} outvar)
set("${out}_LENGTH" "${_len}" PARENT_SCOPE)
endif()
set("${out}" "${outvar}" PARENT_SCOPE)
endfunction()
# Kinda cancerous but whatever
function(AddJsonPackage)
set(oneValueArgs
NAME
# these are overrides that can be generated at runtime, so can be defined separately from the json
DOWNLOAD_ONLY
SYSTEM_PACKAGE
BUNDLED_PACKAGE
)
set(multiValueArgs OPTIONS)
cmake_parse_arguments(JSON "" "${oneValueArgs}" "${multiValueArgs}"
"${ARGN}")
list(LENGTH ARGN argnLength)
# single name argument
if(argnLength EQUAL 1)
set(JSON_NAME "${ARGV0}")
endif()
if (NOT DEFINED CPMFILE_CONTENT)
cpm_utils_message(WARNING ${name} "No cpmfile, AddJsonPackage is a no-op")
return()
endif()
if (NOT DEFINED JSON_NAME)
cpm_utils_message(FATAL_ERROR "json package" "No name specified")
endif()
string(JSON object ERROR_VARIABLE err GET "${CPMFILE_CONTENT}" "${JSON_NAME}")
if (err)
cpm_utils_message(FATAL_ERROR ${JSON_NAME} "Not found in cpmfile")
endif()
get_json_element("${object}" package package ${JSON_NAME})
get_json_element("${object}" repo repo "")
get_json_element("${object}" ci ci OFF)
get_json_element("${object}" version version "")
if (ci)
get_json_element("${object}" name name "${JSON_NAME}")
get_json_element("${object}" extension extension "tar.zst")
get_json_element("${object}" min_version min_version "")
get_json_element("${object}" cmake_filename cmake_filename "")
get_json_element("${object}" raw_disabled disabled_platforms "")
if (raw_disabled)
array_to_list("${raw_disabled}" ${raw_disabled_LENGTH} disabled_platforms)
else()
set(disabled_platforms "")
endif()
AddCIPackage(
VERSION ${version}
NAME ${name}
REPO ${repo}
PACKAGE ${package}
EXTENSION ${extension}
MIN_VERSION ${min_version}
DISABLED_PLATFORMS ${disabled_platforms}
CMAKE_FILENAME ${cmake_filename}
)
return()
endif()
get_json_element("${object}" hash hash "")
get_json_element("${object}" sha sha "")
get_json_element("${object}" url url "")
get_json_element("${object}" key key "")
get_json_element("${object}" tag tag "")
get_json_element("${object}" artifact artifact "")
get_json_element("${object}" git_version git_version "")
get_json_element("${object}" source_subdir source_subdir "")
get_json_element("${object}" bundled bundled "unset")
get_json_element("${object}" find_args find_args "")
get_json_element("${object}" raw_patches patches "")
# format patchdir
if (raw_patches)
math(EXPR range "${raw_patches_LENGTH} - 1")
foreach(IDX RANGE ${range})
string(JSON _patch GET "${raw_patches}" "${IDX}")
set(full_patch "${CMAKE_SOURCE_DIR}/.patch/${JSON_NAME}/${_patch}")
if (NOT EXISTS ${full_patch})
cpm_utils_message(FATAL_ERROR ${JSON_NAME} "specifies patch ${full_patch} which does not exist")
endif()
list(APPEND patches "${full_patch}")
endforeach()
endif()
# end format patchdir
# options
get_json_element("${object}" raw_options options "")
if (raw_options)
array_to_list("${raw_options}" ${raw_options_LENGTH} options)
endif()
set(options ${options} ${JSON_OPTIONS})
# end options
# system/bundled
if (bundled STREQUAL "unset" AND DEFINED JSON_BUNDLED_PACKAGE)
set(bundled ${JSON_BUNDLED_PACKAGE})
endif()
AddPackage(
NAME "${package}"
VERSION "${version}"
URL "${url}"
HASH "${hash}"
SHA "${sha}"
REPO "${repo}"
KEY "${key}"
PATCHES "${patches}"
OPTIONS "${options}"
FIND_PACKAGE_ARGUMENTS "${find_args}"
BUNDLED_PACKAGE "${bundled}"
SOURCE_SUBDIR "${source_subdir}"
GIT_VERSION ${git_version}
ARTIFACT ${artifact}
TAG ${tag}
)
# pass stuff to parent scope
set(${package}_ADDED "${${package}_ADDED}"
PARENT_SCOPE)
set(${package}_SOURCE_DIR "${${package}_SOURCE_DIR}"
PARENT_SCOPE)
set(${package}_BINARY_DIR "${${package}_BINARY_DIR}"
PARENT_SCOPE)
endfunction()
function(AddPackage) function(AddPackage)
cpm_set_policies() cpm_set_policies()
@ -64,10 +256,8 @@ function(AddPackage)
GIT_URL GIT_URL
KEY KEY
DOWNLOAD_ONLY
FIND_PACKAGE_ARGUMENTS
SYSTEM_PACKAGE
BUNDLED_PACKAGE BUNDLED_PACKAGE
FIND_PACKAGE_ARGUMENTS
) )
set(multiValueArgs OPTIONS PATCHES) set(multiValueArgs OPTIONS PATCHES)
@ -79,6 +269,9 @@ function(AddPackage)
cpm_utils_message(FATAL_ERROR "package" "No package name defined") cpm_utils_message(FATAL_ERROR "package" "No package name defined")
endif() endif()
option(${PKG_ARGS_NAME}_FORCE_SYSTEM "Force the system package for ${PKG_ARGS_NAME}")
option(${PKG_ARGS_NAME}_FORCE_BUNDLED "Force the bundled package for ${PKG_ARGS_NAME}")
if (DEFINED PKG_ARGS_URL) if (DEFINED PKG_ARGS_URL)
set(pkg_url ${PKG_ARGS_URL}) set(pkg_url ${PKG_ARGS_URL})
@ -124,9 +317,9 @@ function(AddPackage)
cpm_utils_message(STATUS ${PKG_ARGS_NAME} "Download URL is ${pkg_url}") cpm_utils_message(STATUS ${PKG_ARGS_NAME} "Download URL is ${pkg_url}")
if (DEFINED PKG_ARGS_GIT_VERSION) if (DEFINED PKG_ARGS_GIT_VERSION)
set(git_version ${PKG_ARGS_VERSION})
elseif(DEFINED PKG_ARGS_VERSION)
set(git_version ${PKG_ARGS_GIT_VERSION}) set(git_version ${PKG_ARGS_GIT_VERSION})
elseif(DEFINED PKG_ARGS_VERSION)
set(git_version ${PKG_ARGS_VERSION})
endif() endif()
if (NOT DEFINED PKG_ARGS_KEY) if (NOT DEFINED PKG_ARGS_KEY)
@ -178,25 +371,55 @@ function(AddPackage)
if (DEFINED hash_url) if (DEFINED hash_url)
set(outfile ${CMAKE_CURRENT_BINARY_DIR}/${PKG_ARGS_NAME}.hash) set(outfile ${CMAKE_CURRENT_BINARY_DIR}/${PKG_ARGS_NAME}.hash)
# TODO(crueter): This is kind of a bad solution
# because "technically" the hash is invalidated each week
# but it works for now kjsdnfkjdnfjksdn
string(TOLOWER ${PKG_ARGS_NAME} lowername)
if (NOT EXISTS ${outfile} AND NOT EXISTS ${CPM_SOURCE_CACHE}/${lowername}/${pkg_key})
file(DOWNLOAD ${hash_url} ${outfile}) file(DOWNLOAD ${hash_url} ${outfile})
file(READ ${outfile} pkg_hash_tmp) endif()
file(REMOVE ${outfile})
if (EXISTS ${outfile})
file(READ ${outfile} pkg_hash_tmp)
endif()
if (DEFINED ${pkg_hash_tmp})
set(pkg_hash "${hash_algo}=${pkg_hash_tmp}") set(pkg_hash "${hash_algo}=${pkg_hash_tmp}")
endif() endif()
if (NOT CPMUTIL_DEFAULT_SYSTEM)
set(CPM_USE_LOCAL_PACKAGES OFF)
elseif (DEFINED PKG_ARGS_SYSTEM_PACKAGE)
set(CPM_USE_LOCAL_PACKAGES ${PKG_ARGS_SYSTEM_PACKAGE})
elseif (DEFINED PKG_ARGS_BUNDLED_PACKAGE)
if (PKG_ARGS_BUNDLED_PACKAGE)
set(CPM_USE_LOCAL_PACKAGES OFF)
else()
set(CPM_USE_LOCAL_PACKAGES ON)
endif() endif()
macro(set_precedence local force)
set(CPM_USE_LOCAL_PACKAGES ${local})
set(CPM_LOCAL_PACKAGES_ONLY ${force})
endmacro()
#[[
Precedence:
- package_FORCE_SYSTEM
- package_FORCE_BUNDLED
- CPMUTIL_FORCE_SYSTEM
- CPMUTIL_FORCE_BUNDLED
- BUNDLED_PACKAGE
- default to allow local
]]#
if (${PKG_ARGS_NAME}_FORCE_SYSTEM)
set_precedence(ON ON)
elseif (${PKG_ARGS_NAME}_FORCE_BUNDLED)
set_precedence(OFF OFF)
elseif (CPMUTIL_FORCE_SYSTEM)
set_precedence(ON ON)
elseif(CPMUTIL_FORCE_BUNDLED)
set_precedence(OFF OFF)
elseif (DEFINED PKG_ARGS_BUNDLED_PACKAGE AND NOT PKG_ARGS_BUNDLED_PACKAGE STREQUAL "unset")
if (PKG_ARGS_BUNDLED_PACKAGE)
set(local OFF)
else() else()
set(CPM_USE_LOCAL_PACKAGES ON) set(local ON)
endif()
set_precedence(${local} OFF)
else()
set_precedence(ON OFF)
endif() endif()
CPMAddPackage( CPMAddPackage(
@ -210,6 +433,7 @@ function(AddPackage)
OPTIONS ${PKG_ARGS_OPTIONS} OPTIONS ${PKG_ARGS_OPTIONS}
PATCHES ${PKG_ARGS_PATCHES} PATCHES ${PKG_ARGS_PATCHES}
EXCLUDE_FROM_ALL ON
${PKG_ARGS_UNPARSED_ARGUMENTS} ${PKG_ARGS_UNPARSED_ARGUMENTS}
) )
@ -257,7 +481,7 @@ function(add_ci_package key)
set(ARTIFACT ${ARTIFACT_NAME}-${key}-${ARTIFACT_VERSION}.${ARTIFACT_EXT}) set(ARTIFACT ${ARTIFACT_NAME}-${key}-${ARTIFACT_VERSION}.${ARTIFACT_EXT})
AddPackage( AddPackage(
NAME ${ARTIFACT_PACKAGE}-${key} NAME ${ARTIFACT_PACKAGE}
REPO ${ARTIFACT_REPO} REPO ${ARTIFACT_REPO}
TAG v${ARTIFACT_VERSION} TAG v${ARTIFACT_VERSION}
VERSION ${ARTIFACT_VERSION} VERSION ${ARTIFACT_VERSION}
@ -266,15 +490,12 @@ function(add_ci_package key)
KEY ${key} KEY ${key}
HASH_SUFFIX sha512sum HASH_SUFFIX sha512sum
BUNDLED_PACKAGE ON BUNDLED_PACKAGE ON
DOWNLOAD_ONLY ON
) )
if (NOT ARTIFACT_FORCE_DOWNLOAD OR ARTIFACT_OVERRIDE) set(ARTIFACT_DIR ${${ARTIFACT_PACKAGE}_SOURCE_DIR} PARENT_SCOPE)
set(ARTIFACT_DIR ${${ARTIFACT_PACKAGE}-${key}_SOURCE_DIR} PARENT_SCOPE)
endif()
endfunction() endfunction()
# TODO(crueter): doc # name is the artifact name, package is for find_package override
function(AddCIPackage) function(AddCIPackage)
set(oneValueArgs set(oneValueArgs
VERSION VERSION
@ -282,11 +503,9 @@ function(AddCIPackage)
REPO REPO
PACKAGE PACKAGE
EXTENSION EXTENSION
FORCE_DOWNLOAD
MIN_VERSION MIN_VERSION
DISABLED_PLATFORMS DISABLED_PLATFORMS
CMAKE_FILENAME CMAKE_FILENAME
TARGET
) )
cmake_parse_arguments(PKG_ARGS "" "${oneValueArgs}" "" ${ARGN}) cmake_parse_arguments(PKG_ARGS "" "${oneValueArgs}" "" ${ARGN})
@ -316,12 +535,6 @@ function(AddCIPackage)
set(ARTIFACT_EXT ${PKG_ARGS_EXTENSION}) set(ARTIFACT_EXT ${PKG_ARGS_EXTENSION})
endif() endif()
if(NOT DEFINED PKG_ARGS_FORCE_DOWNLOAD)
set(ARTIFACT_FORCE_DOWNLOAD OFF)
else()
set(ARTIFACT_FORCE_DOWNLOAD ${PKG_ARGS_FORCE_DOWNLOAD})
endif()
if (DEFINED PKG_ARGS_MIN_VERSION) if (DEFINED PKG_ARGS_MIN_VERSION)
set(ARTIFACT_MIN_VERSION ${PKG_ARGS_MIN_VERSION}) set(ARTIFACT_MIN_VERSION ${PKG_ARGS_MIN_VERSION})
endif() endif()
@ -336,86 +549,42 @@ function(AddCIPackage)
set(ARTIFACT_REPO ${PKG_ARGS_REPO}) set(ARTIFACT_REPO ${PKG_ARGS_REPO})
set(ARTIFACT_PACKAGE ${PKG_ARGS_PACKAGE}) set(ARTIFACT_PACKAGE ${PKG_ARGS_PACKAGE})
if ((MSVC AND ARCHITECTURE_x86_64) OR ARTIFACT_FORCE_DOWNLOAD AND NOT "windows-amd64" IN_LIST DISABLED_PLATFORMS) if ((MSVC AND ARCHITECTURE_x86_64) AND NOT "windows-amd64" IN_LIST DISABLED_PLATFORMS)
# kinda hacky
if(MSVC AND ARCHITECTURE_x86_64)
set(ARTIFACT_OVERRIDE ON)
endif()
add_ci_package(windows-amd64) add_ci_package(windows-amd64)
set(ARTIFACT_OVERRIDE OFF)
endif()
if ((MSVC AND ARCHITECTURE_arm64) OR ARTIFACT_FORCE_DOWNLOAD AND NOT "windows-arm64" IN_LIST DISABLED_PLATFORMS)
if(MSVC AND ARCHITECTURE_arm64)
set(ARTIFACT_OVERRIDE ON)
endif() endif()
if ((MSVC AND ARCHITECTURE_arm64) AND NOT "windows-arm64" IN_LIST DISABLED_PLATFORMS)
add_ci_package(windows-arm64) add_ci_package(windows-arm64)
set(ARTIFACT_OVERRIDE OFF)
endif()
if (ANDROID OR ARTIFACT_FORCE_DOWNLOAD AND NOT "android" IN_LIST DISABLED_PLATFORMS)
if(ANDROID)
set(ARTIFACT_OVERRIDE ON)
endif() endif()
if (ANDROID AND NOT "android" IN_LIST DISABLED_PLATFORMS)
add_ci_package(android) add_ci_package(android)
set(ARTIFACT_OVERRIDE OFF)
endif()
if(PLATFORM_SUN OR ARTIFACT_FORCE_DOWNLOAD AND NOT "solaris" IN_LIST DISABLED_PLATFORMS)
if(PLATFORM_SUN)
set(ARTIFACT_OVERRIDE ON)
endif() endif()
if(PLATFORM_SUN AND NOT "solaris" IN_LIST DISABLED_PLATFORMS)
add_ci_package(solaris) add_ci_package(solaris)
set(ARTIFACT_OVERRIDE OFF)
endif()
if(PLATFORM_FREEBSD OR ARTIFACT_FORCE_DOWNLOAD AND NOT "freebsd" IN_LIST DISABLED_PLATFORMS)
if(PLATFORM_FREEBSD)
set(ARTIFACT_OVERRIDE ON)
endif() endif()
if(PLATFORM_FREEBSD AND NOT "freebsd" IN_LIST DISABLED_PLATFORMS)
add_ci_package(freebsd) add_ci_package(freebsd)
set(ARTIFACT_OVERRIDE OFF)
endif()
if((PLATFORM_LINUX AND ARCHITECTURE_x86_64) OR ARTIFACT_FORCE_DOWNLOAD AND NOT "linux" IN_LIST DISABLED_PLATFORMS)
if(PLATFORM_LINUX AND ARCHITECTURE_x86_64)
set(ARTIFACT_OVERRIDE ON)
endif() endif()
if((PLATFORM_LINUX AND ARCHITECTURE_x86_64) AND NOT "linux" IN_LIST DISABLED_PLATFORMS)
add_ci_package(linux) add_ci_package(linux)
set(ARTIFACT_OVERRIDE OFF)
endif()
if((PLATFORM_LINUX AND ARCHITECTURE_arm64) OR ARTIFACT_FORCE_DOWNLOAD AND NOT "linux-aarch64" IN_LIST DISABLED_PLATFORMS)
if(PLATFORM_LINUX AND ARCHITECTURE_arm64)
set(ARTIFACT_OVERRIDE ON)
endif() endif()
if((PLATFORM_LINUX AND ARCHITECTURE_arm64) AND NOT "linux-aarch64" IN_LIST DISABLED_PLATFORMS)
add_ci_package(linux-aarch64) add_ci_package(linux-aarch64)
set(ARTIFACT_OVERRIDE OFF)
endif() endif()
if (DEFINED ARTIFACT_DIR) if (DEFINED ARTIFACT_DIR)
if (NOT DEFINED PKG_ARGS_TARGET OR NOT TARGET "${PKG_ARGS_TARGET}")
include(${ARTIFACT_DIR}/${ARTIFACT_CMAKE}.cmake) include(${ARTIFACT_DIR}/${ARTIFACT_CMAKE}.cmake)
# Overrides find package
CPMAddPackage(
NAME ${ARTIFACT_PACKAGE}
SOURCE_DIR ${ARTIFACT_DIR}
)
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_NAMES ${ARTIFACT_NAME}) set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_NAMES ${ARTIFACT_NAME})
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_URLS "https://github.com/${ARTIFACT_REPO}") # TODO(crueter) other hosts? set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_URLS "https://github.com/${ARTIFACT_REPO}") # TODO(crueter) other hosts?
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS ${ARTIFACT_VERSION}) set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS ${ARTIFACT_VERSION})
set(${ARTIFACT_PACKAGE}_ADDED TRUE PARENT_SCOPE) set(${ARTIFACT_PACKAGE}_ADDED TRUE PARENT_SCOPE)
endif()
else() else()
find_package(${ARTIFACT_PACKAGE} ${ARTIFACT_MIN_VERSION} REQUIRED) find_package(${ARTIFACT_PACKAGE} ${ARTIFACT_MIN_VERSION} REQUIRED)
endif() endif()

View file

@ -29,6 +29,7 @@ function(download_bundled_external remote_path lib_name cpm_key prefix_var versi
set(package_url "${package_base_url}${package_repo}") set(package_url "${package_base_url}${package_repo}")
set(full_url ${package_url}${remote_path}${lib_name}${package_extension}) set(full_url ${package_url}${remote_path}${lib_name}${package_extension})
# TODO(crueter): DELETE THIS ENTIRELY, GLORY BE TO THE CI!
AddPackage( AddPackage(
NAME ${cpm_key} NAME ${cpm_key}
VERSION ${version} VERSION ${version}

View file

@ -0,0 +1,19 @@
# SPDX-FileCopyrightText: 2022 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
include(FindPackageHandleStandardArgs)
find_package(PkgConfig QUIET)
pkg_search_module(SPIRV-Tools QUIET IMPORTED_TARGET SPIRV-Tools)
find_package_handle_standard_args(SPIRV-Tools
REQUIRED_VARS SPIRV-Tools_LINK_LIBRARIES
VERSION_VAR SPIRV-Tools_VERSION
)
if (SPIRV-Tools_FOUND AND NOT TARGET SPIRV-Tools::SPIRV-Tools)
if (TARGET SPIRV-Tools)
add_library(SPIRV-Tools::SPIRV-Tools ALIAS SPIRV-Tools)
else()
add_library(SPIRV-Tools::SPIRV-Tools ALIAS PkgConfig::SPIRV-Tools)
endif()
endif()

View file

@ -3,17 +3,12 @@
include(FindPackageHandleStandardArgs) include(FindPackageHandleStandardArgs)
find_package(zstd QUIET CONFIG) find_package(PkgConfig QUIET)
if (zstd_CONSIDERED_CONFIGS) pkg_search_module(ZSTD QUIET IMPORTED_TARGET libzstd)
find_package_handle_standard_args(zstd CONFIG_MODE) find_package_handle_standard_args(zstd
else()
find_package(PkgConfig QUIET)
pkg_search_module(ZSTD QUIET IMPORTED_TARGET libzstd)
find_package_handle_standard_args(zstd
REQUIRED_VARS ZSTD_LINK_LIBRARIES REQUIRED_VARS ZSTD_LINK_LIBRARIES
VERSION_VAR ZSTD_VERSION VERSION_VAR ZSTD_VERSION
) )
endif()
if (zstd_FOUND AND NOT TARGET zstd::zstd) if (zstd_FOUND AND NOT TARGET zstd::zstd)
if (TARGET zstd::libzstd_shared) if (TARGET zstd::libzstd_shared)

147
cpmfile.json Normal file
View file

@ -0,0 +1,147 @@
{
"openssl": {
"ci": true,
"package": "OpenSSL",
"name": "openssl",
"repo": "crueter-ci/OpenSSL",
"version": "3.5.2",
"min_version": "1.1.1"
},
"boost": {
"package": "Boost",
"repo": "boostorg/boost",
"tag": "boost-1.88.0",
"artifact": "boost-1.88.0-cmake.7z",
"hash": "e5b049e5b61964480ca816395f63f95621e66cb9bcf616a8b10e441e0e69f129e22443acb11e77bc1e8170f8e4171b9b7719891efc43699782bfcd4b3a365f01",
"git_version": "1.88.0",
"version": "1.57"
},
"fmt": {
"repo": "fmtlib/fmt",
"sha": "40626af88b",
"hash": "d59f06c24339f223de4ec2afeba1c67b5835a0f350a1ffa86242a72fc3e616a6b8b21798355428d4200c75287308b66634619ffa0b52ba5bd74cc01772ea1a8a",
"version": "8",
"options": [
"FMT_INSTALL OFF"
]
},
"lz4": {
"name": "lz4",
"repo": "lz4/lz4",
"sha": "ebb370ca83",
"hash": "43600e87b35256005c0f2498fa56a77de6783937ba4cfce38c099f27c03188d097863e8a50c5779ca0a7c63c29c4f7ed0ae526ec798c1fd2e3736861b62e0a37",
"source_subdir": "build/cmake"
},
"nlohmann": {
"package": "nlohmann_json",
"repo": "nlohmann/json",
"sha": "55f93686c0",
"hash": "b739749b066800e21154506ea150d2c5cbce8a45344177f46f884547a1399d26753166fd0df8135269ce28cf223552b1b65cd625b88c844d54753f2434900486",
"version": "3.8"
},
"zlib": {
"package": "ZLIB",
"repo": "madler/zlib",
"sha": "51b7f2abda",
"hash": "16eaf1f3752489d12fd9ce30f7b5f7cbd5cb8ff53d617005a9847ae72d937f65e01e68be747f62d7ac19fd0c9aeba9956e60f16d6b465c5fdc2f3d08b4db2e6c",
"version": "1.2",
"options": [
"ZLIB_BUILD_SHARED OFF",
"ZLIB_INSTALL OFF"
]
},
"zstd": {
"repo": "facebook/zstd",
"sha": "f8745da6ff",
"hash": "3037007f990040fe32573b46f9bef8762fd5dbeeb07ffffcbfeba51ec98167edae39bb9c87f9299efcd61c4e467c5e84f7c19f0df7799bc1fc04864a278792ee",
"version": "1.5",
"source_subdir": "build/cmake",
"find_args": "MODULE",
"options": [
"ZSTD_BUILD_SHARED OFF"
]
},
"catch2": {
"package": "Catch2",
"repo": "catchorg/Catch2",
"sha": "644821ce28",
"hash": "f8795f98acf2c02c0db8e734cc866d5caebab4b4a306e93598b97cb3c0c728dafe8283dce27ffe8d42460e5ae7302f3f32e7e274a7f991b73511ac88eef21b1f",
"version": "3.0.1"
},
"enet": {
"repo": "lsalzman/enet",
"sha": "2662c0de09",
"hash": "3de1beb4fa3d6b1e03eda8dd1e7580694f854af3ed3975dcdabfdcdf76b97f322b9734d35ea7f185855bb490d957842b938b26da4dd2dfded509390f8d2794dd",
"version": "1.3",
"find_args": "MODULE"
},
"opus": {
"package": "Opus",
"repo": "xiph/opus",
"sha": "5ded705cf4",
"hash": "0dc89e58ddda1f3bc6a7037963994770c5806c10e66f5cc55c59286fc76d0544fe4eca7626772b888fd719f434bc8a92f792bdb350c807968b2ac14cfc04b203",
"version": "1.3",
"find_args": "MODULE",
"options": [
"OPUS_BUILD_TESTING OFF",
"OPUS_BUILD_PROGRAMS OFF",
"OPUS_INSTALL_PKG_CONFIG_MODULE OFF",
"OPUS_INSTALL_CMAKE_CONFIG_MODULE OFF"
]
},
"cubeb": {
"repo": "mozilla/cubeb",
"sha": "fa02160712",
"hash": "82d808356752e4064de48c8fecbe7856715ade1e76b53937116bf07129fc1cc5b3de5e4b408de3cd000187ba8dc32ca4109661cb7e0355a52e54bd81b9be1c61",
"find_args": "CONFIG",
"options": [
"USE_SANITIZERS OFF",
"BUILD_TESTS OFF",
"BUILD_TOOLS OFF",
"BUNDLE_SPEEX ON"
]
},
"boost_headers": {
"repo": "boostorg/headers",
"sha": "0456900fad",
"hash": "50cd75dcdfc5f082225cdace058f47b4fb114a47585f7aee1d22236a910a80b667186254c214fa2fcebac67ae6d37ba4b6e695e1faea8affd6fd42a03cf996e3",
"bundled": true
},
"discord-rpc": {
"repo": "eden-emulator/discord-rpc",
"sha": "1cf7772bb6",
"hash": "e9b35e6f2c075823257bcd59f06fe7bb2ccce1976f44818d2e28810435ef79c712a3c4f20f40da41f691342a4058cf86b078eb7f9d9e4dae83c0547c21ec4f97"
},
"simpleini": {
"package": "SimpleIni",
"repo": "brofield/simpleini",
"sha": "09c21bda1d",
"hash": "99779ca9b6e040d36558cadf484f9ffdab5b47bcc8fc72e4d33639d1d60c0ceb4410d335ba445d72a4324e455167fd6769d99b459943aa135bec085dff2d4b7c",
"find_args": "MODULE"
},
"sdl2_generic": {
"package": "SDL2",
"repo": "libsdl-org/SDL",
"sha": "54772f345a",
"hash": "2a68a0e01c390043aa9d9df63d8a20a52076c88bb460ac4e0f33194ca7d9bc8fadbbcc04e7506872ac4b6354a73fbc267c036f82200da59465789b87c7d9e3a4",
"key": "generic",
"bundled": true
},
"sdl2_steamdeck": {
"package": "SDL2",
"repo": "libsdl-org/SDL",
"sha": "cc016b0046",
"hash": "34d5ef58da6a4f9efa6689c82f67badcbd741f5a4f562a9c2c30828fa839830fb07681c5dc6a7851520e261c8405a416ac0a2c2513b51984fb3b4fa4dcb3e20b",
"key": "steamdeck",
"bundled": true
},
"sdl2": {
"ci": true,
"package": "SDL2",
"name": "SDL2",
"repo": "crueter-ci/SDL2",
"version": "2.32.8",
"min_version": "2.26.4",
"cmake_filename": "sdl2"
}
}

View file

@ -10,7 +10,7 @@ Type=Application
Name=Eden Name=Eden
GenericName=Switch Emulator GenericName=Switch Emulator
Comment=Nintendo Switch video game console emulator Comment=Nintendo Switch video game console emulator
Icon=org.eden_emu.eden Icon=dev.eden_emu.eden
TryExec=eden TryExec=eden
Exec=eden %f Exec=eden %f
Categories=Game;Emulator;Qt; Categories=Game;Emulator;Qt;

View file

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

Before After
Before After

252
docs/CPM.md Normal file
View file

@ -0,0 +1,252 @@
# CPM
CPM (CMake Package Manager) is the preferred method of managing dependencies within Eden.
Global Options:
- `YUZU_USE_CPM` is set by default on MSVC and Android. Other platforms should use this if certain "required" system dependencies (e.g. OpenSSL) are broken or missing
* If this is `OFF`, required system dependencies will be searched via `find_package`, although certain externals use CPM regardless.
- `CPMUTIL_FORCE_SYSTEM` (default `OFF`): Require all CPM dependencies to use system packages. NOT RECOMMENDED!
* Many packages, e.g. mcl, sirit, xbyak, discord-rpc, are not generally available as a system package.
* You may optionally override these (see CPMUtil section)
- `CPMUTIL_FORCE_BUNDLED` (default `ON` on MSVC and Android, `OFF` elsewhere): Require all CPM dependencies to use bundled packages.
## CPMUtil
CPMUtil is a wrapper around CPM that aims to reduce boilerplate and add useful utility functions to make dependency management a piece of cake.
### AddPackage
`AddPackage` is the core of the CPMUtil wrapper, and is generally the lowest level you will need to go when dealing with dependencies.
**Identification/Fetching**
- `NAME` (required): The package name (must be the same as the `find_package` name if applicable)
- `VERSION`: The minimum version of this package that can be used on the system
- `GIT_VERSION`: The version found within git, only used for identification
- `URL`: The URL to fetch.
- `REPO`: The GitHub repo to use (`owner/repo`).
* Only GitHub is supported for now, though other platforms will see support at some point
- `TAG`: The tag to fetch, if applicable.
- `ARTIFACT`: The name of the artifact, if applicable.
- `SHA`: Commit sha to fetch, if applicable.
- `BRANCH`: Branch to fetch, if applicable.
The following configurations are supported, in descending order of precedence:
- `URL`: Bare URL download, useful for custom artifacts
* If this is set, `GIT_URL` or `REPO` should be set to allow the dependency viewer to link to the project's Git repository.
* If this is NOT set, `REPO` must be defined.
- `REPO + TAG + ARTIFACT`: GitHub release artifact
* The final download URL will be `https://github.com/${REPO}/releases/download/${TAG}/${ARTIFACT}`
* Useful for prebuilt libraries and prefetched archives
- `REPO + TAG`: GitHub tag archive
* The final download URL will be `https://github.com/${REPO}/archive/refs/tags/${TAG}.tar.gz`
* Useful for pinning to a specific tag, better for build identification
- `REPO + SHA`: GitHub commit archive
* The final download URL will be `https://github.com/${REPO}/archive/${SHA}.zip`
* Useful for pinning to a specific commit
- `REPO + BRANCH`: GitHub branch archive
* The final download URL will be `https://github.com/${REPO}/archive/refs/heads/${BRANCH}.zip`
* Generally not recommended unless the branch is frozen
- `REPO`: GitHub master archive
* The final download URL will be `https://github.com/${REPO}/archive/refs/heads/master.zip`
* Generally not recommended unless the project is dead
**Hashing**
Hashing is used for verifying downloads. It's highly recommended to use these.
- `HASH_ALGO` (default `SHA512`): Hash algorithm to use
Hashing strategies, descending order of precedence:
- `HASH`: Bare hash verification, useful for static downloads e.g. commit archives
- `HASH_SUFFIX`: Download the hash as `${DOWNLOAD_URL}.${HASH_SUFFIX}`
* The downloaded hash *must* match the hash algorithm and contain nothing but the hash; no filenames or extra content.
- `HASH_URL`: Download the hash from a separate URL
**Additional Options**
- `KEY`: Custom cache key to use (stored as `.cache/cpm/${packagename_lower}/${key}`)
* Default is based on, in descending order of precedence:
- First 4 characters of the sha
- `GIT_VERSION`, or `VERSION` if not specified
- Tag
- Otherwise, CPM defaults will be used. This is not recommended as it doesn't produce reproducible caches
- `DOWNLOAD_ONLY`: Whether or not to configure the downloaded package via CMake
* Useful to turn `OFF` if the project doesn't use CMake
- `SOURCE_SUBDIR`: Subdirectory of the project containing a CMakeLists.txt file
- `FIND_PACKAGE_ARGUMENTS`: Arguments to pass to the `find_package` call
- `BUNDLED_PACKAGE`: Set to `ON` to force the usage of a bundled package
- `OPTIONS`: Options to pass to the configuration of the package
- `PATCHES`: Patches to apply to the package, stored in `.patch/${packagename_lower}/0001-patch-name.patch` and so on
- Other arguments can be passed to CPM as well
**Extra Variables**
For each added package, users may additionally force usage of the system/bundled package.
- `${package}_FORCE_SYSTEM`: Require the package to be installed on the system
- `${package}_FORCE_BUNDLED`: Force the package to be fetched and use the bundled version
**Bundled/System Switching**
Descending order of precedence:
- If `${package}_FORCE_SYSTEM` is true, requires the package to be on the system
- If `${package}_FORCE_BUNDLED` is true, forcefully uses the bundled package
- If `CPMUTIL_FORCE_SYSTEM` is true, requires the package to be on the system
- If `CPMUTIL_FORCE_BUNDLED` is true, forcefully uses the bundled package
- If the `BUNDLED_PACKAGE` argument is true, forcefully uses the bundled package
- Otherwise, CPM will search for the package first, and if not found, will use the bundled package
**Identification**
All dependencies must be identifiable in some way for usage in the dependency viewer. Lists are provided in descending order of precedence.
URLs:
- `GIT_URL`
- `REPO` as a GitHub repository
- `URL`
Versions (bundled):
- `SHA`
- `GIT_VERSION`
- `VERSION`
- `TAG`
- "unknown"
If the package is a system package, AddPackage will attempt to determine the package version and append ` (system)` to the identifier. Otherwise, it will be marked as `unknown (system)`
### AddCIPackage
Adds a package that follows crueter's CI repository spec.
- `VERSION` (required): The version to get (the tag will be `v${VERSION}`)
- `NAME` (required): Name used within the artifacts
- `REPO` (required): CI repository, e.g. `crueter-ci/OpenSSL`
- `PACKAGE` (required): `find_package` package name
- `EXTENSION`: Artifact extension (default `tar.zst`)
- `MIN_VERSION`: Minimum version for `find_package`. Only used if platform does not support this package as a bundled artifact
- `DISABLED_PLATFORMS`: List of platforms that lack artifacts for this package. One of:
* `windows-amd64`
* `windows-arm64`
* `android`
* `solaris`
* `freebsd`
* `linux`
* `linux-aarch64`
- `CMAKE_FILENAME`: Custom CMake filename, relative to the package root (default `${PACKAGE_ROOT}/${NAME}.cmake`)
### AddJsonPackage
This is the recommended method of usage for CPMUtil. In each directory that utilizes `CPMUtil`, there must be a `cpmfile.json` that defines dependencies in a similar manner to the individual calls.
The cpmfile is an object of objects, with each sub-object being named according to the package's identifier, e.g. `openssl`, which can then be fetched with `AddJsonPackage(<identifier>)`. Options are designed to map closely to the argument names, and are always strings unless otherwise specified.
- `package` -> `NAME` (`PACKAGE` for CI), defaults to the object key
- `repo` -> `REPO`
- `version` -> `VERSION`
- `ci` (bool)
If `ci` is `false`:
- `hash` -> `HASH`
- `sha` -> `SHA`
- `tag` -> `TAG`
- `artifact` -> `ARTIFACT`
- `git_version` -> `GIT_VERSION`
- `source_subdir` -> `SOURCE_SUBDIR`
- `bundled` -> `BUNDLED_PACKAGE`
- `find_args` -> `FIND_PACKAGE_ARGUMENTS`
- `patches` -> `PATCHES` (array)
- `options` -> `OPTIONS` (array)
Other arguments aren't currently supported. If you wish to add them, see the `AddJsonPackage` function in `CMakeModules/CPMUtil.cmake`.
If `ci` is `true`:
- `name` -> `NAME`, defaults to the object key
- `extension` -> `EXTENSION`, defaults to `tar.zst`
- `min_version` -> `MIN_VERSION`
- `cmake_filename` -> `CMAKE_FILENAME`
- `extension` -> `EXTENSION`
### Examples
In order: OpenSSL CI, Boost (tag + artifact), discord-rpc (sha + options + patches), Opus (options + find_args)
```json
{
"openssl": {
"ci": true,
"package": "OpenSSL",
"name": "openssl",
"repo": "crueter-ci/OpenSSL",
"version": "3.5.2",
"min_version": "1.1.1"
},
"boost": {
"package": "Boost",
"repo": "boostorg/boost",
"tag": "boost-1.88.0",
"artifact": "boost-1.88.0-cmake.7z",
"hash": "e5b049e5b61964480ca816395f63f95621e66cb9bcf616a8b10e441e0e69f129e22443acb11e77bc1e8170f8e4171b9b7719891efc43699782bfcd4b3a365f01",
"git_version": "1.88.0",
"version": "1.57"
},
"opus": {
"package": "Opus",
"repo": "xiph/opus",
"sha": "5ded705cf4",
"hash": "0dc89e58ddda1f3bc6a7037963994770c5806c10e66f5cc55c59286fc76d0544fe4eca7626772b888fd719f434bc8a92f792bdb350c807968b2ac14cfc04b203",
"version": "1.3",
"find_args": "MODULE",
"options": [
"OPUS_BUILD_TESTING OFF",
"OPUS_BUILD_PROGRAMS OFF",
"OPUS_INSTALL_PKG_CONFIG_MODULE OFF",
"OPUS_INSTALL_CMAKE_CONFIG_MODULE OFF"
]
},
"discord-rpc": {
"repo": "discord/discord-rpc",
"sha": "963aa9f3e5",
"hash": "386e1344e9a666d730f2d335ee3aef1fd05b1039febefd51aa751b705009cc764411397f3ca08dffd46205c72f75b235c870c737b2091a4ed0c3b061f5919bde",
"options": [
"BUILD_EXAMPLES OFF"
],
"patches": [
"0001-cmake-version.patch",
"0002-no-clang-format.patch",
"0003-fix-cpp17.patch"
]
},
}
```
### Inclusion
To include CPMUtil:
```cmake
set(CPMUTIL_JSON_FILE ${CMAKE_CURRENT_SOURCE_DIR}/cpmfile.json)
include(CPMUtil)
```
You may omit the first line if you are not utilizing cpmfile.
## Prefetching
- To prefetch a CPM dependency (requires cpmfile):
* `tools/cpm-fetch.sh <packages>`
- To prefetch all CPM dependencies:
* `tools/cpm-fetch-all.sh`
Currently, `cpm-fetch.sh` defines the following directories for cpmfiles:
`externals src/yuzu/externals externals/ffmpeg src/dynarmic/externals externals/nx_tzdb`
Whenever you add a new cpmfile, update the script accordingly

2
docs/build/Linux.md vendored
View file

@ -37,7 +37,7 @@ Dependencies are listed here as commands that can be copied/pasted. Of course, t
- GCC 11 or later is required. - GCC 11 or later is required.
- Ubuntu / Linux Mint / Debian: - Ubuntu / Linux Mint / Debian:
- `sudo apt-get install autoconf cmake g++ gcc git glslang-tools libasound2 libboost-context-dev libglu1-mesa-dev libhidapi-dev libpulse-dev libtool libudev-dev libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-render-util0 libxcb-xinerama0 libxcb-xkb1 libxext-dev libxkbcommon-x11-0 mesa-common-dev nasm ninja-build qt6-base-private-dev libmbedtls-dev catch2 libfmt-dev liblz4-dev nlohmann-json3-dev libzstd-dev libssl-dev libavfilter-dev libavcodec-dev libswscale-dev pkg-config zlib1g-dev` - `sudo apt-get install autoconf cmake g++ gcc git glslang-tools libasound2 libboost-context-dev libglu1-mesa-dev libhidapi-dev libpulse-dev libtool libudev-dev libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-render-util0 libxcb-xinerama0 libxcb-xkb1 libxext-dev libxkbcommon-x11-0 mesa-common-dev nasm ninja-build qt6-base-private-dev libmbedtls-dev catch2 libfmt-dev liblz4-dev nlohmann-json3-dev libzstd-dev libssl-dev libavfilter-dev libavcodec-dev libswscale-dev pkg-config zlib1g-dev libva-dev libvdpau-dev`
- Ubuntu 22.04, Linux Mint 20, or Debian 12 or later is required. - Ubuntu 22.04, Linux Mint 20, or Debian 12 or later is required.
- Users need to manually specify building with QT Web Engine enabled. This is done using the parameter `-DYUZU_USE_QT_WEB_ENGINE=ON` when running CMake. - Users need to manually specify building with QT Web Engine enabled. This is done using the parameter `-DYUZU_USE_QT_WEB_ENGINE=ON` when running CMake.
- Users need to manually disable building SDL2 from externals if they intend to use the version provided by their system by adding the parameters `-DYUZU_USE_EXTERNAL_SDL2=OFF` - Users need to manually disable building SDL2 from externals if they intend to use the version provided by their system by adding the parameters `-DYUZU_USE_EXTERNAL_SDL2=OFF`

View file

@ -7,7 +7,8 @@
# TODO(crueter): A lot of this should be moved to the root. # TODO(crueter): A lot of this should be moved to the root.
# otherwise we have to do weird shenanigans with library linking and stuff # otherwise we have to do weird shenanigans with library linking and stuff
# cpm # Explicitly include CPMUtil here since we have a separate cpmfile for externals
set(CPMUTIL_JSON_FILE ${CMAKE_CURRENT_SOURCE_DIR}/cpmfile.json)
include(CPMUtil) include(CPMUtil)
# Explicitly declare this option here to propagate to the oaknut CPM call # Explicitly declare this option here to propagate to the oaknut CPM call
@ -33,32 +34,15 @@ endif()
# Xbyak (also used by Dynarmic, so needs to be added first) # Xbyak (also used by Dynarmic, so needs to be added first)
if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64) if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64)
if (PLATFORM_SUN) if (PLATFORM_SUN)
# Fix regset.h collisions AddJsonPackage(xbyak_sun)
set(XBYAK_HASH 51f507b0b3)
set(XBYAK_SHA512SUM 4a29a3c2f97f7d5adf667a21a008be03c951fb6696b0d7ba27e7e4afa037bc76eb5e059bb84860e01baf741d4d3ac851b840cd54c99d038812fbe0f1fa6d38a4)
else() else()
set(XBYAK_HASH 4e44f4614d) AddJsonPackage(xbyak)
set(XBYAK_SHA512SUM 5824e92159e07fa36a774aedd3b3ef3541d0241371d522cffa4ab3e1f215fa5097b1b77865b47b2481376c704fa079875557ea463ca63d0a7fd6a8a20a589e70)
endif() endif()
AddPackage(
NAME xbyak
REPO "Lizzie841/xbyak"
SHA ${XBYAK_HASH}
HASH ${XBYAK_SHA512SUM}
BUNDLED_PACKAGE ON
)
endif() endif()
# Oaknut (also used by Dynarmic, so needs to be added first) # Oaknut (also used by Dynarmic, so needs to be added first)
if (ARCHITECTURE_arm64 OR DYNARMIC_TESTS) if (ARCHITECTURE_arm64 OR DYNARMIC_TESTS)
AddPackage( AddJsonPackage(oaknut)
NAME oaknut
VERSION 2.0.1
REPO "merryhime/oaknut"
SHA 94c726ce03
HASH d8d082242fa1881abce3c82f8dafa002c4e561e66a69e7fc038af67faa5eff2630f082d3d19579c88c4c9f9488e54552accc8cb90e7ce743efe043b6230c08ac
)
endif() endif()
# getopt # getopt
@ -70,14 +54,7 @@ endif()
add_subdirectory(glad) add_subdirectory(glad)
# mbedtls # mbedtls
AddPackage( AddJsonPackage(mbedtls)
NAME mbedtls
REPO "Mbed-TLS/mbedtls"
SHA "8c88150ca1"
HASH 769ad1e94c570671071e1f2a5c0f1027e0bf6bcdd1a80ea8ac970f2c86bc45ce4e31aa88d6d8110fc1bed1de81c48bc624df1b38a26f8b340a44e109d784a966
PATCHES
${CMAKE_SOURCE_DIR}/.patch/mbedtls/0001-cmake-version.patch
)
if (mbedtls_ADDED) if (mbedtls_ADDED)
target_include_directories(mbedtls PUBLIC ${mbedtls_SOURCE_DIR}/include) target_include_directories(mbedtls PUBLIC ${mbedtls_SOURCE_DIR}/include)
@ -97,23 +74,11 @@ endif()
# Sirit # Sirit
# TODO(crueter): spirv-tools doesn't work w/ system # TODO(crueter): spirv-tools doesn't work w/ system
set(SPIRV_WERROR OFF) set(SPIRV_WERROR OFF)
AddPackage( AddJsonPackage(spirv-headers)
NAME SPIRV-Headers
REPO "KhronosGroup/SPIRV-Headers"
SHA 4e209d3d7e
HASH f48bbe18341ed55ea0fe280dbbbc0a44bf222278de6e716e143ca1e95ca320b06d4d23d6583fbf8d03e1428f3dac8fa00e5b82ddcd6b425e6236d85af09550a4
)
AddPackage( AddJsonPackage(sirit)
NAME sirit
REPO "eden-emulator/sirit"
SHA db1f1e8ab5
HASH 73eb3a042848c63a10656545797e85f40d142009dfb7827384548a385e1e28e1ac72f42b25924ce530d58275f8638554281e884d72f9c7aaf4ed08690a414b05
OPTIONS
"SIRIT_USE_SYSTEM_SPIRV_HEADERS ON"
)
if(MSVC AND USE_CCACHE AND TARGET sirit) if(MSVC AND USE_CCACHE AND sirit_ADDED)
get_target_property(_opts sirit COMPILE_OPTIONS) get_target_property(_opts sirit COMPILE_OPTIONS)
list(FILTER _opts EXCLUDE REGEX "/Zi") list(FILTER _opts EXCLUDE REGEX "/Zi")
list(APPEND _opts "/Z7") list(APPEND _opts "/Z7")
@ -122,45 +87,16 @@ endif()
# httplib # httplib
if (ENABLE_WEB_SERVICE OR ENABLE_QT_UPDATE_CHECKER) if (ENABLE_WEB_SERVICE OR ENABLE_QT_UPDATE_CHECKER)
AddPackage( AddJsonPackage(httplib)
NAME httplib
REPO "yhirose/cpp-httplib"
SHA a609330e4c
HASH dd3fd0572f8367d8549e1319fd98368b3e75801a293b0c3ac9b4adb806473a4506a484b3d389dc5bee5acc460cb90af7a20e5df705a1696b56496b30b9ce7ed2
OPTIONS
"HTTPLIB_REQUIRE_OPENSSL ${ENABLE_OPENSSL}"
)
endif() endif()
# cpp-jwt # cpp-jwt
if (ENABLE_WEB_SERVICE) if (ENABLE_WEB_SERVICE)
AddPackage( AddJsonPackage(cpp-jwt)
NAME cpp-jwt
VERSION 1.4
REPO "arun11299/cpp-jwt"
SHA a54fa08a3b
HASH a90f7e594ada0c7e49d5ff9211c71097534e7742a8e44bf0851b0362642a7271d53f5d83d04eeaae2bad17ef3f35e09e6818434d8eaefa038f3d1f7359d0969a
FIND_PACKAGE_ARGUMENTS "CONFIG"
OPTIONS
"CPP_JWT_BUILD_EXAMPLES OFF"
"CPP_JWT_BUILD_TESTS OFF"
"CPP_JWT_USE_VENDORED_NLOHMANN_JSON OFF"
PATCHES
${CMAKE_SOURCE_DIR}/.patch/cpp-jwt/0001-no-install.patch
${CMAKE_SOURCE_DIR}/.patch/cpp-jwt/0002-missing-decl.patch
)
endif() endif()
# unordered_dense # unordered_dense
AddPackage( AddJsonPackage(unordered-dense)
NAME unordered_dense
REPO "Lizzie841/unordered_dense"
SHA e59d30b7b1
HASH 71eff7bd9ba4b9226967bacd56a8ff000946f8813167cb5664bb01e96fb79e4e220684d824fe9c59c4d1cc98c606f13aff05b7940a1ed8ab3c95d6974ee34fa0
FIND_PACKAGE_ARGUMENTS "CONFIG"
OPTIONS
"UNORDERED_DENSE_INSTALL OFF"
)
# FFMpeg # FFMpeg
if (YUZU_USE_BUNDLED_FFMPEG) if (YUZU_USE_BUNDLED_FFMPEG)
@ -175,47 +111,37 @@ endif()
# TODO(crueter): Vk1.4 impl # TODO(crueter): Vk1.4 impl
AddPackage( AddJsonPackage(
NAME VulkanHeaders NAME vulkan-headers
VERSION 1.3.274
REPO "KhronosGroup/Vulkan-Headers"
SHA 89268a6d17
HASH 3ab349f74298ba72cafb8561015690c0674d428a09fb91ccd3cd3daca83650d190d46d33fd97b0a8fd4223fe6df2bcabae89136fbbf7c0bfeb8776f9448304c8
BUNDLED_PACKAGE ${YUZU_USE_EXTERNAL_VULKAN_HEADERS} BUNDLED_PACKAGE ${YUZU_USE_EXTERNAL_VULKAN_HEADERS}
) )
# Vulkan-Utility-Libraries # Vulkan-Utility-Libraries
AddPackage( AddJsonPackage(
NAME VulkanUtilityLibraries NAME vulkan-utility-libraries
REPO "KhronosGroup/Vulkan-Utility-Libraries"
SHA df2e358152
HASH 3e468c3d9ff93f6d418d71e5527abe0a12c8c7ab5b0b52278bbbee4d02bb87e99073906729b727e0147242b7e3fd5dedf68b803f1878cb4c0e4f730bc2238d79
BUNDLED_PACKAGE ${YUZU_USE_EXTERNAL_VULKAN_UTILITY_LIBRARIES} BUNDLED_PACKAGE ${YUZU_USE_EXTERNAL_VULKAN_UTILITY_LIBRARIES}
) )
# SPIRV-Tools # SPIRV Tools
if (YUZU_USE_EXTERNAL_VULKAN_SPIRV_TOOLS) AddJsonPackage(
AddPackage( NAME spirv-tools
NAME SPIRV-Tools BUNDLED_PACKAGE ${YUZU_USE_EXTERNAL_VULKAN_SPIRV_TOOLS}
REPO "KhronosGroup/SPIRV-Tools" )
SHA 40eb301f32
HASH 58d0fb1047d69373cf24c73e6f78c73a72a6cca3b4df1d9f083b9dcc0962745ef154abf3dbe9b3623b835be20c6ec769431cf11733349f45e7568b3525f707aa if (SPIRV-Tools_ADDED)
OPTIONS add_library(SPIRV-Tools::SPIRV-Tools ALIAS SPIRV-Tools-static)
"SPIRV_SKIP_EXECUTABLES ON" target_link_libraries(SPIRV-Tools-static PRIVATE SPIRV-Tools-opt SPIRV-Tools-link)
)
endif() endif()
# TZDB (Time Zone Database) # TZDB (Time Zone Database)
add_subdirectory(nx_tzdb) add_subdirectory(nx_tzdb)
# VMA # VMA
AddPackage( AddJsonPackage(vulkan-memory-allocator)
NAME VulkanMemoryAllocator
REPO "GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator" if (VulkanMemoryAllocator_ADDED AND MSVC)
SHA 1076b348ab target_compile_options(VulkanMemoryAllocator INTERFACE /wd4189)
HASH a46b44e4286d08cffda058e856c47f44c7fed3da55fe9555976eb3907fdcc20ead0b1860b0c38319cda01dbf9b1aa5d4b4038c7f1f8fbd97283d837fa9af9772 endif()
FIND_PACKAGE_ARGUMENTS "CONFIG"
)
if (NOT TARGET LLVM::Demangle) if (NOT TARGET LLVM::Demangle)
add_library(demangle demangle/ItaniumDemangle.cpp) add_library(demangle demangle/ItaniumDemangle.cpp)
@ -245,17 +171,8 @@ if (NOT TARGET RenderDoc::API)
add_library(RenderDoc::API ALIAS renderdoc) add_library(RenderDoc::API ALIAS renderdoc)
endif() endif()
if (ANDROID) if (ANDROID AND ARCHITECTURE_arm64)
if (ARCHITECTURE_arm64) AddJsonPackage(libadrenotools)
AddPackage(
NAME libadrenotools
REPO "bylaws/libadrenotools"
SHA 8fae8ce254
HASH c74fa855f0edebbf25c9bce40b00966daa2447bfc5e15f0cf1a95f86cbf70fc6b02590707edbde16328a0a2a4fb9a1fc419d2dfc22a4a4150971be91892d4edb
PATCHES
${CMAKE_SOURCE_DIR}/.patch/libadrenotools/0001-linkerns-cpm.patch
)
endif()
endif() endif()
if (UNIX AND NOT APPLE AND NOT TARGET gamemode::headers) if (UNIX AND NOT APPLE AND NOT TARGET gamemode::headers)
@ -278,6 +195,7 @@ if (YUZU_CRASH_DUMPS AND NOT TARGET libbreakpad_client)
_CRT_NONSTDC_NO_DEPRECATE _CRT_NONSTDC_NO_DEPRECATE
) )
# TODO
AddPackage( AddPackage(
NAME breakpad NAME breakpad
URL "google/breakpad" URL "google/breakpad"
@ -378,13 +296,7 @@ endif()
# oboe # oboe
if (ANDROID) if (ANDROID)
AddPackage( AddJsonPackage(oboe)
NAME oboe
REPO "google/oboe"
SHA 2bc873e53c
HASH 02329058a7f9cf7d5039afaae5ab170d9f42f60f4c01e21eaf4f46073886922b057a9ae30eeac040b3ac182f51b9c1bfe9fe1050a2c9f6ce567a1a9a0ec2c768
BUNDLED_PACKAGE ON
)
add_library(oboe::oboe ALIAS oboe) add_library(oboe::oboe ALIAS oboe)
endif() endif()

122
externals/cpmfile.json vendored Normal file
View file

@ -0,0 +1,122 @@
{
"mbedtls": {
"repo": "Mbed-TLS/mbedtls",
"sha": "8c88150ca1",
"hash": "769ad1e94c570671071e1f2a5c0f1027e0bf6bcdd1a80ea8ac970f2c86bc45ce4e31aa88d6d8110fc1bed1de81c48bc624df1b38a26f8b340a44e109d784a966",
"patches": [
"0001-cmake-version.patch"
]
},
"spirv-headers": {
"package": "SPIRV-Headers",
"repo": "KhronosGroup/SPIRV-Headers",
"sha": "4e209d3d7e",
"hash": "f48bbe18341ed55ea0fe280dbbbc0a44bf222278de6e716e143ca1e95ca320b06d4d23d6583fbf8d03e1428f3dac8fa00e5b82ddcd6b425e6236d85af09550a4"
},
"sirit": {
"repo": "eden-emulator/sirit",
"sha": "db1f1e8ab5",
"hash": "73eb3a042848c63a10656545797e85f40d142009dfb7827384548a385e1e28e1ac72f42b25924ce530d58275f8638554281e884d72f9c7aaf4ed08690a414b05",
"options": [
"SIRIT_USE_SYSTEM_SPIRV_HEADERS ON"
]
},
"httplib": {
"repo": "yhirose/cpp-httplib",
"sha": "a609330e4c",
"hash": "dd3fd0572f8367d8549e1319fd98368b3e75801a293b0c3ac9b4adb806473a4506a484b3d389dc5bee5acc460cb90af7a20e5df705a1696b56496b30b9ce7ed2"
},
"cpp-jwt": {
"version": "1.4",
"repo": "arun11299/cpp-jwt",
"sha": "a54fa08a3b",
"hash": "a90f7e594ada0c7e49d5ff9211c71097534e7742a8e44bf0851b0362642a7271d53f5d83d04eeaae2bad17ef3f35e09e6818434d8eaefa038f3d1f7359d0969a",
"find_args": "CONFIG",
"options": [
"CPP_JWT_BUILD_EXAMPLES OFF",
"CPP_JWT_BUILD_TESTS OFF",
"CPP_JWT_USE_VENDORED_NLOHMANN_JSON OFF"
],
"patches": [
"0001-no-install.patch",
"0002-missing-decl.patch"
]
},
"vulkan-headers": {
"package": "VulkanHeaders",
"version": "1.3.274",
"repo": "KhronosGroup/Vulkan-Headers",
"sha": "89268a6d17",
"hash": "3ab349f74298ba72cafb8561015690c0674d428a09fb91ccd3cd3daca83650d190d46d33fd97b0a8fd4223fe6df2bcabae89136fbbf7c0bfeb8776f9448304c8"
},
"vulkan-utility-libraries": {
"package": "VulkanUtilityLibraries",
"repo": "KhronosGroup/Vulkan-Utility-Libraries",
"sha": "df2e358152",
"hash": "3e468c3d9ff93f6d418d71e5527abe0a12c8c7ab5b0b52278bbbee4d02bb87e99073906729b727e0147242b7e3fd5dedf68b803f1878cb4c0e4f730bc2238d79"
},
"vulkan-memory-allocator": {
"package": "VulkanMemoryAllocator",
"repo": "GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator",
"sha": "1076b348ab",
"hash": "a46b44e4286d08cffda058e856c47f44c7fed3da55fe9555976eb3907fdcc20ead0b1860b0c38319cda01dbf9b1aa5d4b4038c7f1f8fbd97283d837fa9af9772",
"find_args": "CONFIG"
},
"spirv-tools": {
"package": "SPIRV-Tools",
"repo": "KhronosGroup/SPIRV-Tools",
"sha": "40eb301f32",
"hash": "58d0fb1047d69373cf24c73e6f78c73a72a6cca3b4df1d9f083b9dcc0962745ef154abf3dbe9b3623b835be20c6ec769431cf11733349f45e7568b3525f707aa",
"find_args": "MODULE",
"options": [
"SPIRV_SKIP_EXECUTABLES ON"
]
},
"xbyak_sun": {
"package": "xbyak",
"repo": "herumi/xbyak",
"sha": "9bb219333a",
"hash": "303165d45c8c19387ec49d9fda7d7a4e0d86d4c0153898c23f25ce2d58ece567f44c0bbbfe348239b933edb6e1a1e34f4bc1c0ab3a285bee5da0e548879387b0",
"bundled": true
},
"xbyak": {
"package": "xbyak",
"repo": "herumi/xbyak",
"sha": "4e44f4614d",
"hash": "5824e92159e07fa36a774aedd3b3ef3541d0241371d522cffa4ab3e1f215fa5097b1b77865b47b2481376c704fa079875557ea463ca63d0a7fd6a8a20a589e70",
"bundled": true
},
"oaknut": {
"version": "2.0.1",
"repo": "merryhime/oaknut",
"sha": "94c726ce03",
"hash": "d8d082242fa1881abce3c82f8dafa002c4e561e66a69e7fc038af67faa5eff2630f082d3d19579c88c4c9f9488e54552accc8cb90e7ce743efe043b6230c08ac"
},
"libadrenotools": {
"repo": "bylaws/libadrenotools",
"sha": "8fae8ce254",
"hash": "c74fa855f0edebbf25c9bce40b00966daa2447bfc5e15f0cf1a95f86cbf70fc6b02590707edbde16328a0a2a4fb9a1fc419d2dfc22a4a4150971be91892d4edb",
"patches": [
"0001-linkerns-cpm.patch"
]
},
"oboe": {
"repo": "google/oboe",
"sha": "2bc873e53c",
"hash": "02329058a7f9cf7d5039afaae5ab170d9f42f60f4c01e21eaf4f46073886922b057a9ae30eeac040b3ac182f51b9c1bfe9fe1050a2c9f6ce567a1a9a0ec2c768",
"bundled": true
},
"unordered-dense": {
"package": "unordered_dense",
"repo": "martinus/unordered_dense",
"sha": "73f3cbb237",
"hash": "c08c03063938339d61392b687562909c1a92615b6ef39ec8df19ea472aa6b6478e70d7d5e33d4a27b5d23f7806daf57fe1bacb8124c8a945c918c7663a9e8532",
"find_args": "CONFIG",
"options": [
"UNORDERED_DENSE_INSTALL OFF"
],
"patches": [
"0001-cmake.patch"
]
}
}

View file

@ -1,6 +1,10 @@
# SPDX-FileCopyrightText: 2021 yuzu Emulator Project # SPDX-FileCopyrightText: 2021 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later # SPDX-License-Identifier: GPL-2.0-or-later
# Explicitly include CPMUtil here since we have a separate cpmfile for ffmpeg
set(CPMUTIL_JSON_FILE ${CMAKE_CURRENT_SOURCE_DIR}/cpmfile.json)
include(CPMUtil)
if (NOT WIN32 AND NOT ANDROID) if (NOT WIN32 AND NOT ANDROID)
# Build FFmpeg from externals # Build FFmpeg from externals
message(STATUS "Using FFmpeg from externals") message(STATUS "Using FFmpeg from externals")
@ -19,13 +23,7 @@ if (NOT WIN32 AND NOT ANDROID)
message(FATAL_ERROR "Required program `autoconf` not found.") message(FATAL_ERROR "Required program `autoconf` not found.")
endif() endif()
AddPackage( AddJsonPackage(ffmpeg)
NAME ffmpeg
REPO "FFmpeg/FFmpeg"
SHA c2184b65d2
HASH 2a89d664119debbb3c006ab1c48d5d7f26e889f4a65ad2e25c8b0503308295123d5a9c5c78bf683aef5ff09acef8c3fc2837f22d3e8c611528b933bf03bcdd97
SYSTEM_PACKAGE OFF
)
set(FFmpeg_PREFIX ${ffmpeg_SOURCE_DIR}) set(FFmpeg_PREFIX ${ffmpeg_SOURCE_DIR})
set(FFmpeg_BUILD_DIR ${ffmpeg_BINARY_DIR}) set(FFmpeg_BUILD_DIR ${ffmpeg_BINARY_DIR})
@ -65,32 +63,46 @@ if (NOT WIN32 AND NOT ANDROID)
set(FFmpeg_HWACCEL_INCLUDE_DIRS) set(FFmpeg_HWACCEL_INCLUDE_DIRS)
set(FFmpeg_HWACCEL_LDFLAGS) set(FFmpeg_HWACCEL_LDFLAGS)
if(LIBVA_FOUND) if (NOT APPLE)
# In Solaris needs explicit linking for ffmpeg which links to /lib/amd64/libX11.so
if(PLATFORM_SUN)
list(APPEND FFmpeg_HWACCEL_LIBRARIES
X11
"/usr/lib/xorg/amd64/libdrm.so")
else()
pkg_check_modules(LIBDRM libdrm REQUIRED) pkg_check_modules(LIBDRM libdrm REQUIRED)
list(APPEND FFmpeg_HWACCEL_LIBRARIES
${LIBDRM_LIBRARIES})
list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS
${LIBDRM_INCLUDE_DIRS})
endif()
list(APPEND FFmpeg_HWACCEL_FLAGS
--enable-libdrm)
endif()
if(LIBVA_FOUND)
find_package(X11 REQUIRED) find_package(X11 REQUIRED)
pkg_check_modules(LIBVA-DRM libva-drm REQUIRED) pkg_check_modules(LIBVA-DRM libva-drm REQUIRED)
pkg_check_modules(LIBVA-X11 libva-x11 REQUIRED) pkg_check_modules(LIBVA-X11 libva-x11 REQUIRED)
list(APPEND FFmpeg_HWACCEL_LIBRARIES list(APPEND FFmpeg_HWACCEL_LIBRARIES
${LIBDRM_LIBRARIES}
${X11_LIBRARIES} ${X11_LIBRARIES}
${LIBVA-DRM_LIBRARIES} ${LIBVA-DRM_LIBRARIES}
${LIBVA-X11_LIBRARIES} ${LIBVA-X11_LIBRARIES}
${LIBVA_LIBRARIES}) ${LIBVA_LIBRARIES})
set(FFmpeg_HWACCEL_FLAGS list(APPEND FFmpeg_HWACCEL_FLAGS
--enable-hwaccel=h264_vaapi --enable-hwaccel=h264_vaapi
--enable-hwaccel=vp8_vaapi --enable-hwaccel=vp8_vaapi
--enable-hwaccel=vp9_vaapi --enable-hwaccel=vp9_vaapi)
--enable-libdrm)
list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS
${LIBDRM_INCLUDE_DIRS}
${X11_INCLUDE_DIRS} ${X11_INCLUDE_DIRS}
${LIBVA-DRM_INCLUDE_DIRS} ${LIBVA-DRM_INCLUDE_DIRS}
${LIBVA-X11_INCLUDE_DIRS} ${LIBVA-X11_INCLUDE_DIRS}
${LIBVA_INCLUDE_DIRS} ${LIBVA_INCLUDE_DIRS}
) )
message(STATUS "VA-API found") message(STATUS "ffmpeg: va-api libraries version ${LIBVA_VERSION} found")
else() else()
set(FFmpeg_HWACCEL_FLAGS --disable-vaapi) list(APPEND FFmpeg_HWACCEL_FLAGS --disable-vaapi)
message(WARNING "ffmpeg: libva-dev not found, disabling Video Aceleraion API (VA-API)...")
endif() endif()
if (FFNVCODEC_FOUND) if (FFNVCODEC_FOUND)
@ -105,7 +117,7 @@ if (NOT WIN32 AND NOT ANDROID)
list(APPEND FFmpeg_HWACCEL_LIBRARIES ${FFNVCODEC_LIBRARIES}) list(APPEND FFmpeg_HWACCEL_LIBRARIES ${FFNVCODEC_LIBRARIES})
list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS ${FFNVCODEC_INCLUDE_DIRS}) list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS ${FFNVCODEC_INCLUDE_DIRS})
list(APPEND FFmpeg_HWACCEL_LDFLAGS ${FFNVCODEC_LDFLAGS}) list(APPEND FFmpeg_HWACCEL_LDFLAGS ${FFNVCODEC_LDFLAGS})
message(STATUS "ffnvcodec libraries version ${FFNVCODEC_VERSION} found") message(STATUS "ffmpeg: ffnvcodec libraries version ${FFNVCODEC_VERSION} found")
# ffnvenc could load CUDA libraries at the runtime using dlopen/dlsym or LoadLibrary/GetProcAddress # ffnvenc could load CUDA libraries at the runtime using dlopen/dlsym or LoadLibrary/GetProcAddress
# here we handle the hard-linking senario where CUDA is linked during compilation # here we handle the hard-linking senario where CUDA is linked during compilation
if (CUDA_FOUND) if (CUDA_FOUND)
@ -114,7 +126,7 @@ if (NOT WIN32 AND NOT ANDROID)
list(APPEND FFmpeg_HWACCEL_LIBRARIES ${CUDA_LIBRARIES}) list(APPEND FFmpeg_HWACCEL_LIBRARIES ${CUDA_LIBRARIES})
list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS ${CUDA_INCLUDE_DIRS}) list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS ${CUDA_INCLUDE_DIRS})
list(APPEND FFmpeg_HWACCEL_LDFLAGS ${CUDA_LDFLAGS}) list(APPEND FFmpeg_HWACCEL_LDFLAGS ${CUDA_LDFLAGS})
message(STATUS "CUDA libraries found, hard-linking will be performed") message(STATUS "ffmpeg: CUDA libraries found, hard-linking will be performed")
endif(CUDA_FOUND) endif(CUDA_FOUND)
endif() endif()
@ -127,9 +139,10 @@ if (NOT WIN32 AND NOT ANDROID)
list(APPEND FFmpeg_HWACCEL_LIBRARIES ${VDPAU_LIBRARIES}) list(APPEND FFmpeg_HWACCEL_LIBRARIES ${VDPAU_LIBRARIES})
list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS ${VDPAU_INCLUDE_DIRS}) list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS ${VDPAU_INCLUDE_DIRS})
list(APPEND FFmpeg_HWACCEL_LDFLAGS ${VDPAU_LDFLAGS}) list(APPEND FFmpeg_HWACCEL_LDFLAGS ${VDPAU_LDFLAGS})
message(STATUS "vdpau libraries version ${VDPAU_VERSION} found") message(STATUS "ffmpeg: vdpau libraries version ${VDPAU_VERSION} found")
else() else()
list(APPEND FFmpeg_HWACCEL_FLAGS --disable-vdpau) list(APPEND FFmpeg_HWACCEL_FLAGS --disable-vdpau)
message(WARNING "ffmpeg: libvdpau-dev not found, disabling Video Decode and Presentation API for Unix (VDPAU)...")
endif() endif()
find_program(BASH_PROGRAM bash REQUIRED) find_program(BASH_PROGRAM bash REQUIRED)

8
externals/ffmpeg/cpmfile.json vendored Normal file
View file

@ -0,0 +1,8 @@
{
"ffmpeg": {
"repo": "FFmpeg/FFmpeg",
"sha": "c2184b65d2",
"hash": "2a89d664119debbb3c006ab1c48d5d7f26e889f4a65ad2e25c8b0503308295123d5a9c5c78bf683aef5ff09acef8c3fc2837f22d3e8c611528b933bf03bcdd97",
"bundled": true
}
}

View file

@ -1,6 +1,13 @@
# SPDX-FileCopyrightText: 2025 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2023 yuzu Emulator Project # SPDX-FileCopyrightText: 2023 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later # SPDX-License-Identifier: GPL-2.0-or-later
# Explicitly include CPMUtil here since we have a separate cpmfile for nx_tzdb
set(CPMUTIL_JSON_FILE ${CMAKE_CURRENT_SOURCE_DIR}/cpmfile.json)
include(CPMUtil)
set(NX_TZDB_INCLUDE_DIR "${CMAKE_CURRENT_BINARY_DIR}/include") set(NX_TZDB_INCLUDE_DIR "${CMAKE_CURRENT_BINARY_DIR}/include")
add_library(nx_tzdb INTERFACE) add_library(nx_tzdb INTERFACE)
@ -11,33 +18,38 @@ find_program(DATE_PROG date)
set(CAN_BUILD_NX_TZDB true) set(CAN_BUILD_NX_TZDB true)
if (NOT GIT) if (NOT (GIT AND GNU_MAKE AND DATE_PROG) OR CMAKE_SYSTEM_NAME STREQUAL "Windows" OR ANDROID)
set(CAN_BUILD_NX_TZDB false)
endif()
if (NOT GNU_MAKE)
set(CAN_BUILD_NX_TZDB false)
endif()
if (NOT DATE_PROG)
set(CAN_BUILD_NX_TZDB false)
endif()
if (CMAKE_SYSTEM_NAME STREQUAL "Windows" OR ANDROID)
# tzdb_to_nx currently requires a posix-compliant host # tzdb_to_nx currently requires a posix-compliant host
# MinGW and Android are handled here due to the executable format being different from the host system # MinGW and Android are handled here due to the executable format being different from the host system
# TODO (lat9nq): cross-compiling support # TODO (lat9nq): cross-compiling support
set(CAN_BUILD_NX_TZDB false) set(CAN_BUILD_NX_TZDB false)
endif() endif()
set(NX_TZDB_VERSION "250725") if (CAN_BUILD_NX_TZDB AND NOT YUZU_DOWNLOAD_TIME_ZONE_DATA)
set(NX_TZDB_ARCHIVE "${CPM_SOURCE_CACHE}/nx_tzdb/${NX_TZDB_VERSION}.zip") message(FATAL_ERROR "Building tzdb is currently unsupported. Check back later.")
add_subdirectory(tzdb_to_nx)
add_dependencies(nx_tzdb x80e)
set(NX_TZDB_ROMFS_DIR "${CPM_SOURCE_CACHE}/nx_tzdb/tz") set(NX_TZDB_BASE_DIR "${NX_TZDB_DIR}")
set(NX_TZDB_TZ_DIR "${NX_TZDB_BASE_DIR}/zoneinfo")
endif()
# TODO(crueter): This is a terrible solution, but MSVC fails to link without it
# Need to investigate further but I still can't reproduce...
if (MSVC)
set(NX_TZDB_VERSION "250725")
set(NX_TZDB_ARCHIVE "${CPM_SOURCE_CACHE}/nx_tzdb/${NX_TZDB_VERSION}.zip")
set(NX_TZDB_BASE_DIR "${CPM_SOURCE_CACHE}/nx_tzdb/tz")
set(NX_TZDB_TZ_DIR "${NX_TZDB_BASE_DIR}/zoneinfo")
if ((NOT CAN_BUILD_NX_TZDB OR YUZU_DOWNLOAD_TIME_ZONE_DATA) AND NOT EXISTS ${NX_TZDB_ROMFS_DIR})
set(NX_TZDB_DOWNLOAD_URL "https://github.com/crueter/tzdb_to_nx/releases/download/${NX_TZDB_VERSION}/${NX_TZDB_VERSION}.zip") set(NX_TZDB_DOWNLOAD_URL "https://github.com/crueter/tzdb_to_nx/releases/download/${NX_TZDB_VERSION}/${NX_TZDB_VERSION}.zip")
message(STATUS "Downloading time zone data from ${NX_TZDB_DOWNLOAD_URL}...") message(STATUS "Downloading time zone data from ${NX_TZDB_DOWNLOAD_URL}...")
file(DOWNLOAD ${NX_TZDB_DOWNLOAD_URL} ${NX_TZDB_ARCHIVE} file(DOWNLOAD ${NX_TZDB_DOWNLOAD_URL} ${NX_TZDB_ARCHIVE}
STATUS NX_TZDB_DOWNLOAD_STATUS) STATUS NX_TZDB_DOWNLOAD_STATUS)
list(GET NX_TZDB_DOWNLOAD_STATUS 0 NX_TZDB_DOWNLOAD_STATUS_CODE) list(GET NX_TZDB_DOWNLOAD_STATUS 0 NX_TZDB_DOWNLOAD_STATUS_CODE)
if (NOT NX_TZDB_DOWNLOAD_STATUS_CODE EQUAL 0) if (NOT NX_TZDB_DOWNLOAD_STATUS_CODE EQUAL 0)
message(FATAL_ERROR "Time zone data download failed (status code ${NX_TZDB_DOWNLOAD_STATUS_CODE})") message(FATAL_ERROR "Time zone data download failed (status code ${NX_TZDB_DOWNLOAD_STATUS_CODE})")
@ -47,13 +59,17 @@ if ((NOT CAN_BUILD_NX_TZDB OR YUZU_DOWNLOAD_TIME_ZONE_DATA) AND NOT EXISTS ${NX_
INPUT INPUT
${NX_TZDB_ARCHIVE} ${NX_TZDB_ARCHIVE}
DESTINATION DESTINATION
${NX_TZDB_ROMFS_DIR}) ${NX_TZDB_BASE_DIR})
elseif (CAN_BUILD_NX_TZDB AND NOT YUZU_DOWNLOAD_TIME_ZONE_DATA) else()
# TODO(crueter): this sucked to do with cpm, see if i can get it to work again message(STATUS "Downloading time zone data...")
add_subdirectory(tzdb_to_nx) AddJsonPackage(tzdb)
add_dependencies(nx_tzdb x80e)
set(NX_TZDB_ROMFS_DIR "${NX_TZDB_DIR}") target_include_directories(nx_tzdb
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include
INTERFACE ${NX_TZDB_INCLUDE_DIR})
set(NX_TZDB_BASE_DIR "${CPM_SOURCE_CACHE}/nx_tzdb")
set(NX_TZDB_TZ_DIR "${nx_tzdb_SOURCE_DIR}")
endif() endif()
target_include_directories(nx_tzdb target_include_directories(nx_tzdb
@ -78,25 +94,25 @@ function(CreateHeader ZONE_PATH HEADER_NAME)
target_sources(nx_tzdb PRIVATE ${HEADER_PATH}) target_sources(nx_tzdb PRIVATE ${HEADER_PATH})
endfunction() endfunction()
CreateHeader(${NX_TZDB_ROMFS_DIR} base) CreateHeader(${NX_TZDB_BASE_DIR} base)
CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo zoneinfo) CreateHeader(${NX_TZDB_TZ_DIR} zoneinfo)
CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Africa africa) CreateHeader(${NX_TZDB_TZ_DIR}/Africa africa)
CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/America america) CreateHeader(${NX_TZDB_TZ_DIR}/America america)
CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/America/Argentina america_argentina) CreateHeader(${NX_TZDB_TZ_DIR}/America/Argentina america_argentina)
CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/America/Indiana america_indiana) CreateHeader(${NX_TZDB_TZ_DIR}/America/Indiana america_indiana)
CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/America/Kentucky america_kentucky) CreateHeader(${NX_TZDB_TZ_DIR}/America/Kentucky america_kentucky)
CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/America/North_Dakota america_north_dakota) CreateHeader(${NX_TZDB_TZ_DIR}/America/North_Dakota america_north_dakota)
CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Antarctica antarctica) CreateHeader(${NX_TZDB_TZ_DIR}/Antarctica antarctica)
CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Arctic arctic) CreateHeader(${NX_TZDB_TZ_DIR}/Arctic arctic)
CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Asia asia) CreateHeader(${NX_TZDB_TZ_DIR}/Asia asia)
CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Atlantic atlantic) CreateHeader(${NX_TZDB_TZ_DIR}/Atlantic atlantic)
CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Australia australia) CreateHeader(${NX_TZDB_TZ_DIR}/Australia australia)
CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Brazil brazil) CreateHeader(${NX_TZDB_TZ_DIR}/Brazil brazil)
CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Canada canada) CreateHeader(${NX_TZDB_TZ_DIR}/Canada canada)
CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Chile chile) CreateHeader(${NX_TZDB_TZ_DIR}/Chile chile)
CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Etc etc) CreateHeader(${NX_TZDB_TZ_DIR}/Etc etc)
CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Europe europe) CreateHeader(${NX_TZDB_TZ_DIR}/Europe europe)
CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Indian indian) CreateHeader(${NX_TZDB_TZ_DIR}/Indian indian)
CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Mexico mexico) CreateHeader(${NX_TZDB_TZ_DIR}/Mexico mexico)
CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Pacific pacific) CreateHeader(${NX_TZDB_TZ_DIR}/Pacific pacific)
CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/US us) CreateHeader(${NX_TZDB_TZ_DIR}/US us)

8
externals/nx_tzdb/cpmfile.json vendored Normal file
View file

@ -0,0 +1,8 @@
{
"tzdb": {
"package": "nx_tzdb",
"url": "https://github.com/crueter/tzdb_to_nx/releases/download/250725/250725.zip",
"hash": "8f60b4b29f285e39c0443f3d5572a73780f3dbfcfd5b35004451fadad77f3a215b2e2aa8d0fffe7e348e2a7b0660882b35228b6178dda8804a14ce44509fd2ca",
"version": "250725"
}
}

View file

@ -173,9 +173,13 @@ android {
"-DENABLE_OPENSSL=ON", "-DENABLE_OPENSSL=ON",
"-DANDROID_ARM_NEON=true", // cryptopp requires Neon to work "-DANDROID_ARM_NEON=true", // cryptopp requires Neon to work
"-DYUZU_USE_CPM=ON", "-DYUZU_USE_CPM=ON",
"-DCPMUTIL_FORCE_BUNDLED=ON",
"-DYUZU_USE_BUNDLED_FFMPEG=ON", "-DYUZU_USE_BUNDLED_FFMPEG=ON",
"-DYUZU_ENABLE_LTO=ON", "-DYUZU_ENABLE_LTO=ON",
"-DCMAKE_EXPORT_COMPILE_COMMANDS=ON" "-DCMAKE_EXPORT_COMPILE_COMMANDS=ON",
"-DBUILD_TESTING=OFF",
"-DYUZU_TESTS=OFF",
"-DDYNARMIC_TESTS=OFF"
) )
abiFilters("arm64-v8a") abiFilters("arm64-v8a")

View file

@ -79,7 +79,7 @@ class DriverFetcherFragment : Fragment() {
IntRange(600, 639) to "Mr. Purple EOL-24.3.4", IntRange(600, 639) to "Mr. Purple EOL-24.3.4",
IntRange(640, 699) to "Mr. Purple T19", IntRange(640, 699) to "Mr. Purple T19",
IntRange(700, 710) to "KIMCHI 25.2.0_r5", IntRange(700, 710) to "KIMCHI 25.2.0_r5",
IntRange(711, 799) to "Mr. Purple T21", IntRange(711, 799) to "Mr. Purple T22",
IntRange(800, 899) to "GameHub Adreno 8xx", IntRange(800, 899) to "GameHub Adreno 8xx",
IntRange(900, Int.MAX_VALUE) to "Unsupported" IntRange(900, Int.MAX_VALUE) to "Unsupported"
) )

View file

@ -509,6 +509,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
gpuModel = GpuDriverHelper.getGpuModel().toString() gpuModel = GpuDriverHelper.getGpuModel().toString()
fwVersion = NativeLibrary.firmwareVersion() fwVersion = NativeLibrary.firmwareVersion()
updateQuickOverlayMenuEntry(BooleanSetting.SHOW_INPUT_OVERLAY.getBoolean())
binding.surfaceEmulation.holder.addCallback(this) binding.surfaceEmulation.holder.addCallback(this)
binding.doneControlConfig.setOnClickListener { stopConfiguringControls() } binding.doneControlConfig.setOnClickListener { stopConfiguringControls() }
@ -530,6 +532,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED) binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
binding.inGameMenu.requestFocus() binding.inGameMenu.requestFocus()
emulationViewModel.setDrawerOpen(true) emulationViewModel.setDrawerOpen(true)
updateQuickOverlayMenuEntry(BooleanSetting.SHOW_INPUT_OVERLAY.getBoolean())
} }
override fun onDrawerClosed(drawerView: View) { override fun onDrawerClosed(drawerView: View) {
@ -571,25 +574,24 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
R.id.menu_pause_emulation -> { R.id.menu_pause_emulation -> {
if (emulationState.isPaused) { if (emulationState.isPaused) {
emulationState.run(false) emulationState.run(false)
it.title = resources.getString(R.string.emulation_pause) updatePauseMenuEntry(false)
it.icon = ResourcesCompat.getDrawable(
resources,
R.drawable.ic_pause,
requireContext().theme
)
} else { } else {
emulationState.pause() emulationState.pause()
it.title = resources.getString(R.string.emulation_unpause) updatePauseMenuEntry(true)
it.icon = ResourcesCompat.getDrawable(
resources,
R.drawable.ic_play,
requireContext().theme
)
} }
binding.inGameMenu.requestFocus() binding.inGameMenu.requestFocus()
true true
} }
R.id.menu_quick_overlay -> {
val newState = !BooleanSetting.SHOW_INPUT_OVERLAY.getBoolean()
BooleanSetting.SHOW_INPUT_OVERLAY.setBoolean(newState)
updateQuickOverlayMenuEntry(newState)
binding.surfaceInputOverlay.refreshControls()
NativeConfig.saveGlobalConfig()
true
}
R.id.menu_settings -> { R.id.menu_settings -> {
val action = HomeNavigationDirections.actionGlobalSettingsActivity( val action = HomeNavigationDirections.actionGlobalSettingsActivity(
null, null,
@ -844,9 +846,50 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
} }
} }
private fun updateQuickOverlayMenuEntry(isVisible: Boolean) {
val menu = binding.inGameMenu.menu
val item = menu.findItem(R.id.menu_quick_overlay)
if (isVisible) {
item.title = getString(R.string.emulation_hide_overlay)
item.icon = ResourcesCompat.getDrawable(
resources,
R.drawable.ic_controller_disconnected,
requireContext().theme
)
} else {
item.title = getString(R.string.emulation_show_overlay)
item.icon = ResourcesCompat.getDrawable(
resources,
R.drawable.ic_controller,
requireContext().theme
)
}
}
private fun updatePauseMenuEntry(isPaused: Boolean) {
val menu = binding.inGameMenu.menu
val pauseItem = menu.findItem(R.id.menu_pause_emulation)
if (isPaused) {
pauseItem.title = getString(R.string.emulation_unpause)
pauseItem.icon = ResourcesCompat.getDrawable(
resources,
R.drawable.ic_play,
requireContext().theme
)
} else {
pauseItem.title = getString(R.string.emulation_pause)
pauseItem.icon = ResourcesCompat.getDrawable(
resources,
R.drawable.ic_pause,
requireContext().theme
)
}
}
override fun onPause() { override fun onPause() {
if (emulationState.isRunning && emulationActivity?.isInPictureInPictureMode != true) { if (emulationState.isRunning && emulationActivity?.isInPictureInPictureMode != true) {
emulationState.pause() emulationState.pause()
updatePauseMenuEntry(true)
} }
super.onPause() super.onPause()
} }
@ -869,6 +912,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
val socPosition = IntSetting.SOC_OVERLAY_POSITION.getInt() val socPosition = IntSetting.SOC_OVERLAY_POSITION.getInt()
updateSocPosition(socPosition) updateSocPosition(socPosition)
binding.inGameMenu.post {
emulationState?.isPaused?.let { updatePauseMenuEntry(it) }
}
} }
private fun resetInputOverlay() { private fun resetInputOverlay() {
@ -1391,6 +1438,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
R.id.menu_show_overlay -> { R.id.menu_show_overlay -> {
it.isChecked = !it.isChecked it.isChecked = !it.isChecked
BooleanSetting.SHOW_INPUT_OVERLAY.setBoolean(it.isChecked) BooleanSetting.SHOW_INPUT_OVERLAY.setBoolean(it.isChecked)
updateQuickOverlayMenuEntry(it.isChecked)
binding.surfaceInputOverlay.refreshControls() binding.surfaceInputOverlay.refreshControls()
true true
} }

View file

@ -38,6 +38,7 @@ import org.yuzu.yuzu_emu.model.DriverViewModel
import org.yuzu.yuzu_emu.model.GamesViewModel import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.model.InstallResult import org.yuzu.yuzu_emu.model.InstallResult
import android.os.Build
import org.yuzu.yuzu_emu.model.TaskState import org.yuzu.yuzu_emu.model.TaskState
import org.yuzu.yuzu_emu.model.TaskViewModel import org.yuzu.yuzu_emu.model.TaskViewModel
import org.yuzu.yuzu_emu.utils.* import org.yuzu.yuzu_emu.utils.*
@ -47,6 +48,7 @@ import java.io.BufferedOutputStream
import java.util.zip.ZipEntry import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream import java.util.zip.ZipInputStream
import androidx.core.content.edit import androidx.core.content.edit
import kotlin.text.compareTo
class MainActivity : AppCompatActivity(), ThemeProvider { class MainActivity : AppCompatActivity(), ThemeProvider {
private lateinit var binding: ActivityMainBinding private lateinit var binding: ActivityMainBinding
@ -110,6 +112,19 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
binding = ActivityMainBinding.inflate(layoutInflater) binding = ActivityMainBinding.inflate(layoutInflater)
// Since Android 15, google automatically forces "games" to be 60 hrz
// This ensures the display's max refresh rate is actually used
display?.let {
val supportedModes = it.supportedModes
val maxRefreshRate = supportedModes.maxByOrNull { mode -> mode.refreshRate }
if (maxRefreshRate != null) {
val layoutParams = window.attributes
layoutParams.preferredDisplayModeId = maxRefreshRate.modeId
window.attributes = layoutParams
}
}
setContentView(binding.root) setContentView(binding.root)
checkAndRequestBluetoothPermissions() checkAndRequestBluetoothPermissions()

View file

@ -124,11 +124,16 @@ object CustomSettingsHandler {
// Check for driver requirements if activity and driverViewModel are provided // Check for driver requirements if activity and driverViewModel are provided
if (activity != null && driverViewModel != null) { if (activity != null && driverViewModel != null) {
val driverPath = extractDriverPath(customSettings) val rawDriverPath = extractDriverPath(customSettings)
if (driverPath != null) { if (rawDriverPath != null) {
Log.info("[CustomSettingsHandler] Custom settings specify driver: $driverPath") // Normalize to local storage path (we only store drivers under driverStoragePath)
val driverFilename = rawDriverPath.substringAfterLast('/')
.substringAfterLast('\\')
val localDriverPath = "${GpuDriverHelper.driverStoragePath}$driverFilename"
Log.info("[CustomSettingsHandler] Custom settings specify driver: $rawDriverPath (normalized: $localDriverPath)")
// Check if driver exists in the driver storage // Check if driver exists in the driver storage
val driverFile = File(driverPath) val driverFile = File(localDriverPath)
if (!driverFile.exists()) { if (!driverFile.exists()) {
Log.info("[CustomSettingsHandler] Driver not found locally: ${driverFile.name}") Log.info("[CustomSettingsHandler] Driver not found locally: ${driverFile.name}")
@ -182,7 +187,7 @@ object CustomSettingsHandler {
} }
// Attempt to download and install the driver // Attempt to download and install the driver
val driverUri = DriverResolver.ensureDriverAvailable(driverPath, activity) { progress -> val driverUri = DriverResolver.ensureDriverAvailable(driverFilename, activity) { progress ->
progressChannel.trySend(progress.toInt()) progressChannel.trySend(progress.toInt())
} }
@ -209,12 +214,12 @@ object CustomSettingsHandler {
return null return null
} }
// Verify the downloaded driver // Verify the downloaded driver (from normalized local path)
val installedFile = File(driverPath) val installedFile = File(localDriverPath)
val metadata = GpuDriverHelper.getMetadataFromZip(installedFile) val metadata = GpuDriverHelper.getMetadataFromZip(installedFile)
if (metadata.name == null) { if (metadata.name == null) {
Log.error( Log.error(
"[CustomSettingsHandler] Downloaded driver is invalid: $driverPath" "[CustomSettingsHandler] Downloaded driver is invalid: $localDriverPath"
) )
Toast.makeText( Toast.makeText(
activity, activity,
@ -232,7 +237,7 @@ object CustomSettingsHandler {
} }
// Add to driver list // Add to driver list
driverViewModel.onDriverAdded(Pair(driverPath, metadata)) driverViewModel.onDriverAdded(Pair(localDriverPath, metadata))
Log.info( Log.info(
"[CustomSettingsHandler] Successfully downloaded and installed driver: ${metadata.name}" "[CustomSettingsHandler] Successfully downloaded and installed driver: ${metadata.name}"
) )
@ -268,7 +273,7 @@ object CustomSettingsHandler {
// Driver exists, verify it's valid // Driver exists, verify it's valid
val metadata = GpuDriverHelper.getMetadataFromZip(driverFile) val metadata = GpuDriverHelper.getMetadataFromZip(driverFile)
if (metadata.name == null) { if (metadata.name == null) {
Log.error("[CustomSettingsHandler] Invalid driver file: $driverPath") Log.error("[CustomSettingsHandler] Invalid driver file: $localDriverPath")
Toast.makeText( Toast.makeText(
activity, activity,
activity.getString( activity.getString(
@ -459,6 +464,8 @@ object CustomSettingsHandler {
if (inGpuDriverSection && trimmed.startsWith("driver_path=")) { if (inGpuDriverSection && trimmed.startsWith("driver_path=")) {
return trimmed.substringAfter("driver_path=") return trimmed.substringAfter("driver_path=")
.trim()
.removeSurrounding("\"", "\"")
} }
} }

View file

@ -68,6 +68,48 @@ object DriverResolver {
val filename: String val filename: String
) )
// Matching helpers
private val KNOWN_SUFFIXES = listOf(
".adpkg.zip",
".zip",
".7z",
".tar.gz",
".tar.xz",
".rar"
)
private fun stripKnownSuffixes(name: String): String {
var result = name
var changed: Boolean
do {
changed = false
for (s in KNOWN_SUFFIXES) {
if (result.endsWith(s, ignoreCase = true)) {
result = result.dropLast(s.length)
changed = true
}
}
} while (changed)
return result
}
private fun normalizeName(name: String): String {
val base = stripKnownSuffixes(name.lowercase())
// Remove non-alphanumerics to make substring checks resilient
return base.replace(Regex("[^a-z0-9]+"), " ").trim()
}
private fun tokenize(name: String): Set<String> =
normalizeName(name).split(Regex("\\s+")).filter { it.isNotBlank() }.toSet()
// Jaccard similarity between two sets
private fun jaccard(a: Set<String>, b: Set<String>): Double {
if (a.isEmpty() || b.isEmpty()) return 0.0
val inter = a.intersect(b).size.toDouble()
val uni = a.union(b).size.toDouble()
return if (uni == 0.0) 0.0 else inter / uni
}
/** /**
* Resolve a driver download URL from its filename * Resolve a driver download URL from its filename
* @param filename The driver filename (e.g., "turnip_mrpurple-T19-toasted.adpkg.zip") * @param filename The driver filename (e.g., "turnip_mrpurple-T19-toasted.adpkg.zip")
@ -98,7 +140,7 @@ object DriverResolver {
async { async {
searchRepository(repoPath, filename) searchRepository(repoPath, filename)
} }
}.mapNotNull { it.await() }.firstOrNull().also { resolved -> }.firstNotNullOfOrNull { it.await() }.also { resolved ->
// Cache the result if found // Cache the result if found
resolved?.let { resolved?.let {
urlCache[filename] = it urlCache[filename] = it
@ -119,22 +161,56 @@ object DriverResolver {
releaseCache[repoPath] = it releaseCache[repoPath] = it
} }
// Search through all releases and artifacts // First pass: exact name (case-insensitive) against asset filenames
val target = filename.lowercase()
for (release in releases) { for (release in releases) {
for (artifact in release.artifacts) { for (artifact in release.artifacts) {
if (artifact.name == filename) { if (artifact.name.equals(filename, ignoreCase = true) || artifact.name.lowercase() == target) {
Log.info( Log.info("[DriverResolver] Found $filename in $repoPath/${release.tagName}")
"[DriverResolver] Found $filename in $repoPath/${release.tagName}"
)
return@withContext ResolvedDriver( return@withContext ResolvedDriver(
downloadUrl = artifact.url.toString(), downloadUrl = artifact.url.toString(),
repoPath = repoPath, repoPath = repoPath,
releaseTag = release.tagName, releaseTag = release.tagName,
filename = filename filename = artifact.name
) )
} }
} }
} }
// Second pass: fuzzy match by asset filenames only
val reqNorm = normalizeName(filename)
val reqTokens = tokenize(filename)
var best: ResolvedDriver? = null
var bestScore = 0.0
for (release in releases) {
for (artifact in release.artifacts) {
val artNorm = normalizeName(artifact.name)
val artTokens = tokenize(artifact.name)
var score = jaccard(reqTokens, artTokens)
// Boost if one normalized name contains the other
if (artNorm.contains(reqNorm) || reqNorm.contains(artNorm)) {
score = maxOf(score, 0.92)
}
if (score > bestScore) {
bestScore = score
best = ResolvedDriver(
downloadUrl = artifact.url.toString(),
repoPath = repoPath,
releaseTag = release.tagName,
filename = artifact.name
)
}
}
}
// Threshold to avoid bad guesses, this worked fine in testing but might need tuning
if (best != null && bestScore >= 0.6) {
Log.info("[DriverResolver] Fuzzy matched $filename -> ${best.filename} in ${best.repoPath} (score=%.2f)".format(bestScore))
return@withContext best
}
null null
} catch (e: Exception) { } catch (e: Exception) {
Log.error("[DriverResolver] Failed to search $repoPath: ${e.message}") Log.error("[DriverResolver] Failed to search $repoPath: ${e.message}")
@ -296,8 +372,8 @@ object DriverResolver {
context: Context, context: Context,
onProgress: ((Float) -> Unit)? = null onProgress: ((Float) -> Unit)? = null
): Uri? { ): Uri? {
// Extract filename from path // Extract filename from path (support both separators)
val filename = driverPath.substringAfterLast('/') val filename = driverPath.substringAfterLast('/').substringAfterLast('\\')
// Check if driver already exists locally // Check if driver already exists locally
val localPath = "${GpuDriverHelper.driverStoragePath}$filename" val localPath = "${GpuDriverHelper.driverStoragePath}$filename"

View file

@ -17,7 +17,7 @@ add_library(yuzu-android SHARED
set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR}) set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR})
target_link_libraries(yuzu-android PRIVATE audio_core common core input_common frontend_common Vulkan::Headers) target_link_libraries(yuzu-android PRIVATE audio_core common core input_common frontend_common Vulkan::Headers GPUOpen::VulkanMemoryAllocator)
target_link_libraries(yuzu-android PRIVATE android camera2ndk EGL glad jnigraphics log) target_link_libraries(yuzu-android PRIVATE android camera2ndk EGL glad jnigraphics log)
if (ARCHITECTURE_arm64) if (ARCHITECTURE_arm64)
target_link_libraries(yuzu-android PRIVATE adrenotools) target_link_libraries(yuzu-android PRIVATE adrenotools)

View file

@ -8,6 +8,11 @@
android:icon="@drawable/ic_pause" android:icon="@drawable/ic_pause"
android:title="@string/emulation_pause" /> android:title="@string/emulation_pause" />
<item
android:id="@+id/menu_quick_overlay"
android:icon="@drawable/ic_controller"
android:title="@string/emulation_show_overlay"/>
<item <item
android:id="@+id/menu_settings" android:id="@+id/menu_settings"
android:icon="@drawable/ic_settings" android:icon="@drawable/ic_settings"

View file

@ -733,7 +733,8 @@
<string name="emulation_rel_stick_center">مركز العصا النسبي</string> <string name="emulation_rel_stick_center">مركز العصا النسبي</string>
<string name="emulation_dpad_slide">مزلاق الأسهم</string> <string name="emulation_dpad_slide">مزلاق الأسهم</string>
<string name="emulation_haptics">الاهتزازات الديناميكية</string> <string name="emulation_haptics">الاهتزازات الديناميكية</string>
<string name="emulation_show_overlay">عرض التراكب</string> <string name="emulation_show_overlay">إظهار وحدة التحكم</string>
<string name="emulation_hide_overlay">إخفاء وحدة التحكم</string>
<string name="emulation_toggle_all">الكل</string> <string name="emulation_toggle_all">الكل</string>
<string name="emulation_control_adjust">ضبط التراكب</string> <string name="emulation_control_adjust">ضبط التراكب</string>
<string name="emulation_control_scale">الحجم</string> <string name="emulation_control_scale">الحجم</string>

View file

@ -710,7 +710,8 @@
<string name="emulation_rel_stick_center">ناوەندی گێڕ بەنزیکەیی</string> <string name="emulation_rel_stick_center">ناوەندی گێڕ بەنزیکەیی</string>
<string name="emulation_dpad_slide">خلیسکانی 4 دوگمەکە</string> <string name="emulation_dpad_slide">خلیسکانی 4 دوگمەکە</string>
<string name="emulation_haptics">لەرینەوەی پەنجەلێدان</string> <string name="emulation_haptics">لەرینەوەی پەنجەلێدان</string>
<string name="emulation_show_overlay">نیشاندانی داپۆشەر</string> <string name="emulation_show_overlay">نیشاندانی کۆنتڕۆڵەر</string>
<string name="emulation_hide_overlay">پیشاندانی کۆنتڕۆڵەر</string>
<string name="emulation_toggle_all">گۆڕینی سەرجەم</string> <string name="emulation_toggle_all">گۆڕینی سەرجەم</string>
<string name="emulation_control_adjust">ڕێکخستنی داپۆشەر</string> <string name="emulation_control_adjust">ڕێکخستنی داپۆشەر</string>
<string name="emulation_control_scale">پێوەر</string> <string name="emulation_control_scale">پێوەر</string>

View file

@ -691,7 +691,8 @@
<string name="emulation_rel_stick_center">Relativní střed joysticku</string> <string name="emulation_rel_stick_center">Relativní střed joysticku</string>
<string name="emulation_dpad_slide">D-pad slide</string> <string name="emulation_dpad_slide">D-pad slide</string>
<string name="emulation_haptics">Haptická odezva</string> <string name="emulation_haptics">Haptická odezva</string>
<string name="emulation_show_overlay">Zobrazit překryv</string> <string name="emulation_show_overlay">Zobrazit ovladač</string>
<string name="emulation_hide_overlay">Skrýt ovladač</string>
<string name="emulation_toggle_all">Přepnout vše</string> <string name="emulation_toggle_all">Přepnout vše</string>
<string name="emulation_control_adjust">Upravit překryv</string> <string name="emulation_control_adjust">Upravit překryv</string>
<string name="emulation_control_scale">Měřítko</string> <string name="emulation_control_scale">Měřítko</string>

View file

@ -762,6 +762,13 @@ Wirklich fortfahren?</string>
<string name="emulation_exit">Emulation beenden</string> <string name="emulation_exit">Emulation beenden</string>
<string name="emulation_done">Fertig</string> <string name="emulation_done">Fertig</string>
<string name="emulation_fps_counter">FPS Zähler</string> <string name="emulation_fps_counter">FPS Zähler</string>
<string name="emulation_thermal_indicator"></string>
<string name="emulation_toggle_controls">Steuerung umschalten</string>
<string name="emulation_rel_stick_center">Relativer Stick-Zentrum</string>
<string name="emulation_dpad_slide">D-Pad-Scrollen</string>
<string name="emulation_haptics">Haptisches Feedback</string>
<string name="emulation_show_overlay">Controller anzeigen</string>
<string name="emulation_hide_overlay">Controller ausblenden</string>
<string name="emulation_toggle_all">Alle umschalten</string> <string name="emulation_toggle_all">Alle umschalten</string>
<string name="emulation_control_adjust">Overlay anpassen</string> <string name="emulation_control_adjust">Overlay anpassen</string>
<string name="emulation_control_scale">Größe</string> <string name="emulation_control_scale">Größe</string>

View file

@ -806,7 +806,8 @@
<string name="emulation_rel_stick_center">Centro relativo del stick</string> <string name="emulation_rel_stick_center">Centro relativo del stick</string>
<string name="emulation_dpad_slide">Deslizamiento de la cruceta</string> <string name="emulation_dpad_slide">Deslizamiento de la cruceta</string>
<string name="emulation_haptics">Toques hápticos</string> <string name="emulation_haptics">Toques hápticos</string>
<string name="emulation_show_overlay">Mostrar overlay</string> <string name="emulation_show_overlay">Mostrar controlador</string>
<string name="emulation_hide_overlay">Ocultar controlador</string>
<string name="emulation_toggle_all">Alternar todo</string> <string name="emulation_toggle_all">Alternar todo</string>
<string name="emulation_control_adjust">Ajustar overlay</string> <string name="emulation_control_adjust">Ajustar overlay</string>
<string name="emulation_control_scale">Escala</string> <string name="emulation_control_scale">Escala</string>

View file

@ -805,7 +805,8 @@
<string name="emulation_rel_stick_center">مرکز نسبی استیک</string> <string name="emulation_rel_stick_center">مرکز نسبی استیک</string>
<string name="emulation_dpad_slide">لغزش دکمه‌های جهتی</string> <string name="emulation_dpad_slide">لغزش دکمه‌های جهتی</string>
<string name="emulation_haptics">لرزش لمسی</string> <string name="emulation_haptics">لرزش لمسی</string>
<string name="emulation_show_overlay">نشان دادن نمایش روی صفحه</string> <string name="emulation_show_overlay">نمایش کنترلر</string>
<string name="emulation_hide_overlay">پنهان کردن کنترلر</string>
<string name="emulation_toggle_all">تغییر همه</string> <string name="emulation_toggle_all">تغییر همه</string>
<string name="emulation_control_adjust">تنظیم نمایش روی صفحه</string> <string name="emulation_control_adjust">تنظیم نمایش روی صفحه</string>
<string name="emulation_control_scale">مقیاس</string> <string name="emulation_control_scale">مقیاس</string>

View file

@ -854,7 +854,8 @@
<string name="emulation_rel_stick_center">Centre du stick relatif</string> <string name="emulation_rel_stick_center">Centre du stick relatif</string>
<string name="emulation_dpad_slide">Glissement du D-pad</string> <string name="emulation_dpad_slide">Glissement du D-pad</string>
<string name="emulation_haptics">Toucher haptique</string> <string name="emulation_haptics">Toucher haptique</string>
<string name="emulation_show_overlay">Afficher l\'overlay</string> <string name="emulation_show_overlay">Afficher la manette</string>
<string name="emulation_hide_overlay">Masquer la manette</string>
<string name="emulation_toggle_all">Tout basculer</string> <string name="emulation_toggle_all">Tout basculer</string>
<string name="emulation_control_adjust">Ajuster l\'overlay</string> <string name="emulation_control_adjust">Ajuster l\'overlay</string>
<string name="emulation_control_scale">Échelle</string> <string name="emulation_control_scale">Échelle</string>

View file

@ -739,7 +739,8 @@
<string name="emulation_rel_stick_center">מרכז ג׳ויסטיק יחסי</string> <string name="emulation_rel_stick_center">מרכז ג׳ויסטיק יחסי</string>
<string name="emulation_dpad_slide">החלקת D-pad</string> <string name="emulation_dpad_slide">החלקת D-pad</string>
<string name="emulation_haptics">רטט מגע</string> <string name="emulation_haptics">רטט מגע</string>
<string name="emulation_show_overlay">הצג את שכבת-העל</string> <string name="emulation_show_overlay">הצג בקר</string>
<string name="emulation_hide_overlay">הסתר בקר</string>
<string name="emulation_toggle_all">החלף הכל</string> <string name="emulation_toggle_all">החלף הכל</string>
<string name="emulation_control_adjust">התאם את שכבת-העל</string> <string name="emulation_control_adjust">התאם את שכבת-העל</string>
<string name="emulation_control_scale">קנה מידה</string> <string name="emulation_control_scale">קנה מידה</string>

View file

@ -843,7 +843,8 @@
<string name="emulation_toggle_controls">Irányítás átkapcsolása</string> <string name="emulation_toggle_controls">Irányítás átkapcsolása</string>
<string name="emulation_dpad_slide">D-pad csúsztatása</string> <string name="emulation_dpad_slide">D-pad csúsztatása</string>
<string name="emulation_haptics">Érintés haptikája</string> <string name="emulation_haptics">Érintés haptikája</string>
<string name="emulation_show_overlay">Átfedés mutatása</string> <string name="emulation_show_overlay">Vezérlő megjelenítése</string>
<string name="emulation_hide_overlay">Vezérlő elrejtése</string>
<string name="emulation_toggle_all">Összes átkapcsolása</string> <string name="emulation_toggle_all">Összes átkapcsolása</string>
<string name="emulation_control_adjust">Átfedés testreszabása</string> <string name="emulation_control_adjust">Átfedés testreszabása</string>
<string name="emulation_control_scale">Skálázás</string> <string name="emulation_control_scale">Skálázás</string>

View file

@ -798,7 +798,8 @@
<string name="emulation_rel_stick_center">Pusat stick relatif</string> <string name="emulation_rel_stick_center">Pusat stick relatif</string>
<string name="emulation_dpad_slide">Geser Dpad</string> <string name="emulation_dpad_slide">Geser Dpad</string>
<string name="emulation_haptics">Haptik</string> <string name="emulation_haptics">Haptik</string>
<string name="emulation_show_overlay">Tampilkan Hamparan</string> <string name="emulation_show_overlay">Tampilkan Kontroler</string>
<string name="emulation_hide_overlay">Sembunyikan Kontroler</string>
<string name="emulation_toggle_all">Alihkan Semua</string> <string name="emulation_toggle_all">Alihkan Semua</string>
<string name="emulation_control_adjust">Menyesuaikan</string> <string name="emulation_control_adjust">Menyesuaikan</string>
<string name="emulation_control_scale">Skala</string> <string name="emulation_control_scale">Skala</string>

View file

@ -770,7 +770,8 @@
<string name="emulation_rel_stick_center">Centro relativo degli Stick</string> <string name="emulation_rel_stick_center">Centro relativo degli Stick</string>
<string name="emulation_dpad_slide">DPad A Scorrimento</string> <string name="emulation_dpad_slide">DPad A Scorrimento</string>
<string name="emulation_haptics">Feedback Aptico</string> <string name="emulation_haptics">Feedback Aptico</string>
<string name="emulation_show_overlay">Mostra l\'overlay</string> <string name="emulation_show_overlay">Mostra l\'controller</string>
<string name="emulation_hide_overlay">Nascondi l\'controller</string>
<string name="emulation_toggle_all">Attiva/Disattiva tutto</string> <string name="emulation_toggle_all">Attiva/Disattiva tutto</string>
<string name="emulation_control_adjust">Regola l\'overlay</string> <string name="emulation_control_adjust">Regola l\'overlay</string>
<string name="emulation_control_scale">Scala</string> <string name="emulation_control_scale">Scala</string>

View file

@ -729,7 +729,8 @@
<string name="emulation_rel_stick_center">スティックを固定しない</string> <string name="emulation_rel_stick_center">スティックを固定しない</string>
<string name="emulation_dpad_slide">十字キーをスライド操作</string> <string name="emulation_dpad_slide">十字キーをスライド操作</string>
<string name="emulation_haptics">タッチ振動</string> <string name="emulation_haptics">タッチ振動</string>
<string name="emulation_show_overlay">ボタンを表示</string> <string name="emulation_show_overlay">コントローラーを表示</string>
<string name="emulation_hide_overlay">コントローラーを非表示</string>
<string name="emulation_toggle_all">すべて切替</string> <string name="emulation_toggle_all">すべて切替</string>
<string name="emulation_control_adjust">見た目を調整</string> <string name="emulation_control_adjust">見た目を調整</string>
<string name="emulation_control_scale">大きさ</string> <string name="emulation_control_scale">大きさ</string>

View file

@ -798,6 +798,7 @@
<string name="emulation_dpad_slide">십자키 슬라이드</string> <string name="emulation_dpad_slide">십자키 슬라이드</string>
<string name="emulation_haptics">터치 햅틱</string> <string name="emulation_haptics">터치 햅틱</string>
<string name="emulation_show_overlay">컨트롤러 표시</string> <string name="emulation_show_overlay">컨트롤러 표시</string>
<string name="emulation_hide_overlay">컨트롤러 숨기기</string>
<string name="emulation_toggle_all">모두 선택</string> <string name="emulation_toggle_all">모두 선택</string>
<string name="emulation_control_adjust">컨트롤러 조정</string> <string name="emulation_control_adjust">컨트롤러 조정</string>
<string name="emulation_control_scale">크기</string> <string name="emulation_control_scale">크기</string>

View file

@ -720,7 +720,8 @@
<string name="emulation_rel_stick_center">Relativt pinnesenter</string> <string name="emulation_rel_stick_center">Relativt pinnesenter</string>
<string name="emulation_dpad_slide">D-pad-skyving</string> <string name="emulation_dpad_slide">D-pad-skyving</string>
<string name="emulation_haptics">Berøringshaptikk</string> <string name="emulation_haptics">Berøringshaptikk</string>
<string name="emulation_show_overlay">Vis overlegg</string> <string name="emulation_show_overlay">Vis kontroller</string>
<string name="emulation_hide_overlay">Skjul kontroller</string>
<string name="emulation_toggle_all">Veksle mellom alle</string> <string name="emulation_toggle_all">Veksle mellom alle</string>
<string name="emulation_control_adjust">Juster overlegg</string> <string name="emulation_control_adjust">Juster overlegg</string>
<string name="emulation_control_scale">Skaler</string> <string name="emulation_control_scale">Skaler</string>

View file

@ -718,7 +718,8 @@
<string name="emulation_rel_stick_center">Wycentruj gałki</string> <string name="emulation_rel_stick_center">Wycentruj gałki</string>
<string name="emulation_dpad_slide">Ruchomy D-pad</string> <string name="emulation_dpad_slide">Ruchomy D-pad</string>
<string name="emulation_haptics">Wibracje haptyczne</string> <string name="emulation_haptics">Wibracje haptyczne</string>
<string name="emulation_show_overlay">Pokaż przyciski</string> <string name="emulation_show_overlay">Pokaż kontroler</string>
<string name="emulation_hide_overlay">Ukryj kontroler</string>
<string name="emulation_toggle_all">Włącz wszystkie</string> <string name="emulation_toggle_all">Włącz wszystkie</string>
<string name="emulation_control_adjust">Dostosuj nakładkę</string> <string name="emulation_control_adjust">Dostosuj nakładkę</string>
<string name="emulation_control_scale">Skala</string> <string name="emulation_control_scale">Skala</string>

View file

@ -855,7 +855,8 @@ uma tentativa de mapeamento automático</string>
<string name="emulation_rel_stick_center">Centro Relativo do Analógico</string> <string name="emulation_rel_stick_center">Centro Relativo do Analógico</string>
<string name="emulation_dpad_slide">Deslizamento dos Botões Direcionais</string> <string name="emulation_dpad_slide">Deslizamento dos Botões Direcionais</string>
<string name="emulation_haptics">Vibração ao tocar</string> <string name="emulation_haptics">Vibração ao tocar</string>
<string name="emulation_show_overlay">Mostrar overlay</string> <string name="emulation_show_overlay">Mostrar controle</string>
<string name="emulation_hide_overlay">Ocultar controle</string>
<string name="emulation_toggle_all">Marcar/Desmarcar tudo</string> <string name="emulation_toggle_all">Marcar/Desmarcar tudo</string>
<string name="emulation_control_adjust">Ajustar overlay</string> <string name="emulation_control_adjust">Ajustar overlay</string>
<string name="emulation_control_scale">Escala</string> <string name="emulation_control_scale">Escala</string>

View file

@ -855,7 +855,8 @@ uma tentativa de mapeamento automático</string>
<string name="emulation_rel_stick_center">Centro Relativo de Analógico</string> <string name="emulation_rel_stick_center">Centro Relativo de Analógico</string>
<string name="emulation_dpad_slide">Deslizamento dos Botões Direcionais</string> <string name="emulation_dpad_slide">Deslizamento dos Botões Direcionais</string>
<string name="emulation_haptics">Vibração ao tocar</string> <string name="emulation_haptics">Vibração ao tocar</string>
<string name="emulation_show_overlay">Mostrar overlay</string> <string name="emulation_show_overlay">Mostrar comando</string>
<string name="emulation_hide_overlay">Ocultar comando</string>
<string name="emulation_toggle_all">Marcar/Desmarcar tudo</string> <string name="emulation_toggle_all">Marcar/Desmarcar tudo</string>
<string name="emulation_control_adjust">Ajustar overlay</string> <string name="emulation_control_adjust">Ajustar overlay</string>
<string name="emulation_control_scale">Escala</string> <string name="emulation_control_scale">Escala</string>

View file

@ -856,7 +856,8 @@
<string name="emulation_rel_stick_center">Относительный центр стика</string> <string name="emulation_rel_stick_center">Относительный центр стика</string>
<string name="emulation_dpad_slide">Слайд крестовиной</string> <string name="emulation_dpad_slide">Слайд крестовиной</string>
<string name="emulation_haptics">Обратная связь от нажатий</string> <string name="emulation_haptics">Обратная связь от нажатий</string>
<string name="emulation_show_overlay">Показать оверлей</string> <string name="emulation_show_overlay">Показать контроллер</string>
<string name="emulation_hide_overlay">Скрыть контроллер</string>
<string name="emulation_toggle_all">Переключить всё</string> <string name="emulation_toggle_all">Переключить всё</string>
<string name="emulation_control_adjust">Регулировка оверлея</string> <string name="emulation_control_adjust">Регулировка оверлея</string>
<string name="emulation_control_scale">Масштаб</string> <string name="emulation_control_scale">Масштаб</string>

View file

@ -812,7 +812,8 @@
<string name="emulation_rel_stick_center">Релативни центар за штапић</string> <string name="emulation_rel_stick_center">Релативни центар за штапић</string>
<string name="emulation_dpad_slide">Д-Пад Слиде</string> <string name="emulation_dpad_slide">Д-Пад Слиде</string>
<string name="emulation_haptics">Додирните ХАптицс</string> <string name="emulation_haptics">Додирните ХАптицс</string>
<string name="emulation_show_overlay">Приказати прекривање</string> <string name="emulation_show_overlay">Приказати контролер</string>
<string name="emulation_hide_overlay">Сакрити контролер</string>
<string name="emulation_toggle_all">Пребацивати све</string> <string name="emulation_toggle_all">Пребацивати све</string>
<string name="emulation_control_adjust">Подесити прекривање</string> <string name="emulation_control_adjust">Подесити прекривање</string>
<string name="emulation_control_scale">Скала</string> <string name="emulation_control_scale">Скала</string>

View file

@ -749,7 +749,8 @@
<string name="emulation_rel_stick_center">Відносний центр джойстика</string> <string name="emulation_rel_stick_center">Відносний центр джойстика</string>
<string name="emulation_dpad_slide">Ковзання D-pad</string> <string name="emulation_dpad_slide">Ковзання D-pad</string>
<string name="emulation_haptics">Тактильний відгук</string> <string name="emulation_haptics">Тактильний відгук</string>
<string name="emulation_show_overlay">Показати накладання</string> <string name="emulation_show_overlay">Показати контролер</string>
<string name="emulation_hide_overlay">Сховати контролер</string>
<string name="emulation_toggle_all">Перемкнути все</string> <string name="emulation_toggle_all">Перемкнути все</string>
<string name="emulation_control_adjust">Налаштувати накладання</string> <string name="emulation_control_adjust">Налаштувати накладання</string>
<string name="emulation_control_scale">Масштаб</string> <string name="emulation_control_scale">Масштаб</string>

View file

@ -723,7 +723,8 @@
<string name="emulation_rel_stick_center">Trung tâm nút cần xoay tương đối</string> <string name="emulation_rel_stick_center">Trung tâm nút cần xoay tương đối</string>
<string name="emulation_dpad_slide">Trượt D-pad</string> <string name="emulation_dpad_slide">Trượt D-pad</string>
<string name="emulation_haptics">Chạm haptics</string> <string name="emulation_haptics">Chạm haptics</string>
<string name="emulation_show_overlay">Hiện lớp phủ</string> <string name="emulation_show_overlay">Hiện bộ điều khiển</string>
<string name="emulation_hide_overlay">Ẩn bộ điều khiển</string>
<string name="emulation_toggle_all">Chuyển đổi tất cả</string> <string name="emulation_toggle_all">Chuyển đổi tất cả</string>
<string name="emulation_control_adjust">Điều chỉnh lớp phủ</string> <string name="emulation_control_adjust">Điều chỉnh lớp phủ</string>
<string name="emulation_control_scale">Tỉ lệ thu phóng</string> <string name="emulation_control_scale">Tỉ lệ thu phóng</string>

View file

@ -848,7 +848,8 @@
<string name="emulation_rel_stick_center">相对摇杆中心</string> <string name="emulation_rel_stick_center">相对摇杆中心</string>
<string name="emulation_dpad_slide">十字方向键滑动</string> <string name="emulation_dpad_slide">十字方向键滑动</string>
<string name="emulation_haptics">触觉反馈</string> <string name="emulation_haptics">触觉反馈</string>
<string name="emulation_show_overlay">显示虚拟按键</string> <string name="emulation_show_overlay">显示控制器</string>
<string name="emulation_hide_overlay">隐藏控制器</string>
<string name="emulation_toggle_all">全部切换</string> <string name="emulation_toggle_all">全部切换</string>
<string name="emulation_control_adjust">调整虚拟按键</string> <string name="emulation_control_adjust">调整虚拟按键</string>
<string name="emulation_control_scale">缩放</string> <string name="emulation_control_scale">缩放</string>

View file

@ -853,7 +853,8 @@
<string name="emulation_rel_stick_center">相對搖桿中心</string> <string name="emulation_rel_stick_center">相對搖桿中心</string>
<string name="emulation_dpad_slide">方向鍵滑動</string> <string name="emulation_dpad_slide">方向鍵滑動</string>
<string name="emulation_haptics">觸覺回饋技術</string> <string name="emulation_haptics">觸覺回饋技術</string>
<string name="emulation_show_overlay">顯示覆疊</string> <string name="emulation_show_overlay">顯示控制器</string>
<string name="emulation_hide_overlay">隱藏控制器</string>
<string name="emulation_toggle_all">全部切換</string> <string name="emulation_toggle_all">全部切換</string>
<string name="emulation_control_adjust">調整覆疊</string> <string name="emulation_control_adjust">調整覆疊</string>
<string name="emulation_control_scale">縮放</string> <string name="emulation_control_scale">縮放</string>

View file

@ -835,7 +835,8 @@
<string name="emulation_rel_stick_center">Relative stick center</string> <string name="emulation_rel_stick_center">Relative stick center</string>
<string name="emulation_dpad_slide">D-pad slide</string> <string name="emulation_dpad_slide">D-pad slide</string>
<string name="emulation_haptics">Touch haptics</string> <string name="emulation_haptics">Touch haptics</string>
<string name="emulation_show_overlay">Show overlay</string> <string name="emulation_show_overlay">Show controller</string>
<string name="emulation_hide_overlay">Hide controller</string>
<string name="emulation_toggle_all">Toggle all</string> <string name="emulation_toggle_all">Toggle all</string>
<string name="emulation_control_adjust">Adjust overlay</string> <string name="emulation_control_adjust">Adjust overlay</string>
<string name="emulation_control_scale">Scale</string> <string name="emulation_control_scale">Scale</string>

View file

@ -272,18 +272,7 @@ if (lz4_ADDED)
endif() endif()
target_link_libraries(common PUBLIC fmt::fmt stb::headers Threads::Threads) target_link_libraries(common PUBLIC fmt::fmt stb::headers Threads::Threads)
target_link_libraries(common PRIVATE lz4::lz4 LLVM::Demangle) target_link_libraries(common PRIVATE lz4::lz4 LLVM::Demangle zstd::zstd)
if (TARGET libzstd_static)
target_link_libraries(common PRIVATE libzstd_static)
else()
target_link_libraries(common PRIVATE zstd)
endif()
if (TARGET unordered_dense::unordered_dense)
# weird quirk of system installs
target_link_libraries(common PUBLIC unordered_dense::unordered_dense)
endif()
if(ANDROID) if(ANDROID)
# For ASharedMemory_create # For ASharedMemory_create

View file

@ -1,5 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -35,60 +33,68 @@ HeapTracker::~HeapTracker() = default;
void HeapTracker::Map(size_t virtual_offset, size_t host_offset, size_t length, void HeapTracker::Map(size_t virtual_offset, size_t host_offset, size_t length,
MemoryPermission perm, bool is_separate_heap) { MemoryPermission perm, bool is_separate_heap) {
bool rebuild_required = false;
// When mapping other memory, map pages immediately. // When mapping other memory, map pages immediately.
if (!is_separate_heap) { if (!is_separate_heap) {
m_buffer.Map(virtual_offset, host_offset, length, perm, false); m_buffer.Map(virtual_offset, host_offset, length, perm, false);
return; return;
} }
{ {
// We are mapping part of a separate heap and insert into mappings. // We are mapping part of a separate heap.
std::scoped_lock lk{m_lock}; std::scoped_lock lk{m_lock};
m_map_count++;
const auto it = m_mappings.insert_or_assign(virtual_offset, SeparateHeapMap{ auto* const map = new SeparateHeapMap{
.vaddr = virtual_offset,
.paddr = host_offset, .paddr = host_offset,
.size = length, .size = length,
.tick = m_tick++, .tick = m_tick++,
.perm = perm, .perm = perm,
.is_resident = false, .is_resident = false,
}); };
// Update tick before possible rebuild.
it.first->second.tick = m_tick++; // Insert into mappings.
// Check if we need to rebuild. m_map_count++;
if (m_resident_map_count >= m_max_resident_map_count) m_mappings.insert(*map);
rebuild_required = true;
// Map the area.
m_buffer.Map(it.first->first, it.first->second.paddr, it.first->second.size, it.first->second.perm, false);
// This map is now resident.
it.first->second.is_resident = true;
m_resident_map_count++;
m_resident_mappings.insert(*it.first);
} }
// A rebuild was required, so perform it now.
if (rebuild_required) // Finally, map.
this->RebuildSeparateHeapAddressSpace(); this->DeferredMapSeparateHeap(virtual_offset);
} }
void HeapTracker::Unmap(size_t virtual_offset, size_t size, bool is_separate_heap) { void HeapTracker::Unmap(size_t virtual_offset, size_t size, bool is_separate_heap) {
// If this is a separate heap... // If this is a separate heap...
if (is_separate_heap) { if (is_separate_heap) {
std::scoped_lock lk{m_lock}; std::scoped_lock lk{m_lock};
const SeparateHeapMap key{
.vaddr = virtual_offset,
};
// Split at the boundaries of the region we are removing. // Split at the boundaries of the region we are removing.
this->SplitHeapMapLocked(virtual_offset); this->SplitHeapMapLocked(virtual_offset);
this->SplitHeapMapLocked(virtual_offset + size); this->SplitHeapMapLocked(virtual_offset + size);
// Erase all mappings in range. // Erase all mappings in range.
auto it = m_mappings.find(virtual_offset); auto it = m_mappings.find(key);
while (it != m_mappings.end() && it->first < virtual_offset + size) { while (it != m_mappings.end() && it->vaddr < virtual_offset + size) {
// Get underlying item.
auto* const item = std::addressof(*it);
// If resident, erase from resident map. // If resident, erase from resident map.
if (it->second.is_resident) { if (item->is_resident) {
ASSERT(--m_resident_map_count >= 0); ASSERT(--m_resident_map_count >= 0);
m_resident_mappings.erase(m_resident_mappings.find(it->first)); m_resident_mappings.erase(m_resident_mappings.iterator_to(*item));
} }
// Erase from map. // Erase from map.
ASSERT(--m_map_count >= 0); ASSERT(--m_map_count >= 0);
it = m_mappings.erase(it); it = m_mappings.erase(it);
// Free the item.
delete item;
} }
} }
// Unmap pages. // Unmap pages.
m_buffer.Unmap(virtual_offset, size, false); m_buffer.Unmap(virtual_offset, size, false);
} }
@ -110,51 +116,110 @@ void HeapTracker::Protect(size_t virtual_offset, size_t size, MemoryPermission p
{ {
std::scoped_lock lk2{m_lock}; std::scoped_lock lk2{m_lock};
const SeparateHeapMap key{
.vaddr = next,
};
// Try to get the next mapping corresponding to this address. // Try to get the next mapping corresponding to this address.
const auto it = m_mappings.find(next); const auto it = m_mappings.nfind(key);
if (it == m_mappings.end()) { if (it == m_mappings.end()) {
// There are no separate heap mappings remaining. // There are no separate heap mappings remaining.
next = end; next = end;
should_protect = true; should_protect = true;
} else if (it->first == cur) { } else if (it->vaddr == cur) {
// We are in range. // We are in range.
// Update permission bits. // Update permission bits.
it->second.perm = perm; it->perm = perm;
// Determine next address and whether we should protect. // Determine next address and whether we should protect.
next = cur + it->second.size; next = cur + it->size;
should_protect = it->second.is_resident; should_protect = it->is_resident;
} else /* if (it->vaddr > cur) */ { } else /* if (it->vaddr > cur) */ {
// We weren't in range, but there is a block coming up that will be. // We weren't in range, but there is a block coming up that will be.
next = it->first; next = it->vaddr;
should_protect = true; should_protect = true;
} }
} }
// Clamp to end. // Clamp to end.
next = std::min(next, end); next = std::min(next, end);
// Reprotect, if we need to. // Reprotect, if we need to.
if (should_protect) if (should_protect) {
m_buffer.Protect(cur, next - cur, perm); m_buffer.Protect(cur, next - cur, perm);
}
// Advance. // Advance.
cur = next; cur = next;
} }
} }
bool HeapTracker::DeferredMapSeparateHeap(u8* fault_address) {
if (m_buffer.IsInVirtualRange(fault_address)) {
return this->DeferredMapSeparateHeap(fault_address - m_buffer.VirtualBasePointer());
}
return false;
}
bool HeapTracker::DeferredMapSeparateHeap(size_t virtual_offset) {
bool rebuild_required = false;
{
std::scoped_lock lk{m_lock};
// Check to ensure this was a non-resident separate heap mapping.
const auto it = this->GetNearestHeapMapLocked(virtual_offset);
if (it == m_mappings.end() || it->is_resident) {
return false;
}
// Update tick before possible rebuild.
it->tick = m_tick++;
// Check if we need to rebuild.
if (m_resident_map_count > m_max_resident_map_count) {
rebuild_required = true;
}
// Map the area.
m_buffer.Map(it->vaddr, it->paddr, it->size, it->perm, false);
// This map is now resident.
it->is_resident = true;
m_resident_map_count++;
m_resident_mappings.insert(*it);
}
if (rebuild_required) {
// A rebuild was required, so perform it now.
this->RebuildSeparateHeapAddressSpace();
}
return true;
}
void HeapTracker::RebuildSeparateHeapAddressSpace() { void HeapTracker::RebuildSeparateHeapAddressSpace() {
std::scoped_lock lk{m_rebuild_lock, m_lock}; std::scoped_lock lk{m_rebuild_lock, m_lock};
ASSERT(!m_resident_mappings.empty()); ASSERT(!m_resident_mappings.empty());
// Dump half of the mappings. // Dump half of the mappings.
//
// Despite being worse in theory, this has proven to be better in practice than more // Despite being worse in theory, this has proven to be better in practice than more
// regularly dumping a smaller amount, because it significantly reduces average case // regularly dumping a smaller amount, because it significantly reduces average case
// lock contention. // lock contention.
std::size_t const desired_count = std::min(m_resident_map_count, m_max_resident_map_count) / 2; const size_t desired_count = std::min(m_resident_map_count, m_max_resident_map_count) / 2;
std::size_t const evict_count = m_resident_map_count - desired_count; const size_t evict_count = m_resident_map_count - desired_count;
auto it = m_resident_mappings.begin(); auto it = m_resident_mappings.begin();
for (std::size_t i = 0; i < evict_count && it != m_resident_mappings.end(); i++) {
for (size_t i = 0; i < evict_count && it != m_resident_mappings.end(); i++) {
// Unmark and unmap. // Unmark and unmap.
it->second.is_resident = false; it->is_resident = false;
m_buffer.Unmap(it->first, it->second.size, false); m_buffer.Unmap(it->vaddr, it->size, false);
// Advance. // Advance.
ASSERT(--m_resident_map_count >= 0); ASSERT(--m_resident_map_count >= 0);
it = m_resident_mappings.erase(it); it = m_resident_mappings.erase(it);
@ -163,32 +228,53 @@ void HeapTracker::RebuildSeparateHeapAddressSpace() {
void HeapTracker::SplitHeapMap(VAddr offset, size_t size) { void HeapTracker::SplitHeapMap(VAddr offset, size_t size) {
std::scoped_lock lk{m_lock}; std::scoped_lock lk{m_lock};
this->SplitHeapMapLocked(offset); this->SplitHeapMapLocked(offset);
this->SplitHeapMapLocked(offset + size); this->SplitHeapMapLocked(offset + size);
} }
void HeapTracker::SplitHeapMapLocked(VAddr offset) { void HeapTracker::SplitHeapMapLocked(VAddr offset) {
auto it = this->GetNearestHeapMapLocked(offset); const auto it = this->GetNearestHeapMapLocked(offset);
if (it != m_mappings.end() && it->first != offset) { if (it == m_mappings.end() || it->vaddr == offset) {
// Adjust left iterator // Not contained or no split required.
auto const orig_size = it->second.size; return;
auto const left_size = offset - it->first;
it->second.size = left_size;
// Insert the new right map.
auto const right = SeparateHeapMap{
.paddr = it->second.paddr + left_size,
.size = orig_size - left_size,
.tick = it->second.tick,
.perm = it->second.perm,
.is_resident = it->second.is_resident,
};
m_map_count++;
auto rit = m_mappings.insert_or_assign(it->first + left_size, right);
if (rit.first->second.is_resident) {
m_resident_map_count++;
m_resident_mappings.insert(*rit.first);
} }
// Cache the original values.
auto* const left = std::addressof(*it);
const size_t orig_size = left->size;
// Adjust the left map.
const size_t left_size = offset - left->vaddr;
left->size = left_size;
// Create the new right map.
auto* const right = new SeparateHeapMap{
.vaddr = left->vaddr + left_size,
.paddr = left->paddr + left_size,
.size = orig_size - left_size,
.tick = left->tick,
.perm = left->perm,
.is_resident = left->is_resident,
};
// Insert the new right map.
m_map_count++;
m_mappings.insert(*right);
// If resident, also insert into resident map.
if (right->is_resident) {
m_resident_map_count++;
m_resident_mappings.insert(*right);
} }
} }
HeapTracker::AddrTree::iterator HeapTracker::GetNearestHeapMapLocked(VAddr offset) {
const SeparateHeapMap key{
.vaddr = offset,
};
return m_mappings.find(key);
}
} // namespace Common } // namespace Common

View file

@ -1,55 +1,93 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once
#include <atomic>
#include <mutex> #include <mutex>
#include <set>
#include <shared_mutex> #include <shared_mutex>
#include <ankerl/unordered_dense.h>
#include "common/host_memory.h" #include "common/host_memory.h"
#include "common/intrusive_red_black_tree.h"
namespace Common { namespace Common {
struct SeparateHeapMap { struct SeparateHeapMap {
PAddr paddr{}; //8 Common::IntrusiveRedBlackTreeNode addr_node{};
std::size_t size{}; //8 (16) Common::IntrusiveRedBlackTreeNode tick_node{};
std::size_t tick{}; //8 (24) VAddr vaddr{};
// 4 bits needed, sync with host_memory.h if needed PAddr paddr{};
MemoryPermission perm : 4 = MemoryPermission::Read; size_t size{};
bool is_resident : 1 = false; size_t tick{};
MemoryPermission perm{};
bool is_resident{};
};
struct SeparateHeapMapAddrComparator {
static constexpr int Compare(const SeparateHeapMap& lhs, const SeparateHeapMap& rhs) {
if (lhs.vaddr < rhs.vaddr) {
return -1;
} else if (lhs.vaddr <= (rhs.vaddr + rhs.size - 1)) {
return 0;
} else {
return 1;
}
}
};
struct SeparateHeapMapTickComparator {
static constexpr int Compare(const SeparateHeapMap& lhs, const SeparateHeapMap& rhs) {
if (lhs.tick < rhs.tick) {
return -1;
} else if (lhs.tick > rhs.tick) {
return 1;
} else {
return SeparateHeapMapAddrComparator::Compare(lhs, rhs);
}
}
}; };
static_assert(sizeof(SeparateHeapMap) == 32); //half a cache line! good for coherency
class HeapTracker { class HeapTracker {
public: public:
explicit HeapTracker(Common::HostMemory& buffer); explicit HeapTracker(Common::HostMemory& buffer);
~HeapTracker(); ~HeapTracker();
void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perm, bool is_separate_heap);
void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perm,
bool is_separate_heap);
void Unmap(size_t virtual_offset, size_t size, bool is_separate_heap); void Unmap(size_t virtual_offset, size_t size, bool is_separate_heap);
void Protect(size_t virtual_offset, size_t length, MemoryPermission perm); void Protect(size_t virtual_offset, size_t length, MemoryPermission perm);
inline u8* VirtualBasePointer() noexcept { u8* VirtualBasePointer() {
return m_buffer.VirtualBasePointer(); return m_buffer.VirtualBasePointer();
} }
bool DeferredMapSeparateHeap(u8* fault_address);
bool DeferredMapSeparateHeap(size_t virtual_offset);
private: private:
// TODO: You may want to "fake-map" the first 2GB of 64-bit address space using AddrTreeTraits =
// and dedicate it entirely to a recursive PTE mapping :) Common::IntrusiveRedBlackTreeMemberTraitsDeferredAssert<&SeparateHeapMap::addr_node>;
// However Ankerl is way better than using an RB tree, in all senses using AddrTree = AddrTreeTraits::TreeType<SeparateHeapMapAddrComparator>;
using AddrTree = ankerl::unordered_dense::map<VAddr, SeparateHeapMap>;
AddrTree m_mappings; using TickTreeTraits =
using TicksTree = ankerl::unordered_dense::map<VAddr, SeparateHeapMap>; Common::IntrusiveRedBlackTreeMemberTraitsDeferredAssert<&SeparateHeapMap::tick_node>;
TicksTree m_resident_mappings; using TickTree = TickTreeTraits::TreeType<SeparateHeapMapTickComparator>;
AddrTree m_mappings{};
TickTree m_resident_mappings{};
private: private:
void SplitHeapMap(VAddr offset, size_t size); void SplitHeapMap(VAddr offset, size_t size);
void SplitHeapMapLocked(VAddr offset); void SplitHeapMapLocked(VAddr offset);
AddrTree::iterator GetNearestHeapMapLocked(VAddr offset);
void RebuildSeparateHeapAddressSpace(); void RebuildSeparateHeapAddressSpace();
inline HeapTracker::AddrTree::iterator GetNearestHeapMapLocked(VAddr offset) noexcept {
return m_mappings.find(offset);
}
private: private:
Common::HostMemory& m_buffer; Common::HostMemory& m_buffer;
const s64 m_max_resident_map_count; const s64 m_max_resident_map_count;
std::shared_mutex m_rebuild_lock{}; std::shared_mutex m_rebuild_lock{};
std::mutex m_lock{}; std::mutex m_lock{};
s64 m_map_count{}; s64 m_map_count{};

View file

@ -12,7 +12,7 @@
#include <windows.h> #include <windows.h>
#include "common/dynamic_library.h" #include "common/dynamic_library.h"
#elif defined(__linux__) || defined(__FreeBSD__) || defined(__sun__) // ^^^ Windows ^^^ vvv Linux vvv #elif defined(__linux__) || defined(__FreeBSD__) || defined(__sun__) || defined(__APPLE__) // ^^^ Windows ^^^ vvv POSIX vvv
#ifndef _GNU_SOURCE #ifndef _GNU_SOURCE
#define _GNU_SOURCE #define _GNU_SOURCE
@ -20,10 +20,18 @@
#include <boost/icl/interval_set.hpp> #include <boost/icl/interval_set.hpp>
#include <fcntl.h> #include <fcntl.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/random.h>
#include <unistd.h> #include <unistd.h>
#include "common/scope_exit.h" #include "common/scope_exit.h"
#if defined(__linux__)
#include <sys/random.h>
#elif defined(__APPLE__)
#include <sys/types.h>
#include <sys/random.h>
#include <mach/vm_map.h>
#include <mach/mach.h>
#endif
// FreeBSD // FreeBSD
#ifndef MAP_NORESERVE #ifndef MAP_NORESERVE
#define MAP_NORESERVE 0 #define MAP_NORESERVE 0
@ -32,8 +40,12 @@
#ifndef MAP_ALIGNED_SUPER #ifndef MAP_ALIGNED_SUPER
#define MAP_ALIGNED_SUPER 0 #define MAP_ALIGNED_SUPER 0
#endif #endif
// macOS
#ifndef MAP_ANONYMOUS
#define MAP_ANONYMOUS MAP_ANON
#endif
#endif // ^^^ Linux ^^^ #endif // ^^^ POSIX ^^^
#include <mutex> #include <mutex>
#include <random> #include <random>
@ -372,7 +384,7 @@ private:
std::unordered_map<size_t, size_t> placeholder_host_pointers; ///< Placeholder backing offset std::unordered_map<size_t, size_t> placeholder_host_pointers; ///< Placeholder backing offset
}; };
#elif defined(__linux__) || defined(__FreeBSD__) || defined(__sun__) // ^^^ Windows ^^^ vvv Linux vvv #elif defined(__linux__) || defined(__FreeBSD__) || defined(__sun__) || defined(__APPLE__) // ^^^ Windows ^^^ vvv POSIX vvv
#ifdef ARCHITECTURE_arm64 #ifdef ARCHITECTURE_arm64
@ -417,11 +429,12 @@ static void* ChooseVirtualBase(size_t virtual_size) {
#else #else
static void* ChooseVirtualBase(size_t virtual_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, virtual_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) if (virtual_base != MAP_FAILED)
return virtual_base; return virtual_base;
return mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, #endif
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0); return mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
} }
#endif #endif
@ -488,6 +501,13 @@ public:
#elif defined(__FreeBSD__) && __FreeBSD__ < 13 #elif defined(__FreeBSD__) && __FreeBSD__ < 13
// XXX Drop after FreeBSD 12.* reaches EOL on 2024-06-30 // XXX Drop after FreeBSD 12.* reaches EOL on 2024-06-30
fd = shm_open(SHM_ANON, O_RDWR, 0600); fd = shm_open(SHM_ANON, O_RDWR, 0600);
#elif defined(__APPLE__)
// macOS doesn't have memfd_create, use anonymous temporary file
char template_path[] = "/tmp/eden_mem_XXXXXX";
fd = mkstemp(template_path);
if (fd >= 0) {
unlink(template_path);
}
#else #else
fd = memfd_create("HostMemory", 0); fd = memfd_create("HostMemory", 0);
#endif #endif
@ -644,7 +664,7 @@ private:
FreeRegionManager free_manager{}; FreeRegionManager free_manager{};
}; };
#else // ^^^ Linux ^^^ vvv Generic vvv #else // ^^^ POSIX ^^^ vvv Generic vvv
class HostMemory::Impl { class HostMemory::Impl {
public: public:

View file

@ -15,6 +15,7 @@
#include <span> #include <span>
#include <type_traits> #include <type_traits>
#include <vector> #include <vector>
#include <mutex>
namespace Common { namespace Common {

View file

@ -163,8 +163,9 @@ bool IsFastmemEnabled() {
} }
#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__sun__) #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__sun__)
return false; return false;
#endif #else
return true; return true;
#endif
} }
static bool is_nce_enabled = false; static bool is_nce_enabled = false;

View file

@ -307,10 +307,7 @@ struct Values {
true, true,
#endif #endif
"cpuopt_unsafe_host_mmu", "cpuopt_unsafe_host_mmu",
Category::CpuUnsafe, Category::CpuUnsafe};
Specialization::Default,
true,
true};
SwitchableSetting<bool> cpuopt_unsafe_unfuse_fma{linkage, true, "cpuopt_unsafe_unfuse_fma", SwitchableSetting<bool> cpuopt_unsafe_unfuse_fma{linkage, true, "cpuopt_unsafe_unfuse_fma",
Category::CpuUnsafe}; Category::CpuUnsafe};
SwitchableSetting<bool> cpuopt_unsafe_reduce_fp_error{ SwitchableSetting<bool> cpuopt_unsafe_reduce_fp_error{
@ -554,6 +551,8 @@ struct Values {
3, 3,
#elif defined (ANDROID) #elif defined (ANDROID)
0, 0,
#elif defined (__APPLE__)
0,
#else #else
2, 2,
#endif #endif

View file

@ -7,6 +7,7 @@
#pragma once #pragma once
#include <algorithm>
#include <cstddef> #include <cstddef>
#include <span> #include <span>
#include <string> #include <string>

View file

@ -3,9 +3,47 @@
#ifdef __linux__ #ifdef __linux__
//#include "common/signal_chain.h" #include "common/signal_chain.h"
#include "core/arm/dynarmic/arm_dynarmic.h" #include "core/arm/dynarmic/arm_dynarmic.h"
//#include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_process.h"
//#include "core/memory.h" #include "core/memory.h"
namespace Core {
namespace {
thread_local Core::Memory::Memory* g_current_memory{};
std::once_flag g_registered{};
struct sigaction g_old_segv {};
void HandleSigSegv(int sig, siginfo_t* info, void* ctx) {
if (g_current_memory && g_current_memory->InvalidateSeparateHeap(info->si_addr)) {
return;
}
return g_old_segv.sa_sigaction(sig, info, ctx);
}
} // namespace
ScopedJitExecution::ScopedJitExecution(Kernel::KProcess* process) {
g_current_memory = std::addressof(process->GetMemory());
}
ScopedJitExecution::~ScopedJitExecution() {
g_current_memory = nullptr;
}
void ScopedJitExecution::RegisterHandler() {
std::call_once(g_registered, [] {
struct sigaction sa {};
sa.sa_sigaction = &HandleSigSegv;
sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
Common::SigAction(SIGSEGV, std::addressof(sa), std::addressof(g_old_segv));
});
}
} // namespace Core
#endif #endif

View file

@ -26,4 +26,24 @@ constexpr HaltReason TranslateHaltReason(Dynarmic::HaltReason hr) {
return static_cast<HaltReason>(hr); return static_cast<HaltReason>(hr);
} }
#ifdef __linux__
class ScopedJitExecution {
public:
explicit ScopedJitExecution(Kernel::KProcess* process);
~ScopedJitExecution();
static void RegisterHandler();
};
#else
class ScopedJitExecution {
public:
explicit ScopedJitExecution(Kernel::KProcess* process) {}
~ScopedJitExecution() {}
static void RegisterHandler() {}
};
#endif
} // namespace Core } // namespace Core

View file

@ -343,11 +343,15 @@ bool ArmDynarmic32::IsInThumbMode() const {
} }
HaltReason ArmDynarmic32::RunThread(Kernel::KThread* thread) { HaltReason ArmDynarmic32::RunThread(Kernel::KThread* thread) {
ScopedJitExecution sj(thread->GetOwnerProcess());
m_jit->ClearExclusiveState(); m_jit->ClearExclusiveState();
return TranslateHaltReason(m_jit->Run()); return TranslateHaltReason(m_jit->Run());
} }
HaltReason ArmDynarmic32::StepThread(Kernel::KThread* thread) { HaltReason ArmDynarmic32::StepThread(Kernel::KThread* thread) {
ScopedJitExecution sj(thread->GetOwnerProcess());
m_jit->ClearExclusiveState(); m_jit->ClearExclusiveState();
return TranslateHaltReason(m_jit->Step()); return TranslateHaltReason(m_jit->Step());
} }
@ -389,6 +393,7 @@ ArmDynarmic32::ArmDynarmic32(System& system, bool uses_wall_clock, Kernel::KProc
m_cp15(std::make_shared<DynarmicCP15>(*this)), m_core_index{core_index} { m_cp15(std::make_shared<DynarmicCP15>(*this)), m_core_index{core_index} {
auto& page_table_impl = process->GetPageTable().GetBasePageTable().GetImpl(); auto& page_table_impl = process->GetPageTable().GetBasePageTable().GetImpl();
m_jit = MakeJit(&page_table_impl); m_jit = MakeJit(&page_table_impl);
ScopedJitExecution::RegisterHandler();
} }
ArmDynarmic32::~ArmDynarmic32() = default; ArmDynarmic32::~ArmDynarmic32() = default;

View file

@ -374,11 +374,15 @@ std::shared_ptr<Dynarmic::A64::Jit> ArmDynarmic64::MakeJit(Common::PageTable* pa
} }
HaltReason ArmDynarmic64::RunThread(Kernel::KThread* thread) { HaltReason ArmDynarmic64::RunThread(Kernel::KThread* thread) {
ScopedJitExecution sj(thread->GetOwnerProcess());
m_jit->ClearExclusiveState(); m_jit->ClearExclusiveState();
return TranslateHaltReason(m_jit->Run()); return TranslateHaltReason(m_jit->Run());
} }
HaltReason ArmDynarmic64::StepThread(Kernel::KThread* thread) { HaltReason ArmDynarmic64::StepThread(Kernel::KThread* thread) {
ScopedJitExecution sj(thread->GetOwnerProcess());
m_jit->ClearExclusiveState(); m_jit->ClearExclusiveState();
return TranslateHaltReason(m_jit->Step()); return TranslateHaltReason(m_jit->Step());
} }
@ -418,6 +422,7 @@ ArmDynarmic64::ArmDynarmic64(System& system, bool uses_wall_clock, Kernel::KProc
auto& page_table = process->GetPageTable().GetBasePageTable(); auto& page_table = process->GetPageTable().GetBasePageTable();
auto& page_table_impl = page_table.GetImpl(); auto& page_table_impl = page_table.GetImpl();
m_jit = MakeJit(&page_table_impl, page_table.GetAddressSpaceWidth()); m_jit = MakeJit(&page_table_impl, page_table.GetAddressSpaceWidth());
ScopedJitExecution::RegisterHandler();
} }
ArmDynarmic64::~ArmDynarmic64() = default; ArmDynarmic64::~ArmDynarmic64() = default;

View file

@ -1266,6 +1266,10 @@ void KProcess::InitializeInterfaces() {
#ifdef HAS_NCE #ifdef HAS_NCE
if (this->IsApplication() && Settings::IsNceEnabled()) { if (this->IsApplication() && Settings::IsNceEnabled()) {
// Register the scoped JIT handler before creating any NCE instances
// so that its signal handler will appear first in the signal chain.
Core::ScopedJitExecution::RegisterHandler();
for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
m_arm_interfaces[i] = std::make_unique<Core::ArmNce>(m_kernel.System(), true, i); m_arm_interfaces[i] = std::make_unique<Core::ArmNce>(m_kernel.System(), true, i);
} }

View file

@ -219,28 +219,55 @@ NvResult nvhost_gpu::AllocGPFIFOEx2(IoctlAllocGpfifoEx& params, DeviceFD fd) {
return NvResult::Success; return NvResult::Success;
} }
s32_le nvhost_gpu::GetObjectContextClassNumberIndex(CtxClasses class_number) {
constexpr s32_le invalid_class_number_index = -1;
switch (class_number) {
case CtxClasses::Ctx2D: return 0;
case CtxClasses::Ctx3D: return 1;
case CtxClasses::CtxCompute: return 2;
case CtxClasses::CtxKepler: return 3;
case CtxClasses::CtxDMA: return 4;
case CtxClasses::CtxChannelGPFIFO: return 5;
default: return invalid_class_number_index;
}
}
NvResult nvhost_gpu::AllocateObjectContext(IoctlAllocObjCtx& params) { NvResult nvhost_gpu::AllocateObjectContext(IoctlAllocObjCtx& params) {
LOG_DEBUG(Service_NVDRV, "called, class_num={:X}, flags={:X}, obj_id={:X}", params.class_num, LOG_DEBUG(Service_NVDRV, "called, class_num={:#X}, flags={:#X}, obj_id={:#X}", params.class_num,
params.flags, params.obj_id); params.flags, params.obj_id);
if (!channel_state->initialized) { if (!channel_state || !channel_state->initialized) {
LOG_CRITICAL(Service_NVDRV, "No address space bound to allocate a object context!"); LOG_CRITICAL(Service_NVDRV, "No address space bound to allocate a object context!");
return NvResult::NotInitialized; return NvResult::NotInitialized;
} }
switch (static_cast<CtxClasses>(params.class_num)) { std::scoped_lock lk(channel_mutex);
case CtxClasses::Ctx2D:
case CtxClasses::Ctx3D: if (params.flags) {
case CtxClasses::CtxCompute: LOG_WARNING(Service_NVDRV, "non-zero flags={:#X} for class={:#X}", params.flags,
case CtxClasses::CtxKepler: params.class_num);
case CtxClasses::CtxDMA:
case CtxClasses::CtxChannelGPFIFO: constexpr u32 allowed_mask{};
ctxObj_params.push_back(params); params.flags = allowed_mask;
return NvResult::Success; }
default:
LOG_ERROR(Service_NVDRV, "Invalid class number for object context: {:X}", params.class_num); s32_le ctx_class_number_index =
GetObjectContextClassNumberIndex(static_cast<CtxClasses>(params.class_num));
if (ctx_class_number_index < 0) {
LOG_ERROR(Service_NVDRV, "Invalid class number for object context: {:#X}",
params.class_num);
return NvResult::BadParameter; return NvResult::BadParameter;
} }
if (ctxObjs[ctx_class_number_index].has_value()) {
LOG_ERROR(Service_NVDRV, "Object context for class {:#X} already allocated on this channel",
params.class_num);
return NvResult::AlreadyAllocated;
}
ctxObjs[ctx_class_number_index] = params;
return NvResult::Success;
} }
static boost::container::small_vector<Tegra::CommandHeader, 512> BuildWaitCommandList( static boost::container::small_vector<Tegra::CommandHeader, 512> BuildWaitCommandList(

View file

@ -172,7 +172,7 @@ private:
s32_le nvmap_fd{}; s32_le nvmap_fd{};
u64_le user_data{}; u64_le user_data{};
IoctlZCullBind zcull_params{}; IoctlZCullBind zcull_params{};
std::vector<IoctlAllocObjCtx> ctxObj_params{}; std::array<std::optional<IoctlAllocObjCtx>, 6> ctxObjs{};
u32_le channel_priority{}; u32_le channel_priority{};
u32_le channel_timeslice{}; u32_le channel_timeslice{};
@ -184,9 +184,12 @@ private:
NvResult SetChannelPriority(IoctlChannelSetPriority& params); NvResult SetChannelPriority(IoctlChannelSetPriority& params);
NvResult AllocGPFIFOEx(IoctlAllocGpfifoEx& params, DeviceFD fd); NvResult AllocGPFIFOEx(IoctlAllocGpfifoEx& params, DeviceFD fd);
NvResult AllocGPFIFOEx2(IoctlAllocGpfifoEx& params, DeviceFD fd); NvResult AllocGPFIFOEx2(IoctlAllocGpfifoEx& params, DeviceFD fd);
s32_le GetObjectContextClassNumberIndex(CtxClasses class_number);
NvResult AllocateObjectContext(IoctlAllocObjCtx& params); NvResult AllocateObjectContext(IoctlAllocObjCtx& params);
NvResult SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, Tegra::CommandList&& entries); NvResult SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, Tegra::CommandList&& entries);
NvResult SubmitGPFIFOBase1(IoctlSubmitGpfifo& params, NvResult SubmitGPFIFOBase1(IoctlSubmitGpfifo& params,
std::span<Tegra::CommandListHeader> commands, bool kickoff = false); std::span<Tegra::CommandListHeader> commands, bool kickoff = false);
NvResult SubmitGPFIFOBase2(IoctlSubmitGpfifo& params, NvResult SubmitGPFIFOBase2(IoctlSubmitGpfifo& params,

View file

@ -61,7 +61,8 @@ struct Memory::Impl {
} }
#ifdef __linux__ #ifdef __linux__
buffer.emplace(system.DeviceMemory().buffer); heap_tracker.emplace(system.DeviceMemory().buffer);
buffer = std::addressof(*heap_tracker);
#else #else
buffer = std::addressof(system.DeviceMemory().buffer); buffer = std::addressof(system.DeviceMemory().buffer);
#endif #endif
@ -1023,8 +1024,9 @@ struct Memory::Impl {
std::span<Core::GPUDirtyMemoryManager> gpu_dirty_managers; std::span<Core::GPUDirtyMemoryManager> gpu_dirty_managers;
std::mutex sys_core_guard; std::mutex sys_core_guard;
std::optional<Common::HeapTracker> heap_tracker;
#ifdef __linux__ #ifdef __linux__
std::optional<Common::HeapTracker> buffer; Common::HeapTracker* buffer{};
#else #else
Common::HostMemory* buffer{}; Common::HostMemory* buffer{};
#endif #endif
@ -1228,7 +1230,22 @@ bool Memory::InvalidateNCE(Common::ProcessAddress vaddr, size_t size) {
if (rasterizer) { if (rasterizer) {
impl->InvalidateGPUMemory(ptr, size); impl->InvalidateGPUMemory(ptr, size);
} }
#ifdef __linux__
if (!rasterizer && mapped) {
impl->buffer->DeferredMapSeparateHeap(GetInteger(vaddr));
}
#endif
return mapped && ptr != nullptr; return mapped && ptr != nullptr;
} }
bool Memory::InvalidateSeparateHeap(void* fault_address) {
#ifdef __linux__
return impl->buffer->DeferredMapSeparateHeap(static_cast<u8*>(fault_address));
#else
return false;
#endif
}
} // namespace Core::Memory } // namespace Core::Memory

View file

@ -487,8 +487,13 @@ public:
* marked as debug or non-debug. * marked as debug or non-debug.
*/ */
void MarkRegionDebug(Common::ProcessAddress vaddr, u64 size, bool debug); void MarkRegionDebug(Common::ProcessAddress vaddr, u64 size, bool debug);
void SetGPUDirtyManagers(std::span<Core::GPUDirtyMemoryManager> managers); void SetGPUDirtyManagers(std::span<Core::GPUDirtyMemoryManager> managers);
bool InvalidateNCE(Common::ProcessAddress vaddr, size_t size); bool InvalidateNCE(Common::ProcessAddress vaddr, size_t size);
bool InvalidateSeparateHeap(void* fault_address);
private: private:
Core::System& system; Core::System& system;

View file

@ -1,11 +1,11 @@
# Explicitly include CPMUtil here since we have a separate cpmfile for dynarmic
set(CPMUTIL_JSON_FILE ${CMAKE_CURRENT_SOURCE_DIR}/cpmfile.json)
include(CPMUtil) include(CPMUtil)
# Always build externals as static libraries, even when dynarmic is built as shared # Always build externals as static libraries, even when dynarmic is built as shared
if (BUILD_SHARED_LIBS) set(BUILD_SHARED_LIBS OFF)
set(BUILD_SHARED_LIBS OFF) set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(CMAKE_POSITION_INDEPENDENT_CODE ON) set_property(DIRECTORY PROPERTY EXCLUDE_FROM_ALL ON)
set_property(DIRECTORY PROPERTY EXCLUDE_FROM_ALL ON)
endif()
# Allow options shadowing with normal variables when subproject use old cmake policy # Allow options shadowing with normal variables when subproject use old cmake policy
set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
@ -22,42 +22,32 @@ set(BUILD_TESTING OFF)
if ("riscv" IN_LIST ARCHITECTURE) if ("riscv" IN_LIST ARCHITECTURE)
add_subdirectory(biscuit) add_subdirectory(biscuit)
AddPackage( AddJsonPackage(
NAME biscuit NAME biscuit
VERSION 0.9.1
REPO "lioncash/biscuit"
SHA 76b0be8dae
HASH 47d55ed02d032d6cf3dc107c6c0a9aea686d5f25aefb81d1af91db027b6815bd5add1755505e19d76625feeb17aa2db6cd1668fe0dad2e6a411519bde6ca4489
BUNDLED_PACKAGE ${DYNARMIC_USE_BUNDLED_EXTERNALS} BUNDLED_PACKAGE ${DYNARMIC_USE_BUNDLED_EXTERNALS}
) )
endif() endif()
# catch # catch
# TODO(crueter): dedup # if (NOT TARGET Catch2::Catch2WithMain)
if (NOT TARGET Catch2::Catch2WithMain) # if (DYNARMIC_TESTS)
if (DYNARMIC_TESTS) # find_package(Catch2 3.0.1 REQUIRED)
find_package(Catch2 3.0.1 REQUIRED) # endif()
endif() # endif()
endif()
# fmt # fmt
if (NOT TARGET fmt::fmt) # if (NOT TARGET fmt::fmt)
# fmtlib formatting library # # fmtlib formatting library
set(FMT_INSTALL ON) # set(FMT_INSTALL ON)
add_subdirectory(fmt) # add_subdirectory(fmt)
endif() # endif()
# mcl # mcl
AddPackage( AddJsonPackage(
NAME mcl NAME mcl
VERSION 0.1.12 BUNDLED_PACKAGE ${DYNARMIC_USE_BUNDLED_EXTERNALS}
REPO "azahar-emu/mcl"
SHA 7b08d83418
HASH f943bac39c1879986decad7a442ff4288eaeca4a2907684c7914e115a55ecc43c2782ded85c0835763fe04e40d5c82220ce864423e489e648e408a84f54dc4f3
OPTIONS
"MCL_INSTALL OFF"
) )
# oaknut # oaknut
@ -72,15 +62,10 @@ AddPackage(
# unordered_dense # unordered_dense
AddPackage( # AddJsonPackage(
NAME unordered_dense # NAME unordered-dense
REPO "Lizzie841/unordered_dense" # BUNDLED_PACKAGE ${DYNARMIC_USE_BUNDLED_EXTERNALS}
SHA e59d30b7b1 # )
HASH 71eff7bd9ba4b9226967bacd56a8ff000946f8813167cb5664bb01e96fb79e4e220684d824fe9c59c4d1cc98c606f13aff05b7940a1ed8ab3c95d6974ee34fa0
FIND_PACKAGE_ARGUMENTS "CONFIG"
OPTIONS
"UNORDERED_DENSE_INSTALL OFF"
)
# xbyak # xbyak
# uncomment if in an independent repo # uncomment if in an independent repo
@ -95,31 +80,12 @@ AddPackage(
# TODO(crueter): maybe it's just Gentoo but zydis system package really sucks # TODO(crueter): maybe it's just Gentoo but zydis system package really sucks
if ("x86_64" IN_LIST ARCHITECTURE) if ("x86_64" IN_LIST ARCHITECTURE)
set(CMAKE_DISABLE_FIND_PACKAGE_Doxygen ON)
# TODO(crueter): system zycore doesn't work with zydis # TODO(crueter): system zycore doesn't work with zydis
AddPackage( AddJsonPackage(zycore)
NAME Zycore
REPO "zyantific/zycore-c"
SHA 75a36c45ae
HASH 15aa399f39713e042c4345bc3175c82f14dca849fde2a21d4f591f62c43e227b70d868d8bb86beb5f4eb68b1d6bd3792cdd638acf89009e787e3d10ee7401924
OPTIONS
"CMAKE_DISABLE_FIND_PACKAGE_Doxygen ON"
EXCLUDE_FROM_ALL ON
SYSTEM_PACKAGE OFF
)
AddPackage( AddJsonPackage(
NAME Zydis NAME zydis
VERSION 4 BUNDLED_PACKAGE ${DYNARMIC_USE_BUNDLED_EXTERNALS}
REPO "zyantific/zydis"
SHA c2d2bab025
HASH 7b48f213ff7aab2926f8c9c65195959143bebbfb2b9a25051ffd8b8b0f1baf1670d9739781de674577d955925f91ac89376e16b476a03828c84e2fd765d45020
FIND_PACKAGE_ARGUMENTS "CONFIG"
OPTIONS
"ZYDIS_BUILD_TOOLS OFF"
"ZYDIS_BUILD_EXAMPLES OFF"
"ZYDIS_BUILD_DOXYGEN OFF"
"ZYAN_SYSTEM_ZYCORE ON"
"CMAKE_DISABLE_FIND_PACKAGE_Doxygen ON"
EXCLUDE_FROM_ALL ON
) )
endif() endif()

37
src/dynarmic/externals/cpmfile.json vendored Normal file
View file

@ -0,0 +1,37 @@
{
"biscuit": {
"version": "0.9.1",
"repo": "lioncash/biscuit",
"sha": "76b0be8dae",
"hash": "47d55ed02d032d6cf3dc107c6c0a9aea686d5f25aefb81d1af91db027b6815bd5add1755505e19d76625feeb17aa2db6cd1668fe0dad2e6a411519bde6ca4489"
},
"mcl": {
"version": "0.1.12",
"repo": "azahar-emu/mcl",
"sha": "7b08d83418",
"hash": "f943bac39c1879986decad7a442ff4288eaeca4a2907684c7914e115a55ecc43c2782ded85c0835763fe04e40d5c82220ce864423e489e648e408a84f54dc4f3",
"options": [
"MCL_INSTALL OFF"
]
},
"zycore": {
"package": "Zycore",
"repo": "zyantific/zycore-c",
"sha": "75a36c45ae",
"hash": "15aa399f39713e042c4345bc3175c82f14dca849fde2a21d4f591f62c43e227b70d868d8bb86beb5f4eb68b1d6bd3792cdd638acf89009e787e3d10ee7401924",
"bundled": true
},
"zydis": {
"package": "Zydis",
"version": "4",
"repo": "zyantific/zydis",
"sha": "c2d2bab025",
"hash": "7b48f213ff7aab2926f8c9c65195959143bebbfb2b9a25051ffd8b8b0f1baf1670d9739781de674577d955925f91ac89376e16b476a03828c84e2fd765d45020",
"options": [
"ZYDIS_BUILD_TOOLS OFF",
"ZYDIS_BUILD_EXAMPLES OFF",
"ZYDIS_BUILD_DOXYGEN OFF",
"ZYAN_SYSTEM_ZYCORE ON"
]
}
}

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project. /* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage * Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD * SPDX-License-Identifier: 0BSD
@ -19,6 +22,16 @@
namespace Dynarmic::Common { namespace Dynarmic::Common {
// prevents this function from printing 56,000 character warning messages
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wno-stack-usage"
#endif
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wno-stack-usage"
#endif
template<typename Function, typename... Values> template<typename Function, typename... Values>
inline auto GenerateLookupTableFromList(Function f, mcl::mp::list<Values...>) { inline auto GenerateLookupTableFromList(Function f, mcl::mp::list<Values...>) {
#ifdef _MSC_VER #ifdef _MSC_VER
@ -34,4 +47,11 @@ inline auto GenerateLookupTableFromList(Function f, mcl::mp::list<Values...>) {
return MapT(pair_array.begin(), pair_array.end()); return MapT(pair_array.begin(), pair_array.end());
} }
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
#ifdef __clang__
#pragma clang diagnostic pop
#endif
} // namespace Dynarmic::Common } // namespace Dynarmic::Common

View file

@ -246,7 +246,7 @@ add_library(shader_recompiler STATIC
) )
target_link_libraries(shader_recompiler PUBLIC common fmt::fmt sirit SPIRV-Tools-opt SPIRV-Tools SPIRV-Tools-link) target_link_libraries(shader_recompiler PUBLIC common fmt::fmt sirit SPIRV-Tools::SPIRV-Tools)
if (MSVC) if (MSVC)
target_compile_options(shader_recompiler PRIVATE target_compile_options(shader_recompiler PRIVATE

View file

@ -332,7 +332,8 @@ target_link_options(video_core PRIVATE ${FFmpeg_LDFLAGS})
add_dependencies(video_core host_shaders) add_dependencies(video_core host_shaders)
target_include_directories(video_core PRIVATE ${HOST_SHADERS_INCLUDE}) target_include_directories(video_core PRIVATE ${HOST_SHADERS_INCLUDE})
target_link_libraries(video_core PRIVATE sirit Vulkan::Headers Vulkan::UtilityHeaders GPUOpen::VulkanMemoryAllocator) target_link_libraries(video_core PRIVATE sirit Vulkan::Headers Vulkan::UtilityHeaders)
target_link_libraries(video_core PUBLIC GPUOpen::VulkanMemoryAllocator)
if (ENABLE_NSIGHT_AFTERMATH) if (ENABLE_NSIGHT_AFTERMATH)
if (NOT DEFINED ENV{NSIGHT_AFTERMATH_SDK}) if (NOT DEFINED ENV{NSIGHT_AFTERMATH_SDK})

View file

@ -102,13 +102,16 @@ constexpr VkPipelineVertexInputStateCreateInfo PIPELINE_VERTEX_INPUT_STATE_CREAT
.vertexAttributeDescriptionCount = 0, .vertexAttributeDescriptionCount = 0,
.pVertexAttributeDescriptions = nullptr, .pVertexAttributeDescriptions = nullptr,
}; };
constexpr VkPipelineInputAssemblyStateCreateInfo PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO{
VkPipelineInputAssemblyStateCreateInfo GetPipelineInputAssemblyStateCreateInfo(const Device& device) {
return VkPipelineInputAssemblyStateCreateInfo{
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.pNext = nullptr, .pNext = nullptr,
.flags = 0, .flags = 0,
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
.primitiveRestartEnable = VK_FALSE, .primitiveRestartEnable = device.IsMoltenVK() ? VK_TRUE : VK_FALSE,
}; };
}
constexpr VkPipelineViewportStateCreateInfo PIPELINE_VIEWPORT_STATE_CREATE_INFO{ constexpr VkPipelineViewportStateCreateInfo PIPELINE_VIEWPORT_STATE_CREATE_INFO{
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.pNext = nullptr, .pNext = nullptr,
@ -802,6 +805,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceColorPipeline(const BlitImagePipelineKe
.pAttachments = &blend_attachment, .pAttachments = &blend_attachment,
.blendConstants = {0.0f, 0.0f, 0.0f, 0.0f}, .blendConstants = {0.0f, 0.0f, 0.0f, 0.0f},
}; };
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
blit_color_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({ blit_color_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = nullptr, .pNext = nullptr,
@ -809,7 +813,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceColorPipeline(const BlitImagePipelineKe
.stageCount = static_cast<u32>(stages.size()), .stageCount = static_cast<u32>(stages.size()),
.pStages = stages.data(), .pStages = stages.data(),
.pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, .pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.pInputAssemblyState = &PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, .pInputAssemblyState = &input_assembly_ci,
.pTessellationState = nullptr, .pTessellationState = nullptr,
.pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO, .pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO, .pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
@ -833,6 +837,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceDepthStencilPipeline(const BlitImagePip
} }
blit_depth_stencil_keys.push_back(key); blit_depth_stencil_keys.push_back(key);
const std::array stages = MakeStages(*full_screen_vert, *blit_depth_stencil_frag); const std::array stages = MakeStages(*full_screen_vert, *blit_depth_stencil_frag);
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
blit_depth_stencil_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({ blit_depth_stencil_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = nullptr, .pNext = nullptr,
@ -840,7 +845,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceDepthStencilPipeline(const BlitImagePip
.stageCount = static_cast<u32>(stages.size()), .stageCount = static_cast<u32>(stages.size()),
.pStages = stages.data(), .pStages = stages.data(),
.pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, .pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.pInputAssemblyState = &PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, .pInputAssemblyState = &input_assembly_ci,
.pTessellationState = nullptr, .pTessellationState = nullptr,
.pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO, .pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO, .pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
@ -885,6 +890,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearColorPipeline(const BlitImagePipel
.pAttachments = &color_blend_attachment_state, .pAttachments = &color_blend_attachment_state,
.blendConstants = {0.0f, 0.0f, 0.0f, 0.0f}, .blendConstants = {0.0f, 0.0f, 0.0f, 0.0f},
}; };
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
clear_color_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({ clear_color_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = nullptr, .pNext = nullptr,
@ -892,7 +898,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearColorPipeline(const BlitImagePipel
.stageCount = static_cast<u32>(stages.size()), .stageCount = static_cast<u32>(stages.size()),
.pStages = stages.data(), .pStages = stages.data(),
.pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, .pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.pInputAssemblyState = &PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, .pInputAssemblyState = &input_assembly_ci,
.pTessellationState = nullptr, .pTessellationState = nullptr,
.pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO, .pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO, .pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
@ -940,6 +946,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearStencilPipeline(
.minDepthBounds = 0.0f, .minDepthBounds = 0.0f,
.maxDepthBounds = 0.0f, .maxDepthBounds = 0.0f,
}; };
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
clear_stencil_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({ clear_stencil_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = nullptr, .pNext = nullptr,
@ -947,7 +954,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearStencilPipeline(
.stageCount = static_cast<u32>(stages.size()), .stageCount = static_cast<u32>(stages.size()),
.pStages = stages.data(), .pStages = stages.data(),
.pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, .pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.pInputAssemblyState = &PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, .pInputAssemblyState = &input_assembly_ci,
.pTessellationState = nullptr, .pTessellationState = nullptr,
.pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO, .pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO, .pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
@ -970,6 +977,7 @@ void BlitImageHelper::ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRend
} }
VkShaderModule frag_shader = *convert_float_to_depth_frag; VkShaderModule frag_shader = *convert_float_to_depth_frag;
const std::array stages = MakeStages(*full_screen_vert, frag_shader); const std::array stages = MakeStages(*full_screen_vert, frag_shader);
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
pipeline = device.GetLogical().CreateGraphicsPipeline({ pipeline = device.GetLogical().CreateGraphicsPipeline({
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = nullptr, .pNext = nullptr,
@ -977,7 +985,7 @@ void BlitImageHelper::ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRend
.stageCount = static_cast<u32>(stages.size()), .stageCount = static_cast<u32>(stages.size()),
.pStages = stages.data(), .pStages = stages.data(),
.pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, .pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.pInputAssemblyState = &PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, .pInputAssemblyState = &input_assembly_ci,
.pTessellationState = nullptr, .pTessellationState = nullptr,
.pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO, .pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO, .pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
@ -999,6 +1007,7 @@ void BlitImageHelper::ConvertColorToDepthPipeline(vk::Pipeline& pipeline, VkRend
} }
VkShaderModule frag_shader = *convert_depth_to_float_frag; VkShaderModule frag_shader = *convert_depth_to_float_frag;
const std::array stages = MakeStages(*full_screen_vert, frag_shader); const std::array stages = MakeStages(*full_screen_vert, frag_shader);
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
pipeline = device.GetLogical().CreateGraphicsPipeline({ pipeline = device.GetLogical().CreateGraphicsPipeline({
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = nullptr, .pNext = nullptr,
@ -1006,7 +1015,7 @@ void BlitImageHelper::ConvertColorToDepthPipeline(vk::Pipeline& pipeline, VkRend
.stageCount = static_cast<u32>(stages.size()), .stageCount = static_cast<u32>(stages.size()),
.pStages = stages.data(), .pStages = stages.data(),
.pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, .pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.pInputAssemblyState = &PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, .pInputAssemblyState = &input_assembly_ci,
.pTessellationState = nullptr, .pTessellationState = nullptr,
.pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO, .pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO, .pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
@ -1029,6 +1038,7 @@ void BlitImageHelper::ConvertPipelineEx(vk::Pipeline& pipeline, VkRenderPass ren
return; return;
} }
const std::array stages = MakeStages(*full_screen_vert, *module); const std::array stages = MakeStages(*full_screen_vert, *module);
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
pipeline = device.GetLogical().CreateGraphicsPipeline({ pipeline = device.GetLogical().CreateGraphicsPipeline({
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = nullptr, .pNext = nullptr,
@ -1036,7 +1046,7 @@ void BlitImageHelper::ConvertPipelineEx(vk::Pipeline& pipeline, VkRenderPass ren
.stageCount = static_cast<u32>(stages.size()), .stageCount = static_cast<u32>(stages.size()),
.pStages = stages.data(), .pStages = stages.data(),
.pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, .pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.pInputAssemblyState = &PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, .pInputAssemblyState = &input_assembly_ci,
.pTessellationState = nullptr, .pTessellationState = nullptr,
.pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO, .pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO, .pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
@ -1070,6 +1080,7 @@ void BlitImageHelper::ConvertPipeline(vk::Pipeline& pipeline, VkRenderPass rende
VkShaderModule frag_shader = VkShaderModule frag_shader =
is_target_depth ? *convert_float_to_depth_frag : *convert_depth_to_float_frag; is_target_depth ? *convert_float_to_depth_frag : *convert_depth_to_float_frag;
const std::array stages = MakeStages(*full_screen_vert, frag_shader); const std::array stages = MakeStages(*full_screen_vert, frag_shader);
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
pipeline = device.GetLogical().CreateGraphicsPipeline({ pipeline = device.GetLogical().CreateGraphicsPipeline({
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = nullptr, .pNext = nullptr,
@ -1077,7 +1088,7 @@ void BlitImageHelper::ConvertPipeline(vk::Pipeline& pipeline, VkRenderPass rende
.stageCount = static_cast<u32>(stages.size()), .stageCount = static_cast<u32>(stages.size()),
.pStages = stages.data(), .pStages = stages.data(),
.pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, .pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.pInputAssemblyState = &PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, .pInputAssemblyState = &input_assembly_ci,
.pTessellationState = nullptr, .pTessellationState = nullptr,
.pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO, .pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO, .pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,

View file

@ -400,12 +400,12 @@ static vk::Pipeline CreateWrappedPipelineImpl(
.pVertexAttributeDescriptions = nullptr, .pVertexAttributeDescriptions = nullptr,
}; };
constexpr VkPipelineInputAssemblyStateCreateInfo input_assembly_ci{ const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci{
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.pNext = nullptr, .pNext = nullptr,
.flags = 0, .flags = 0,
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
.primitiveRestartEnable = VK_FALSE, .primitiveRestartEnable = device.IsMoltenVK() ? VK_TRUE : VK_FALSE,
}; };
constexpr VkPipelineViewportStateCreateInfo viewport_state_ci{ constexpr VkPipelineViewportStateCreateInfo viewport_state_ci{

View file

@ -635,14 +635,16 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
.flags = 0, .flags = 0,
.topology = input_assembly_topology, .topology = input_assembly_topology,
.primitiveRestartEnable = .primitiveRestartEnable =
dynamic.primitive_restart_enable != 0 && // MoltenVK/Metal always has primitive restart enabled and cannot disable it
device.IsMoltenVK() ? VK_TRUE :
(dynamic.primitive_restart_enable != 0 &&
((input_assembly_topology != VK_PRIMITIVE_TOPOLOGY_PATCH_LIST && ((input_assembly_topology != VK_PRIMITIVE_TOPOLOGY_PATCH_LIST &&
device.IsTopologyListPrimitiveRestartSupported()) || device.IsTopologyListPrimitiveRestartSupported()) ||
SupportsPrimitiveRestart(input_assembly_topology) || SupportsPrimitiveRestart(input_assembly_topology) ||
(input_assembly_topology == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST && (input_assembly_topology == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST &&
device.IsPatchListPrimitiveRestartSupported())) device.IsPatchListPrimitiveRestartSupported()))
? VK_TRUE ? VK_TRUE
: VK_FALSE, : VK_FALSE),
}; };
const VkPipelineTessellationStateCreateInfo tessellation_ci{ const VkPipelineTessellationStateCreateInfo tessellation_ci{
.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO,

View file

@ -1,3 +1,5 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -8,4 +10,4 @@
#define VMA_STATIC_VULKAN_FUNCTIONS 0 #define VMA_STATIC_VULKAN_FUNCTIONS 0
#define VMA_DYNAMIC_VULKAN_FUNCTIONS 1 #define VMA_DYNAMIC_VULKAN_FUNCTIONS 1
#include <vk_mem_alloc.h> #include "vk_mem_alloc.h"

View file

@ -725,6 +725,11 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
dynamic_state3_enables = true; dynamic_state3_enables = true;
} }
if (is_mvk && Settings::values.dyna_state.GetValue() != 0) {
LOG_WARNING(Render_Vulkan, "MoltenVK detected: Forcing dynamic state to 0 to prevent black screen issues");
Settings::values.dyna_state.SetValue(0);
}
if (Settings::values.dyna_state.GetValue() == 0) { if (Settings::values.dyna_state.GetValue() == 0) {
must_emulate_scaled_formats = true; must_emulate_scaled_formats = true;
LOG_INFO(Render_Vulkan, "Dynamic state is disabled (dyna_state = 0), forcing scaled format emulation ON"); LOG_INFO(Render_Vulkan, "Dynamic state is disabled (dyna_state = 0), forcing scaled format emulation ON");
@ -753,17 +758,23 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
functions.vkGetInstanceProcAddr = dld.vkGetInstanceProcAddr; functions.vkGetInstanceProcAddr = dld.vkGetInstanceProcAddr;
functions.vkGetDeviceProcAddr = dld.vkGetDeviceProcAddr; functions.vkGetDeviceProcAddr = dld.vkGetDeviceProcAddr;
const VmaAllocatorCreateInfo allocator_info = { VmaAllocatorCreateFlags flags = VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT;
.flags = VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT, if (extensions.memory_budget) {
flags |= VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT;
}
const VmaAllocatorCreateInfo allocator_info{
.flags = flags,
.physicalDevice = physical, .physicalDevice = physical,
.device = *logical, .device = *logical,
.preferredLargeHeapBlockSize = 0, .preferredLargeHeapBlockSize = is_integrated
? (64u * 1024u * 1024u)
: (256u * 1024u * 1024u),
.pAllocationCallbacks = nullptr, .pAllocationCallbacks = nullptr,
.pDeviceMemoryCallbacks = nullptr, .pDeviceMemoryCallbacks = nullptr,
.pHeapSizeLimit = nullptr, .pHeapSizeLimit = nullptr,
.pVulkanFunctions = &functions, .pVulkanFunctions = &functions,
.instance = instance, .instance = instance,
.vulkanApiVersion = VK_API_VERSION_1_1, .vulkanApiVersion = ApiVersion(),
.pTypeExternalMemoryHandleTypes = nullptr, .pTypeExternalMemoryHandleTypes = nullptr,
}; };
@ -1090,8 +1101,15 @@ bool Device::GetSuitability(bool requires_swapchain) {
// Some features are mandatory. Check those. // Some features are mandatory. Check those.
#define CHECK_FEATURE(feature, name) \ #define CHECK_FEATURE(feature, name) \
if (!features.feature.name) { \ if (!features.feature.name) { \
if (IsMoltenVK() && (strcmp(#name, "geometryShader") == 0 || \
strcmp(#name, "logicOp") == 0 || \
strcmp(#name, "shaderCullDistance") == 0 || \
strcmp(#name, "wideLines") == 0)) { \
LOG_INFO(Render_Vulkan, "MoltenVK missing feature {} - using fallback", #name); \
} else { \
LOG_ERROR(Render_Vulkan, "Missing required feature {}", #name); \ LOG_ERROR(Render_Vulkan, "Missing required feature {}", #name); \
suitable = false; \ suitable = false; \
} \
} }
#define LOG_FEATURE(feature, name) \ #define LOG_FEATURE(feature, name) \
@ -1378,13 +1396,13 @@ void Device::CollectPhysicalMemoryInfo() {
device_access_memory += mem_properties.memoryHeaps[element].size; device_access_memory += mem_properties.memoryHeaps[element].size;
} }
if (!is_integrated) { if (!is_integrated) {
const u64 reserve_memory = std::min<u64>(device_access_memory / 4, 2_GiB); const u64 reserve_memory = std::min<u64>(device_access_memory / 8, 1_GiB);
device_access_memory -= reserve_memory; device_access_memory -= reserve_memory;
if (Settings::values.vram_usage_mode.GetValue() != Settings::VramUsageMode::Aggressive) { if (Settings::values.vram_usage_mode.GetValue() != Settings::VramUsageMode::Aggressive) {
// Account for resolution scaling in memory limits // Account for resolution scaling in memory limits
const size_t normal_memory = 8_GiB; const size_t normal_memory = 6_GiB;
const size_t scaler_memory = 2_GiB * Settings::values.resolution_info.ScaleUp(1); const size_t scaler_memory = 1_GiB * Settings::values.resolution_info.ScaleUp(1);
device_access_memory = device_access_memory =
std::min<u64>(device_access_memory, normal_memory + scaler_memory); std::min<u64>(device_access_memory, normal_memory + scaler_memory);
} }
@ -1393,7 +1411,7 @@ void Device::CollectPhysicalMemoryInfo() {
} }
const s64 available_memory = static_cast<s64>(device_access_memory - device_initial_usage); const s64 available_memory = static_cast<s64>(device_access_memory - device_initial_usage);
device_access_memory = static_cast<u64>(std::max<s64>( device_access_memory = static_cast<u64>(std::max<s64>(
std::min<s64>(available_memory - 4_GiB, 6_GiB), std::min<s64>(local_memory, 6_GiB))); std::min<s64>(available_memory - 8_GiB, 6_GiB), std::min<s64>(local_memory, 6_GiB)));
} }
void Device::CollectToolingInfo() { void Device::CollectToolingInfo() {

View file

@ -717,6 +717,10 @@ public:
return properties.driver.driverID == VK_DRIVER_ID_NVIDIA_PROPRIETARY; return properties.driver.driverID == VK_DRIVER_ID_NVIDIA_PROPRIETARY;
} }
bool IsMoltenVK() const noexcept {
return properties.driver.driverID == VK_DRIVER_ID_MOLTENVK;
}
NvidiaArchitecture GetNvidiaArch() const noexcept { NvidiaArchitecture GetNvidiaArch() const noexcept {
return nvidia_arch; return nvidia_arch;
} }

View file

@ -6,7 +6,10 @@
#include <algorithm> #include <algorithm>
#include <bit> #include <bit>
#include <limits>
#include <optional> #include <optional>
#include <type_traits>
#include <utility>
#include <vector> #include <vector>
#include "common/alignment.h" #include "common/alignment.h"
@ -21,65 +24,51 @@
#include "video_core/vulkan_common/vulkan_wrapper.h" #include "video_core/vulkan_common/vulkan_wrapper.h"
namespace Vulkan { namespace Vulkan {
namespace { namespace {
struct Range {
u64 begin;
u64 end;
[[nodiscard]] bool Contains(u64 iterator, u64 size) const noexcept { // Helpers translating MemoryUsage to flags/usage
return iterator < end && begin < iterator + size;
}
};
[[nodiscard]] u64 AllocationChunkSize(u64 required_size) { [[maybe_unused]] VkMemoryPropertyFlags MemoryUsagePropertyFlags(MemoryUsage usage) {
static constexpr std::array sizes{
0x1000ULL << 10, 0x1400ULL << 10, 0x1800ULL << 10, 0x1c00ULL << 10, 0x2000ULL << 10,
0x3200ULL << 10, 0x4000ULL << 10, 0x6000ULL << 10, 0x8000ULL << 10, 0xA000ULL << 10,
0x10000ULL << 10, 0x18000ULL << 10, 0x20000ULL << 10,
};
static_assert(std::is_sorted(sizes.begin(), sizes.end()));
const auto it = std::ranges::lower_bound(sizes, required_size);
return it != sizes.end() ? *it : Common::AlignUp(required_size, 4ULL << 20);
}
[[nodiscard]] VkMemoryPropertyFlags MemoryUsagePropertyFlags(MemoryUsage usage) {
switch (usage) { switch (usage) {
case MemoryUsage::DeviceLocal: case MemoryUsage::DeviceLocal:
return VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; return VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
case MemoryUsage::Upload: case MemoryUsage::Upload:
return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
case MemoryUsage::Download: case MemoryUsage::Download:
return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
VK_MEMORY_PROPERTY_HOST_CACHED_BIT; VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
case MemoryUsage::Stream: case MemoryUsage::Stream:
return VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | return VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
} }
ASSERT_MSG(false, "Invalid memory usage={}", usage); ASSERT_MSG(false, "Invalid memory usage={}", usage);
return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
} }
[[nodiscard]] VkMemoryPropertyFlags MemoryUsagePreferredVmaFlags(MemoryUsage usage) { [[nodiscard]] VkMemoryPropertyFlags MemoryUsagePreferredVmaFlags(MemoryUsage usage) {
return usage != MemoryUsage::DeviceLocal ? VK_MEMORY_PROPERTY_HOST_COHERENT_BIT return usage != MemoryUsage::DeviceLocal ? VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
: VkMemoryPropertyFlagBits{}; : VkMemoryPropertyFlagBits{};
} }
[[nodiscard]] VmaAllocationCreateFlags MemoryUsageVmaFlags(MemoryUsage usage) { [[nodiscard]] VmaAllocationCreateFlags MemoryUsageVmaFlags(MemoryUsage usage) {
switch (usage) { switch (usage) {
case MemoryUsage::Upload: case MemoryUsage::Upload:
case MemoryUsage::Stream: case MemoryUsage::Stream:
return VMA_ALLOCATION_CREATE_MAPPED_BIT | return VMA_ALLOCATION_CREATE_MAPPED_BIT |
VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT; VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT;
case MemoryUsage::Download: case MemoryUsage::Download:
return VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT; return VMA_ALLOCATION_CREATE_MAPPED_BIT |
VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT;
case MemoryUsage::DeviceLocal: case MemoryUsage::DeviceLocal:
return {}; return {};
} }
return {}; return {};
} }
[[nodiscard]] VmaMemoryUsage MemoryUsageVma(MemoryUsage usage) { [[nodiscard]] VmaMemoryUsage MemoryUsageVma(MemoryUsage usage) {
switch (usage) { switch (usage) {
case MemoryUsage::DeviceLocal: case MemoryUsage::DeviceLocal:
case MemoryUsage::Stream: case MemoryUsage::Stream:
@ -89,152 +78,130 @@ struct Range {
return VMA_MEMORY_USAGE_AUTO_PREFER_HOST; return VMA_MEMORY_USAGE_AUTO_PREFER_HOST;
} }
return VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; return VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE;
}
} // Anonymous namespace
class MemoryAllocation {
public:
explicit MemoryAllocation(MemoryAllocator* const allocator_, vk::DeviceMemory memory_,
VkMemoryPropertyFlags properties, u64 allocation_size_, u32 type)
: allocator{allocator_}, memory{std::move(memory_)}, allocation_size{allocation_size_},
property_flags{properties}, shifted_memory_type{1U << type} {}
MemoryAllocation& operator=(const MemoryAllocation&) = delete;
MemoryAllocation(const MemoryAllocation&) = delete;
MemoryAllocation& operator=(MemoryAllocation&&) = delete;
MemoryAllocation(MemoryAllocation&&) = delete;
[[nodiscard]] std::optional<MemoryCommit> Commit(VkDeviceSize size, VkDeviceSize alignment) {
const std::optional<u64> alloc = FindFreeRegion(size, alignment);
if (!alloc) {
// Signal out of memory, it'll try to do more allocations.
return std::nullopt;
}
const Range range{
.begin = *alloc,
.end = *alloc + size,
};
commits.insert(std::ranges::upper_bound(commits, *alloc, {}, &Range::begin), range);
return std::make_optional<MemoryCommit>(this, *memory, *alloc, *alloc + size);
}
void Free(u64 begin) {
const auto it = std::ranges::find(commits, begin, &Range::begin);
ASSERT_MSG(it != commits.end(), "Invalid commit");
commits.erase(it);
if (commits.empty()) {
// Do not call any code involving 'this' after this call, the object will be destroyed
allocator->ReleaseMemory(this);
}
}
[[nodiscard]] std::span<u8> Map() {
if (memory_mapped_span.empty()) {
u8* const raw_pointer = memory.Map(0, allocation_size);
memory_mapped_span = std::span<u8>(raw_pointer, allocation_size);
}
return memory_mapped_span;
}
/// Returns whether this allocation is compatible with the arguments.
[[nodiscard]] bool IsCompatible(VkMemoryPropertyFlags flags, u32 type_mask) const {
return (flags & property_flags) == flags && (type_mask & shifted_memory_type) != 0;
} }
private: // This avoids calling vkGetBufferMemoryRequirements* directly.
[[nodiscard]] static constexpr u32 ShiftType(u32 type) { template<typename T>
return 1U << type; static VkBuffer GetVkHandleFromBuffer(const T &buf) {
if constexpr (requires { static_cast<VkBuffer>(buf); }) {
return static_cast<VkBuffer>(buf);
} else if constexpr (requires {{ buf.GetHandle() } -> std::convertible_to<VkBuffer>; }) {
return buf.GetHandle();
} else if constexpr (requires {{ buf.Handle() } -> std::convertible_to<VkBuffer>; }) {
return buf.Handle();
} else if constexpr (requires {{ buf.vk_handle() } -> std::convertible_to<VkBuffer>; }) {
return buf.vk_handle();
} else {
static_assert(sizeof(T) == 0, "Cannot extract VkBuffer handle from vk::Buffer");
return VK_NULL_HANDLE;
}
} }
[[nodiscard]] std::optional<u64> FindFreeRegion(u64 size, u64 alignment) noexcept { } // namespace
ASSERT(std::has_single_bit(alignment));
const u64 alignment_log2 = std::countr_zero(alignment);
std::optional<u64> candidate;
u64 iterator = 0;
auto commit = commits.begin();
while (iterator + size <= allocation_size) {
candidate = candidate.value_or(iterator);
if (commit == commits.end()) {
break;
}
if (commit->Contains(*candidate, size)) {
candidate = std::nullopt;
}
iterator = Common::AlignUpLog2(commit->end, alignment_log2);
++commit;
}
return candidate;
}
MemoryAllocator* const allocator; ///< Parent memory allocation. //MemoryCommit is now VMA-backed
const vk::DeviceMemory memory; ///< Vulkan memory allocation handler. MemoryCommit::MemoryCommit(VmaAllocator alloc, VmaAllocation a,
const u64 allocation_size; ///< Size of this allocation. const VmaAllocationInfo &info) noexcept
const VkMemoryPropertyFlags property_flags; ///< Vulkan memory property flags. : allocator{alloc}, allocation{a}, memory{info.deviceMemory},
const u32 shifted_memory_type; ///< Shifted Vulkan memory type. offset{info.offset}, size{info.size}, mapped_ptr{info.pMappedData} {}
std::vector<Range> commits; ///< All commit ranges done from this allocation.
std::span<u8> memory_mapped_span; ///< Memory mapped span. Empty if not queried before.
};
MemoryCommit::MemoryCommit(MemoryAllocation* allocation_, VkDeviceMemory memory_, u64 begin_, MemoryCommit::~MemoryCommit() { Release(); }
u64 end_) noexcept
: allocation{allocation_}, memory{memory_}, begin{begin_}, end{end_} {}
MemoryCommit::~MemoryCommit() { MemoryCommit::MemoryCommit(MemoryCommit &&rhs) noexcept
Release(); : allocator{std::exchange(rhs.allocator, nullptr)},
} allocation{std::exchange(rhs.allocation, nullptr)},
memory{std::exchange(rhs.memory, VK_NULL_HANDLE)},
MemoryCommit& MemoryCommit::operator=(MemoryCommit&& rhs) noexcept { offset{std::exchange(rhs.offset, 0)},
size{std::exchange(rhs.size, 0)},
mapped_ptr{std::exchange(rhs.mapped_ptr, nullptr)} {}
MemoryCommit &MemoryCommit::operator=(MemoryCommit &&rhs) noexcept {
if (this != &rhs) {
Release(); Release();
allocator = std::exchange(rhs.allocator, nullptr);
allocation = std::exchange(rhs.allocation, nullptr); allocation = std::exchange(rhs.allocation, nullptr);
memory = rhs.memory; memory = std::exchange(rhs.memory, VK_NULL_HANDLE);
begin = rhs.begin; offset = std::exchange(rhs.offset, 0);
end = rhs.end; size = std::exchange(rhs.size, 0);
span = std::exchange(rhs.span, std::span<u8>{}); mapped_ptr = std::exchange(rhs.mapped_ptr, nullptr);
}
return *this; return *this;
}
MemoryCommit::MemoryCommit(MemoryCommit&& rhs) noexcept
: allocation{std::exchange(rhs.allocation, nullptr)}, memory{rhs.memory}, begin{rhs.begin},
end{rhs.end}, span{std::exchange(rhs.span, std::span<u8>{})} {}
std::span<u8> MemoryCommit::Map() {
if (span.empty()) {
span = allocation->Map().subspan(begin, end - begin);
} }
return span;
}
void MemoryCommit::Release() { std::span<u8> MemoryCommit::Map()
if (allocation) { {
allocation->Free(begin); if (!allocation) return {};
if (!mapped_ptr) {
if (vmaMapMemory(allocator, allocation, &mapped_ptr) != VK_SUCCESS) return {};
}
const size_t n = static_cast<size_t>(std::min<VkDeviceSize>(size,
std::numeric_limits<size_t>::max()));
return std::span<u8>{static_cast<u8 *>(mapped_ptr), n};
} }
}
MemoryAllocator::MemoryAllocator(const Device& device_) std::span<const u8> MemoryCommit::Map() const
{
if (!allocation) return {};
if (!mapped_ptr) {
void *p = nullptr;
if (vmaMapMemory(allocator, allocation, &p) != VK_SUCCESS) return {};
const_cast<MemoryCommit *>(this)->mapped_ptr = p;
}
const size_t n = static_cast<size_t>(std::min<VkDeviceSize>(size,
std::numeric_limits<size_t>::max()));
return std::span<const u8>{static_cast<const u8 *>(mapped_ptr), n};
}
void MemoryCommit::Unmap()
{
if (allocation && mapped_ptr) {
vmaUnmapMemory(allocator, allocation);
mapped_ptr = nullptr;
}
}
void MemoryCommit::Release() {
if (allocation && allocator) {
if (mapped_ptr) {
vmaUnmapMemory(allocator, allocation);
mapped_ptr = nullptr;
}
vmaFreeMemory(allocator, allocation);
}
allocation = nullptr;
allocator = nullptr;
memory = VK_NULL_HANDLE;
offset = 0;
size = 0;
}
MemoryAllocator::MemoryAllocator(const Device &device_)
: device{device_}, allocator{device.GetAllocator()}, : device{device_}, allocator{device.GetAllocator()},
properties{device_.GetPhysical().GetMemoryProperties().memoryProperties}, properties{device_.GetPhysical().GetMemoryProperties().memoryProperties},
buffer_image_granularity{ buffer_image_granularity{
device_.GetPhysical().GetProperties().limits.bufferImageGranularity} { device_.GetPhysical().GetProperties().limits.bufferImageGranularity} {
// GPUs not supporting rebar may only have a region with less than 256MB host visible/device
// local memory. In that case, opening 2 RenderDoc captures side-by-side is not possible due to // Preserve the previous "RenderDoc small heap" trimming behavior that we had in original vma minus the heap bug
// the heap running out of memory. With RenderDoc attached and only a small host/device region, if (device.HasDebuggingToolAttached())
// only allow the stream buffer in this memory heap. {
if (device.HasDebuggingToolAttached()) {
using namespace Common::Literals; using namespace Common::Literals;
ForEachDeviceLocalHostVisibleHeap(device, [this](size_t index, VkMemoryHeap& heap) { ForEachDeviceLocalHostVisibleHeap(device, [this](size_t heap_idx, VkMemoryHeap &heap) {
if (heap.size <= 256_MiB) { if (heap.size <= 256_MiB) {
valid_memory_types &= ~(1u << index); for (u32 t = 0; t < properties.memoryTypeCount; ++t) {
if (properties.memoryTypes[t].heapIndex == heap_idx) {
valid_memory_types &= ~(1u << t);
}
}
} }
}); });
} }
} }
MemoryAllocator::~MemoryAllocator() = default; MemoryAllocator::~MemoryAllocator() = default;
vk::Image MemoryAllocator::CreateImage(const VkImageCreateInfo& ci) const { vk::Image MemoryAllocator::CreateImage(const VkImageCreateInfo &ci) const
{
const VmaAllocationCreateInfo alloc_ci = { const VmaAllocationCreateInfo alloc_ci = {
.flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT, .flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT,
.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, .usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE,
@ -248,14 +215,14 @@ vk::Image MemoryAllocator::CreateImage(const VkImageCreateInfo& ci) const {
VkImage handle{}; VkImage handle{};
VmaAllocation allocation{}; VmaAllocation allocation{};
vk::Check(vmaCreateImage(allocator, &ci, &alloc_ci, &handle, &allocation, nullptr)); vk::Check(vmaCreateImage(allocator, &ci, &alloc_ci, &handle, &allocation, nullptr));
return vk::Image(handle, ci.usage, *device.GetLogical(), allocator, allocation, return vk::Image(handle, ci.usage, *device.GetLogical(), allocator, allocation,
device.GetDispatchLoader()); device.GetDispatchLoader());
} }
vk::Buffer MemoryAllocator::CreateBuffer(const VkBufferCreateInfo& ci, MemoryUsage usage) const { vk::Buffer
MemoryAllocator::CreateBuffer(const VkBufferCreateInfo &ci, MemoryUsage usage) const
{
const VmaAllocationCreateInfo alloc_ci = { const VmaAllocationCreateInfo alloc_ci = {
.flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT | MemoryUsageVmaFlags(usage), .flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT | MemoryUsageVmaFlags(usage),
.usage = MemoryUsageVma(usage), .usage = MemoryUsageVma(usage),
@ -272,131 +239,87 @@ vk::Buffer MemoryAllocator::CreateBuffer(const VkBufferCreateInfo& ci, MemoryUsa
VmaAllocation allocation{}; VmaAllocation allocation{};
VkMemoryPropertyFlags property_flags{}; VkMemoryPropertyFlags property_flags{};
VkResult result = vmaCreateBuffer(allocator, &ci, &alloc_ci, &handle, &allocation, &alloc_info); vk::Check(vmaCreateBuffer(allocator, &ci, &alloc_ci, &handle, &allocation, &alloc_info));
if (result == VK_ERROR_OUT_OF_DEVICE_MEMORY) {
LOG_ERROR(Render_Vulkan, "Out of memory creating buffer (size: {})", ci.size);
}
vmaGetAllocationMemoryProperties(allocator, allocation, &property_flags); vmaGetAllocationMemoryProperties(allocator, allocation, &property_flags);
u8* data = reinterpret_cast<u8*>(alloc_info.pMappedData); u8 *data = reinterpret_cast<u8 *>(alloc_info.pMappedData);
const std::span<u8> mapped_data = data ? std::span<u8>{data, ci.size} : std::span<u8>{}; const std::span<u8> mapped_data = data ? std::span<u8>{data, ci.size} : std::span<u8>{};
const bool is_coherent = property_flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; const bool is_coherent = (property_flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0;
return vk::Buffer(handle, *device.GetLogical(), allocator, allocation, mapped_data, is_coherent, return vk::Buffer(handle, *device.GetLogical(), allocator, allocation, mapped_data,
is_coherent,
device.GetDispatchLoader()); device.GetDispatchLoader());
}
MemoryCommit MemoryAllocator::Commit(const VkMemoryRequirements& requirements, MemoryUsage usage) {
// Find the fastest memory flags we can afford with the current requirements
const u32 type_mask = requirements.memoryTypeBits;
const VkMemoryPropertyFlags usage_flags = MemoryUsagePropertyFlags(usage);
const VkMemoryPropertyFlags flags = MemoryPropertyFlags(type_mask, usage_flags);
if (std::optional<MemoryCommit> commit = TryCommit(requirements, flags)) {
return std::move(*commit);
}
// Commit has failed, allocate more memory.
const u64 chunk_size = AllocationChunkSize(requirements.size);
if (!TryAllocMemory(flags, type_mask, chunk_size)) {
// TODO(Rodrigo): Handle out of memory situations in some way like flushing to guest memory.
throw vk::Exception(VK_ERROR_OUT_OF_DEVICE_MEMORY);
}
// Commit again, this time it won't fail since there's a fresh allocation above.
// If it does, there's a bug.
return TryCommit(requirements, flags).value();
} }
bool MemoryAllocator::TryAllocMemory(VkMemoryPropertyFlags flags, u32 type_mask, u64 size) { MemoryCommit MemoryAllocator::Commit(const VkMemoryRequirements &reqs, MemoryUsage usage)
const auto type_opt = FindType(flags, type_mask); {
if (!type_opt) { const auto vma_usage = MemoryUsageVma(usage);
return false; VmaAllocationCreateInfo ci{};
ci.flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT | MemoryUsageVmaFlags(usage);
ci.usage = vma_usage;
ci.memoryTypeBits = reqs.memoryTypeBits & valid_memory_types;
ci.requiredFlags = 0;
ci.preferredFlags = MemoryUsagePreferredVmaFlags(usage);
VmaAllocation a{};
VmaAllocationInfo info{};
VkResult res = vmaAllocateMemory(allocator, &reqs, &ci, &a, &info);
if (res != VK_SUCCESS) {
// Relax 1: drop budget constraint
auto ci2 = ci;
ci2.flags &= ~VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT;
res = vmaAllocateMemory(allocator, &reqs, &ci2, &a, &info);
// Relax 2: if we preferred DEVICE_LOCAL, drop that preference
if (res != VK_SUCCESS && (ci.preferredFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) {
auto ci3 = ci2;
ci3.preferredFlags &= ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
res = vmaAllocateMemory(allocator, &reqs, &ci3, &a, &info);
}
} }
// Adreno stands firm vk::Check(res);
const u64 aligned_size = (device.GetDriverID() == VK_DRIVER_ID_QUALCOMM_PROPRIETARY) ? return MemoryCommit(allocator, a, info);
Common::AlignUp(size, 4096) :
size;
vk::DeviceMemory memory = device.GetLogical().TryAllocateMemory({
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.pNext = nullptr,
.allocationSize = aligned_size,
.memoryTypeIndex = *type_opt,
});
if (!memory) {
return false;
} }
allocations.push_back( MemoryCommit MemoryAllocator::Commit(const vk::Buffer &buffer, MemoryUsage usage) {
std::make_unique<MemoryAllocation>(this, std::move(memory), flags, aligned_size, *type_opt)); // Allocate memory appropriate for this buffer automatically
return true; const auto vma_usage = MemoryUsageVma(usage);
}
void MemoryAllocator::ReleaseMemory(MemoryAllocation* alloc) { VmaAllocationCreateInfo ci{};
const auto it = std::ranges::find(allocations, alloc, &std::unique_ptr<MemoryAllocation>::get); ci.flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT | MemoryUsageVmaFlags(usage);
ASSERT(it != allocations.end()); ci.usage = vma_usage;
allocations.erase(it); ci.requiredFlags = 0;
} ci.preferredFlags = MemoryUsagePreferredVmaFlags(usage);
ci.pool = VK_NULL_HANDLE;
ci.pUserData = nullptr;
ci.priority = 0.0f;
std::optional<MemoryCommit> MemoryAllocator::TryCommit(const VkMemoryRequirements& requirements, const VkBuffer raw = *buffer;
VkMemoryPropertyFlags flags) {
// Conservative, spec-compliant alignment for suballocation
VkDeviceSize eff_align = requirements.alignment;
const auto& limits = device.GetPhysical().GetProperties().limits;
if ((flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) &&
!(flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) {
// Non-coherent memory must be invalidated on atom boundary
if (limits.nonCoherentAtomSize > eff_align) eff_align = limits.nonCoherentAtomSize;
}
// Separate buffers to avoid stalls on tilers
if (buffer_image_granularity > eff_align) {
eff_align = buffer_image_granularity;
}
eff_align = std::bit_ceil(eff_align);
for (auto& allocation : allocations) { VmaAllocation a{};
if (!allocation->IsCompatible(flags, requirements.memoryTypeBits)) { VmaAllocationInfo info{};
continue;
}
if (auto commit = allocation->Commit(requirements.size, eff_align)) {
return commit;
}
}
if ((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0) {
// Look for non device local commits on failure
return TryCommit(requirements, flags & ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
}
return std::nullopt;
}
VkMemoryPropertyFlags MemoryAllocator::MemoryPropertyFlags(u32 type_mask, // Let VMA infer memory requirements from the buffer
VkMemoryPropertyFlags flags) const { VkResult res = vmaAllocateMemoryForBuffer(allocator, raw, &ci, &a, &info);
if (FindType(flags, type_mask)) {
// Found a memory type with those requirements
return flags;
}
if ((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0) {
// Remove host cached bit in case it's not supported
return MemoryPropertyFlags(type_mask, flags & ~VK_MEMORY_PROPERTY_HOST_CACHED_BIT);
}
if ((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0) {
// Remove device local, if it's not supported by the requested resource
return MemoryPropertyFlags(type_mask, flags & ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
}
ASSERT_MSG(false, "No compatible memory types found");
return 0;
}
std::optional<u32> MemoryAllocator::FindType(VkMemoryPropertyFlags flags, u32 type_mask) const { if (res != VK_SUCCESS) {
for (u32 type_index = 0; type_index < properties.memoryTypeCount; ++type_index) { auto ci2 = ci;
const VkMemoryPropertyFlags type_flags = properties.memoryTypes[type_index].propertyFlags; ci2.flags &= ~VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT;
if ((type_mask & (1U << type_index)) != 0 && (type_flags & flags) == flags) { res = vmaAllocateMemoryForBuffer(allocator, raw, &ci2, &a, &info);
// The type matches in type and in the wanted properties.
return type_index; if (res != VK_SUCCESS && (ci.preferredFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) {
auto ci3 = ci2;
ci3.preferredFlags &= ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
res = vmaAllocateMemoryForBuffer(allocator, raw, &ci3, &a, &info);
} }
} }
// Failed to find index
return std::nullopt; vk::Check(res);
} vk::Check(vmaBindBufferMemory2(allocator, a, 0, raw, nullptr));
return MemoryCommit(allocator, a, info);
}
} // namespace Vulkan } // namespace Vulkan

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -6,84 +9,85 @@
#include <memory> #include <memory>
#include <span> #include <span>
#include <vector> #include <vector>
#include "common/common_types.h" #include "common/common_types.h"
#include "video_core/vulkan_common/vulkan_device.h" #include "video_core/vulkan_common/vulkan_device.h"
#include "video_core/vulkan_common/vulkan_wrapper.h" #include "video_core/vulkan_common/vulkan_wrapper.h"
#include "video_core/vulkan_common/vma.h"
VK_DEFINE_HANDLE(VmaAllocator)
namespace Vulkan { namespace Vulkan {
class Device; class Device;
class MemoryMap;
class MemoryAllocation;
/// Hints and requirements for the backing memory type of a commit /// Hints and requirements for the backing memory type of a commit
enum class MemoryUsage { enum class MemoryUsage {
DeviceLocal, ///< Requests device local host visible buffer, falling back to device local DeviceLocal, ///< Requests device local host visible buffer, falling back to device local memory.
///< memory.
Upload, ///< Requires a host visible memory type optimized for CPU to GPU uploads Upload, ///< Requires a host visible memory type optimized for CPU to GPU uploads
Download, ///< Requires a host visible memory type optimized for GPU to CPU readbacks Download, ///< Requires a host visible memory type optimized for GPU to CPU readbacks
Stream, ///< Requests device local host visible buffer, falling back host memory. Stream, ///< Requests device local host visible buffer, falling back host memory.
}; };
template <typename F> template<typename F>
void ForEachDeviceLocalHostVisibleHeap(const Device& device, F&& f) { void ForEachDeviceLocalHostVisibleHeap(const Device &device, F &&f) {
auto memory_props = device.GetPhysical().GetMemoryProperties().memoryProperties; auto memory_props = device.GetPhysical().GetMemoryProperties().memoryProperties;
for (size_t i = 0; i < memory_props.memoryTypeCount; i++) { for (size_t i = 0; i < memory_props.memoryTypeCount; i++) {
auto& memory_type = memory_props.memoryTypes[i]; auto &memory_type = memory_props.memoryTypes[i];
if ((memory_type.propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) && if ((memory_type.propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) &&
(memory_type.propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)) { (memory_type.propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)) {
f(memory_type.heapIndex, memory_props.memoryHeaps[memory_type.heapIndex]); f(memory_type.heapIndex, memory_props.memoryHeaps[memory_type.heapIndex]);
} }
} }
} }
/// Ownership handle of a memory commitment (real VMA allocation).
class MemoryCommit {
public:
MemoryCommit() noexcept = default;
MemoryCommit(VmaAllocator allocator, VmaAllocation allocation,
const VmaAllocationInfo &info) noexcept;
/// Ownership handle of a memory commitment.
/// Points to a subregion of a memory allocation.
class MemoryCommit {
public:
explicit MemoryCommit() noexcept = default;
explicit MemoryCommit(MemoryAllocation* allocation_, VkDeviceMemory memory_, u64 begin_,
u64 end_) noexcept;
~MemoryCommit(); ~MemoryCommit();
MemoryCommit& operator=(MemoryCommit&&) noexcept; MemoryCommit(const MemoryCommit &) = delete;
MemoryCommit(MemoryCommit&&) noexcept;
MemoryCommit& operator=(const MemoryCommit&) = delete; MemoryCommit &operator=(const MemoryCommit &) = delete;
MemoryCommit(const MemoryCommit&) = delete;
/// Returns a host visible memory map. MemoryCommit(MemoryCommit &&) noexcept;
/// It will map the backing allocation if it hasn't been mapped before.
std::span<u8> Map();
/// Returns the Vulkan memory handler. MemoryCommit &operator=(MemoryCommit &&) noexcept;
VkDeviceMemory Memory() const {
return memory;
}
/// Returns the start position of the commit relative to the allocation. [[nodiscard]] std::span<u8> Map();
VkDeviceSize Offset() const {
return static_cast<VkDeviceSize>(begin);
}
private: [[nodiscard]] std::span<const u8> Map() const;
void Unmap();
explicit operator bool() const noexcept { return allocation != nullptr; }
VkDeviceMemory Memory() const noexcept { return memory; }
VkDeviceSize Offset() const noexcept { return offset; }
VkDeviceSize Size() const noexcept { return size; }
VmaAllocation Allocation() const noexcept { return allocation; }
private:
void Release(); void Release();
MemoryAllocation* allocation{}; ///< Pointer to the large memory allocation. VmaAllocator allocator{}; ///< VMA allocator
VkDeviceMemory memory{}; ///< Vulkan device memory handler. VmaAllocation allocation{}; ///< VMA allocation handle
u64 begin{}; ///< Beginning offset in bytes to where the commit exists. VkDeviceMemory memory{}; ///< Underlying VkDeviceMemory chosen by VMA
u64 end{}; ///< Offset in bytes where the commit ends. VkDeviceSize offset{}; ///< Offset of this allocation inside VkDeviceMemory
std::span<u8> span; ///< Host visible memory span. Empty if not queried before. VkDeviceSize size{}; ///< Size of the allocation
}; void *mapped_ptr{}; ///< Optional persistent mapped pointer
};
/// Memory allocator container. /// Memory allocator container.
/// Allocates and releases memory allocations on demand. /// Allocates and releases memory allocations on demand.
class MemoryAllocator { class MemoryAllocator {
friend MemoryAllocation; public:
public:
/** /**
* Construct memory allocator * Construct memory allocator
* *
@ -91,15 +95,17 @@ public:
* *
* @throw vk::Exception on failure * @throw vk::Exception on failure
*/ */
explicit MemoryAllocator(const Device& device_); explicit MemoryAllocator(const Device &device_);
~MemoryAllocator(); ~MemoryAllocator();
MemoryAllocator& operator=(const MemoryAllocator&) = delete; MemoryAllocator &operator=(const MemoryAllocator &) = delete;
MemoryAllocator(const MemoryAllocator&) = delete;
vk::Image CreateImage(const VkImageCreateInfo& ci) const; MemoryAllocator(const MemoryAllocator &) = delete;
vk::Buffer CreateBuffer(const VkBufferCreateInfo& ci, MemoryUsage usage) const; vk::Image CreateImage(const VkImageCreateInfo &ci) const;
vk::Buffer CreateBuffer(const VkBufferCreateInfo &ci, MemoryUsage usage) const;
/** /**
* Commits a memory with the specified requirements. * Commits a memory with the specified requirements.
@ -109,35 +115,28 @@ public:
* *
* @returns A memory commit. * @returns A memory commit.
*/ */
MemoryCommit Commit(const VkMemoryRequirements& requirements, MemoryUsage usage); MemoryCommit Commit(const VkMemoryRequirements &requirements, MemoryUsage usage);
/// Commits memory required by the buffer and binds it. /// Commits memory required by the buffer and binds it (for buffers created outside VMA).
MemoryCommit Commit(const vk::Buffer& buffer, MemoryUsage usage); MemoryCommit Commit(const vk::Buffer &buffer, MemoryUsage usage);
private: private:
/// Tries to allocate a chunk of memory. static bool IsAutoUsage(VmaMemoryUsage u) noexcept {
bool TryAllocMemory(VkMemoryPropertyFlags flags, u32 type_mask, u64 size); switch (u) {
case VMA_MEMORY_USAGE_AUTO:
case VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE:
case VMA_MEMORY_USAGE_AUTO_PREFER_HOST:
return true;
default:
return false;
}
}
/// Releases a chunk of memory. const Device &device; ///< Device handle.
void ReleaseMemory(MemoryAllocation* alloc); VmaAllocator allocator; ///< VMA allocator.
const VkPhysicalDeviceMemoryProperties properties; ///< Physical device memory properties.
/// Tries to allocate a memory commit. VkDeviceSize buffer_image_granularity; ///< Adjacent buffer/image granularity
std::optional<MemoryCommit> TryCommit(const VkMemoryRequirements& requirements,
VkMemoryPropertyFlags flags);
/// Returns the fastest compatible memory property flags from the wanted flags.
VkMemoryPropertyFlags MemoryPropertyFlags(u32 type_mask, VkMemoryPropertyFlags flags) const;
/// Returns index to the fastest memory type compatible with the passed requirements.
std::optional<u32> FindType(VkMemoryPropertyFlags flags, u32 type_mask) const;
const Device& device; ///< Device handle.
VmaAllocator allocator; ///< Vma allocator.
const VkPhysicalDeviceMemoryProperties properties; ///< Physical device properties.
std::vector<std::unique_ptr<MemoryAllocation>> allocations; ///< Current allocations.
VkDeviceSize buffer_image_granularity; // The granularity for adjacent offsets between buffers
// and optimal images
u32 valid_memory_types{~0u}; u32 valid_memory_types{~0u};
}; };
} // namespace Vulkan } // namespace Vulkan

View file

@ -580,6 +580,7 @@ DescriptorSets DescriptorPool::Allocate(const VkDescriptorSetAllocateInfo& ai) c
case VK_SUCCESS: case VK_SUCCESS:
return DescriptorSets(std::move(sets), num, owner, handle, *dld); return DescriptorSets(std::move(sets), num, owner, handle, *dld);
case VK_ERROR_OUT_OF_POOL_MEMORY: case VK_ERROR_OUT_OF_POOL_MEMORY:
case VK_ERROR_FRAGMENTED_POOL:
return {}; return {};
default: default:
throw Exception(result); throw Exception(result);
@ -604,6 +605,7 @@ CommandBuffers CommandPool::Allocate(std::size_t num_buffers, VkCommandBufferLev
case VK_SUCCESS: case VK_SUCCESS:
return CommandBuffers(std::move(buffers), num_buffers, owner, handle, *dld); return CommandBuffers(std::move(buffers), num_buffers, owner, handle, *dld);
case VK_ERROR_OUT_OF_POOL_MEMORY: case VK_ERROR_OUT_OF_POOL_MEMORY:
case VK_ERROR_FRAGMENTED_POOL:
return {}; return {};
default: default:
throw Exception(result); throw Exception(result);

View file

@ -501,11 +501,6 @@ if (YUZU_ROOM)
target_link_libraries(yuzu PRIVATE yuzu-room) target_link_libraries(yuzu PRIVATE yuzu-room)
endif() endif()
# Explicit linking required
if (PLATFORM_SUN)
target_link_libraries(yuzu PRIVATE X11 "/usr/lib/xorg/amd64/libdrm.so")
endif()
# Extra deps # Extra deps
add_subdirectory(externals) add_subdirectory(externals)
target_link_libraries(yuzu PRIVATE QuaZip::QuaZip) target_link_libraries(yuzu PRIVATE QuaZip::QuaZip)

View file

@ -60,6 +60,10 @@ void ConfigureGraphicsExtensions::Setup(const ConfigurationShared::Builder& buil
if (setting->Id() == Settings::values.dyna_state.Id()) { if (setting->Id() == Settings::values.dyna_state.Id()) {
widget->slider->setTickInterval(1); widget->slider->setTickInterval(1);
widget->slider->setTickPosition(QSlider::TicksAbove); widget->slider->setTickPosition(QSlider::TicksAbove);
#ifdef __APPLE__
widget->setEnabled(false);
widget->setToolTip(tr("Extended Dynamic State is disabled on macOS due to MoltenVK compatibility issues that cause black screens."));
#endif
} }
} }

View file

@ -1,7 +1,8 @@
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project # SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
# cpm # Explicitly include CPMUtil here since we have a separate cpmfile for Qt externals
set(CPMUTIL_JSON_FILE ${CMAKE_CURRENT_SOURCE_DIR}/cpmfile.json)
include(CPMUtil) include(CPMUtil)
# Disable tests/tools in all externals supporting the standard option name # Disable tests/tools in all externals supporting the standard option name
@ -14,12 +15,4 @@ set(BUILD_SHARED_LIBS OFF)
set_directory_properties(PROPERTIES EXCLUDE_FROM_ALL ON) set_directory_properties(PROPERTIES EXCLUDE_FROM_ALL ON)
# QuaZip # QuaZip
AddPackage( AddJsonPackage(quazip)
NAME QuaZip-Qt6
VERSION 1.3
REPO "crueter/quazip-qt6"
SHA f838774d63
HASH 9f629a438699801244a106c8df6d5f8f8d19e80df54f530a89403a10c8c4e37a6e95606bbdd307f23636961e8ce34eb37a2186d589a1f227ac9c8e2c678e326e
OPTIONS
"QUAZIP_INSTALL OFF"
)

12
src/yuzu/externals/cpmfile.json vendored Normal file
View file

@ -0,0 +1,12 @@
{
"quazip": {
"package": "QuaZip-Qt6",
"repo": "crueter/quazip-qt6",
"sha": "f838774d63",
"hash": "9f629a438699801244a106c8df6d5f8f8d19e80df54f530a89403a10c8c4e37a6e95606bbdd307f23636961e8ce34eb37a2186d589a1f227ac9c8e2c678e326e",
"version": "1.3",
"options": [
"QUAZIP_INSTALL OFF"
]
}
}

View file

@ -553,9 +553,6 @@ GMainWindow::GMainWindow(bool has_broken_vulkan)
// Gen keys if necessary // Gen keys if necessary
OnCheckFirmwareDecryption(); OnCheckFirmwareDecryption();
// Check firmware
OnCheckFirmware();
game_list->LoadCompatibilityList(); game_list->LoadCompatibilityList();
// force reload on first load to ensure add-ons get updated // force reload on first load to ensure add-ons get updated
game_list->PopulateAsync(UISettings::values.game_dirs, false); game_list->PopulateAsync(UISettings::values.game_dirs, false);
@ -3094,34 +3091,7 @@ bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path,
const std::filesystem::path& command, const std::filesystem::path& command,
const std::string& arguments, const std::string& categories, const std::string& arguments, const std::string& categories,
const std::string& keywords, const std::string& name) try { const std::string& keywords, const std::string& name) try {
#if defined(__linux__) || defined(__FreeBSD__) // Linux and FreeBSD #ifdef _WIN32 // Windows
std::filesystem::path shortcut_path_full = shortcut_path / (name + ".desktop");
std::ofstream shortcut_stream(shortcut_path_full, std::ios::binary | std::ios::trunc);
if (!shortcut_stream.is_open()) {
LOG_ERROR(Frontend, "Failed to create shortcut");
return false;
}
// TODO: Migrate fmt::print to std::print in futures STD C++ 23.
fmt::print(shortcut_stream, "[Desktop Entry]\n");
fmt::print(shortcut_stream, "Type=Application\n");
fmt::print(shortcut_stream, "Version=1.0\n");
fmt::print(shortcut_stream, "Name={}\n", name);
if (!comment.empty()) {
fmt::print(shortcut_stream, "Comment={}\n", comment);
}
if (std::filesystem::is_regular_file(icon_path)) {
fmt::print(shortcut_stream, "Icon={}\n", icon_path.string());
}
fmt::print(shortcut_stream, "TryExec={}\n", command.string());
fmt::print(shortcut_stream, "Exec={} {}\n", command.string(), arguments);
if (!categories.empty()) {
fmt::print(shortcut_stream, "Categories={}\n", categories);
}
if (!keywords.empty()) {
fmt::print(shortcut_stream, "Keywords={}\n", keywords);
}
return true;
#elif defined(_WIN32) // Windows
HRESULT hr = CoInitialize(nullptr); HRESULT hr = CoInitialize(nullptr);
if (FAILED(hr)) { if (FAILED(hr)) {
LOG_ERROR(Frontend, "CoInitialize failed"); LOG_ERROR(Frontend, "CoInitialize failed");
@ -3183,6 +3153,33 @@ bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path,
return false; return false;
} }
return true; return true;
#elif defined(__unix__) && !defined(__APPLE__) && !defined(__ANDROID__) // Any desktop NIX
std::filesystem::path shortcut_path_full = shortcut_path / (name + ".desktop");
std::ofstream shortcut_stream(shortcut_path_full, std::ios::binary | std::ios::trunc);
if (!shortcut_stream.is_open()) {
LOG_ERROR(Frontend, "Failed to create shortcut");
return false;
}
// TODO: Migrate fmt::print to std::print in futures STD C++ 23.
fmt::print(shortcut_stream, "[Desktop Entry]\n");
fmt::print(shortcut_stream, "Type=Application\n");
fmt::print(shortcut_stream, "Version=1.0\n");
fmt::print(shortcut_stream, "Name={}\n", name);
if (!comment.empty()) {
fmt::print(shortcut_stream, "Comment={}\n", comment);
}
if (std::filesystem::is_regular_file(icon_path)) {
fmt::print(shortcut_stream, "Icon={}\n", icon_path.string());
}
fmt::print(shortcut_stream, "TryExec={}\n", command.string());
fmt::print(shortcut_stream, "Exec={} {}\n", command.string(), arguments);
if (!categories.empty()) {
fmt::print(shortcut_stream, "Categories={}\n", categories);
}
if (!keywords.empty()) {
fmt::print(shortcut_stream, "Keywords={}\n", keywords);
}
return true;
#else // Unsupported platform #else // Unsupported platform
return false; return false;
#endif #endif
@ -3228,7 +3225,7 @@ bool GMainWindow::MakeShortcutIcoPath(const u64 program_id, const std::string_vi
#if defined(_WIN32) #if defined(_WIN32)
out_icon_path = Common::FS::GetEdenPath(Common::FS::EdenPath::IconsDir); out_icon_path = Common::FS::GetEdenPath(Common::FS::EdenPath::IconsDir);
ico_extension = "ico"; ico_extension = "ico";
#elif defined(__linux__) || defined(__FreeBSD__) #elif defined(__unix__) && !defined(__APPLE__) && !defined(__ANDROID__)
out_icon_path = Common::FS::GetDataDirectory("XDG_DATA_HOME") / "icons/hicolor/256x256"; out_icon_path = Common::FS::GetDataDirectory("XDG_DATA_HOME") / "icons/hicolor/256x256";
#endif #endif
// Create icons directory if it doesn't exist // Create icons directory if it doesn't exist
@ -4459,7 +4456,6 @@ void GMainWindow::InstallFirmware(const QString& location, bool recursive) {
progress.close(); progress.close();
OnCheckFirmwareDecryption(); OnCheckFirmwareDecryption();
OnCheckFirmware();
} }
void GMainWindow::OnInstallFirmware() { void GMainWindow::OnInstallFirmware() {
@ -4580,7 +4576,6 @@ void GMainWindow::OnInstallDecryptionKeys() {
} }
OnCheckFirmwareDecryption(); OnCheckFirmwareDecryption();
OnCheckFirmware();
} }
void GMainWindow::OnAbout() { void GMainWindow::OnAbout() {
@ -4609,6 +4604,7 @@ void GMainWindow::OnToggleStatusBar() {
void GMainWindow::OnGameListRefresh() { void GMainWindow::OnGameListRefresh() {
// force reload add-ons etc // force reload add-ons etc
game_list->ForceRefreshGameDirectory(); game_list->ForceRefreshGameDirectory();
SetFirmwareVersion();
} }
void GMainWindow::OnAlbum() { void GMainWindow::OnAlbum() {
@ -4707,13 +4703,42 @@ void GMainWindow::OnOpenControllerMenu() {
} }
void GMainWindow::OnHomeMenu() { void GMainWindow::OnHomeMenu() {
auto result = FirmwareManager::VerifyFirmware(*system.get());
switch (result) {
case FirmwareManager::ErrorFirmwareMissing:
QMessageBox::warning(this, tr("No firmware available"),
tr("Please install firmware to use the Home Menu."));
return;
case FirmwareManager::ErrorFirmwareCorrupted:
QMessageBox::warning(this, tr("Firmware Corrupted"),
tr(FirmwareManager::GetFirmwareCheckString(result)));
return;
case FirmwareManager::ErrorFirmwareTooNew: {
if (!UISettings::values.show_fw_warning.GetValue()) break;
QMessageBox box(QMessageBox::Warning,
tr("Firmware Too New"),
tr(FirmwareManager::GetFirmwareCheckString(result)) + tr("\nContinue anyways?"),
QMessageBox::Yes | QMessageBox::No,
this);
QCheckBox *checkbox = new QCheckBox(tr("Don't show again"));
box.setCheckBox(checkbox);
int button = box.exec();
if (checkbox->isChecked()) {
UISettings::values.show_fw_warning.SetValue(false);
}
if (button == static_cast<int>(QMessageBox::No)) return;
break;
} default:
break;
}
constexpr u64 QLaunchId = static_cast<u64>(Service::AM::AppletProgramId::QLaunch); constexpr u64 QLaunchId = static_cast<u64>(Service::AM::AppletProgramId::QLaunch);
auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
if (!bis_system) {
QMessageBox::warning(this, tr("No firmware available"),
tr("Please install the firmware to use the Home Menu."));
return;
}
auto qlaunch_applet_nca = bis_system->GetEntry(QLaunchId, FileSys::ContentRecordType::Program); auto qlaunch_applet_nca = bis_system->GetEntry(QLaunchId, FileSys::ContentRecordType::Program);
if (!qlaunch_applet_nca) { if (!qlaunch_applet_nca) {
@ -4853,7 +4878,7 @@ void GMainWindow::CreateShortcut(const std::string& game_path, const u64 program
} }
} }
#if defined(__linux__) #if defined(__unix__) && !defined(__APPLE__) && !defined(__ANDROID__)
// Special case for AppImages // Special case for AppImages
// Warn once if we are making a shortcut to a volatile AppImage // Warn once if we are making a shortcut to a volatile AppImage
if (command.string().ends_with(".AppImage") && !UISettings::values.shortcut_already_warned) { if (command.string().ends_with(".AppImage") && !UISettings::values.shortcut_already_warned) {
@ -4863,7 +4888,7 @@ void GMainWindow::CreateShortcut(const std::string& game_path, const u64 program
} }
UISettings::values.shortcut_already_warned = true; UISettings::values.shortcut_already_warned = true;
} }
#endif // __linux__ #endif
// Create shortcut // Create shortcut
std::string arguments{arguments_}; std::string arguments{arguments_};
@ -5240,19 +5265,6 @@ void GMainWindow::OnCheckFirmwareDecryption() {
UpdateMenuState(); UpdateMenuState();
} }
void GMainWindow::OnCheckFirmware() {
auto result = FirmwareManager::VerifyFirmware(*system.get());
switch (result) {
case FirmwareManager::FirmwareGood:
break;
default:
QMessageBox::warning(this, tr("Firmware Read Error"),
tr(FirmwareManager::GetFirmwareCheckString(result)));
break;
}
}
bool GMainWindow::CheckFirmwarePresence() { bool GMainWindow::CheckFirmwarePresence() {
return FirmwareManager::CheckFirmwarePresence(*system.get()); return FirmwareManager::CheckFirmwarePresence(*system.get());
} }
@ -5730,17 +5742,13 @@ int main(int argc, char* argv[]) {
#ifdef _WIN32 #ifdef _WIN32
// Increases the maximum open file limit to 8192 // Increases the maximum open file limit to 8192
_setmaxstdio(8192); _setmaxstdio(8192);
#endif #elif defined(__APPLE__)
#ifdef __APPLE__
// If you start a bundle (binary) on OSX without the Terminal, the working directory is "/". // If you start a bundle (binary) on OSX without the Terminal, the working directory is "/".
// But since we require the working directory to be the executable path for the location of // But since we require the working directory to be the executable path for the location of
// the user folder in the Qt Frontend, we need to cd into that working directory // the user folder in the Qt Frontend, we need to cd into that working directory
const auto bin_path = Common::FS::GetBundleDirectory() / ".."; const auto bin_path = Common::FS::GetBundleDirectory() / "..";
chdir(Common::FS::PathToUTF8String(bin_path).c_str()); chdir(Common::FS::PathToUTF8String(bin_path).c_str());
#endif #elif defined(__unix__) && !defined(__ANDROID__)
#ifdef __linux__
// Set the DISPLAY variable in order to open web browsers // Set the DISPLAY variable in order to open web browsers
// TODO (lat9nq): Find a better solution for AppImages to start external applications // TODO (lat9nq): Find a better solution for AppImages to start external applications
if (QString::fromLocal8Bit(qgetenv("DISPLAY")).isEmpty()) { if (QString::fromLocal8Bit(qgetenv("DISPLAY")).isEmpty()) {
@ -5749,7 +5757,7 @@ int main(int argc, char* argv[]) {
// Fix the Wayland appId. This needs to match the name of the .desktop file without the .desktop // Fix the Wayland appId. This needs to match the name of the .desktop file without the .desktop
// suffix. // suffix.
QGuiApplication::setDesktopFileName(QStringLiteral("org.eden_emu.eden")); QGuiApplication::setDesktopFileName(QStringLiteral("dev.eden_emu.eden"));
#endif #endif
SetHighDPIAttributes(); SetHighDPIAttributes();

View file

@ -424,7 +424,6 @@ private slots:
void OnCreateHomeMenuShortcut(GameListShortcutTarget target); void OnCreateHomeMenuShortcut(GameListShortcutTarget target);
void OnCaptureScreenshot(); void OnCaptureScreenshot();
void OnCheckFirmwareDecryption(); void OnCheckFirmwareDecryption();
void OnCheckFirmware();
void OnLanguageChanged(const QString& locale); void OnLanguageChanged(const QString& locale);
void OnMouseActivity(); void OnMouseActivity();
bool OnShutdownBegin(); bool OnShutdownBegin();

View file

@ -212,6 +212,9 @@ struct Values {
// Play time // Play time
Setting<bool> show_play_time{linkage, true, "show_play_time", Category::UiGameList}; Setting<bool> show_play_time{linkage, true, "show_play_time", Category::UiGameList};
// misc
Setting<bool> show_fw_warning{linkage, true, "show_fw_warning", Category::Miscellaneous};
bool configuration_applied; bool configuration_applied;
bool reset_to_defaults; bool reset_to_defaults;
bool shortcut_already_warned{false}; bool shortcut_already_warned{false};

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2015 Citra Emulator Project // SPDX-FileCopyrightText: 2015 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -138,7 +141,7 @@ bool SaveIconToFile(const std::filesystem::path& icon_path, const QImage& image)
icon_file.Close(); icon_file.Close();
return true; return true;
#elif defined(__linux__) || defined(__FreeBSD__) #elif defined(__unix__) && !defined(__APPLE__) && !defined(__ANDROID__)
// Convert and write the icon as a PNG // Convert and write the icon as a PNG
if (!image.save(QString::fromStdString(icon_path.string()))) { if (!image.save(QString::fromStdString(icon_path.string()))) {
LOG_ERROR(Frontend, "Could not write icon as PNG to file"); LOG_ERROR(Frontend, "Could not write icon as PNG to file");

View file

@ -39,11 +39,7 @@ create_resource("../../dist/yuzu.bmp" "yuzu_cmd/yuzu_icon.h" "yuzu_icon")
target_include_directories(yuzu-cmd PRIVATE ${RESOURCES_DIR}) target_include_directories(yuzu-cmd PRIVATE ${RESOURCES_DIR})
target_link_libraries(yuzu-cmd PRIVATE SDL2::SDL2 Vulkan::Headers) target_link_libraries(yuzu-cmd PRIVATE SDL2::SDL2 Vulkan::Headers)
target_link_libraries(yuzu-cmd PRIVATE GPUOpen::VulkanMemoryAllocator)
# In Solaris needs explicit linking for ffmpeg which links to /lib/amd64/libX11.so
if (PLATFORM_SUN)
target_link_libraries(yuzu-cmd PRIVATE X11 "/usr/lib/xorg/amd64/libdrm.so")
endif()
if(UNIX AND NOT APPLE) if(UNIX AND NOT APPLE)
install(TARGETS yuzu-cmd) install(TARGETS yuzu-cmd)

10
tools/cpm-fetch-all.sh Executable file
View file

@ -0,0 +1,10 @@
#!/bin/bash -e
# SPDX-FileCopyrightText: 2025 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2025 crueter
# SPDX-License-Identifier: GPL-3.0-or-later
LIBS=$(find . externals externals/nx_tzdb src/yuzu/externals externals/ffmpeg src/dynarmic/externals -maxdepth 1 -name cpmfile.json -exec jq -j 'keys_unsorted | join(" ")' {} \; -printf " ")
tools/cpm-fetch.sh $LIBS

198
tools/cpm-fetch.sh Executable file
View file

@ -0,0 +1,198 @@
#!/bin/bash -e
# SPDX-FileCopyrightText: 2025 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2025 crueter
# SPDX-License-Identifier: GPL-3.0-or-later
[ -z "$CPM_SOURCE_CACHE" ] && CPM_SOURCE_CACHE=$PWD/.cache/cpm
mkdir -p $CPM_SOURCE_CACHE
ROOTDIR="$PWD"
TMP=$(mktemp -d)
download_package() {
FILENAME=$(basename "$DOWNLOAD")
OUTFILE="$TMP/$FILENAME"
LOWER_PACKAGE=$(tr '[:upper:]' '[:lower:]' <<< "$PACKAGE_NAME")
OUTDIR="${CPM_SOURCE_CACHE}/${LOWER_PACKAGE}/${KEY}"
[ -d "$OUTDIR" ] && return
curl "$DOWNLOAD" -sS -L -o "$OUTFILE"
ACTUAL_HASH=$(${HASH_ALGO}sum "$OUTFILE" | cut -d" " -f1)
[ "$ACTUAL_HASH" != "$HASH" ] && echo "$FILENAME did not match expected hash; expected $HASH but got $ACTUAL_HASH" && exit 1
mkdir -p "$OUTDIR"
pushd "$OUTDIR" > /dev/null
case "$FILENAME" in
(*.7z)
7z x "$OUTFILE" > /dev/null
;;
(*.tar*)
tar xf "$OUTFILE" > /dev/null
;;
(*.zip)
unzip "$OUTFILE" > /dev/null
;;
esac
# basically if only one real item exists at the top we just move everything from there
# since github and some vendors hate me
DIRS=$(find -maxdepth 1 -type d -o -type f)
# thanks gnu
if [ $(wc -l <<< "$DIRS") -eq 2 ]; then
SUBDIR=$(find . -maxdepth 1 -type d -not -name ".")
mv "$SUBDIR"/* .
mv "$SUBDIR"/.* . 2>/dev/null || true
rmdir "$SUBDIR"
fi
if grep -e "patches" <<< "$JSON" > /dev/null; then
PATCHES=$(jq -r '.patches | join(" ")' <<< "$JSON")
for patch in $PATCHES; do
patch -p1 < "$ROOTDIR"/.patch/$package/$patch
done
fi
popd > /dev/null
}
ci_package() {
REPO=$(jq -r ".repo" <<< "$JSON")
EXT=$(jq -r '.extension' <<< "$JSON")
[ "$EXT" == null ] && EXT="tar.zst"
VERSION=$(jq -r ".version" <<< "$JSON")
NAME=$(jq -r ".name | \"$package\"" <<< "$JSON")
PACKAGE=$(jq -r ".package | \"$package\"" <<< "$JSON")
# TODO(crueter)
# DISABLED=$(jq -j '.disabled_platforms | join(" ")' <<< "$JSON")
[ "$REPO" == null ] && echo "No repo defined for CI package $package" && return
echo "CI package $PACKAGE"
for platform in windows-amd64 windows-arm64 android solaris freebsd linux linux-aarch64; do
FILENAME="${NAME}-${platform}-${VERSION}.${EXT}"
DOWNLOAD="https://github.com/${REPO}/releases/download/v${VERSION}/${FILENAME}"
PACKAGE_NAME="$PACKAGE"
KEY=$platform
echo "- platform $KEY"
HASH_ALGO=$(jq -r ".hash_algo" <<< "$JSON")
[ "$HASH_ALGO" == null ] && HASH_ALGO=sha512
HASH_SUFFIX="${HASH_ALGO}sum"
HASH_URL="${DOWNLOAD}.${HASH_SUFFIX}"
HASH=$(curl "$HASH_URL" -sS -q -L -o -)
download_package
done
}
for package in $@
do
# prepare for cancer
JSON=$(find . externals src/yuzu/externals externals/ffmpeg src/dynarmic/externals externals/nx_tzdb -maxdepth 1 -name cpmfile.json -exec jq -r ".\"$package\" | select( . != null )" {} \;)
[ -z "$JSON" ] && echo "No cpmfile definition for $package" && continue
PACKAGE_NAME=$(jq -r ".package" <<< "$JSON")
[ "$PACKAGE_NAME" == null ] && PACKAGE_NAME="$package"
CI=$(jq -r ".ci" <<< "$JSON")
if [ "$CI" != null ]; then
ci_package
continue
fi
# url parsing WOOOHOOHOHOOHOHOH
URL=$(jq -r ".url" <<< "$JSON")
REPO=$(jq -r ".repo" <<< "$JSON")
SHA=$(jq -r ".sha" <<< "$JSON")
if [ "$URL" != "null" ]; then
DOWNLOAD="$URL"
elif [ "$REPO" != "null" ]; then
GIT_URL="https://github.com/$REPO"
TAG=$(jq -r ".tag" <<< "$JSON")
ARTIFACT=$(jq -r ".artifact" <<< "$JSON")
BRANCH=$(jq -r ".branch" <<< "$JSON")
if [ "$TAG" != "null" ]; then
if [ "$ARTIFACT" != "null" ]; then
DOWNLOAD="${GIT_URL}/releases/download/${TAG}/${ARTIFACT}"
else
DOWNLOAD="${GIT_URL}/archive/refs/tags/${TAG}.tar.gz"
fi
elif [ "$SHA" != "null" ]; then
DOWNLOAD="${GIT_URL}/archive/${SHA}.zip"
else
if [ "$BRANCH" == null ]; then
BRANCH=master
fi
DOWNLOAD="${GIT_URL}/archive/refs/heads/${BRANCH}.zip"
fi
else
echo "No repo or URL defined for $package"
continue
fi
# key parsing
KEY=$(jq -r ".key" <<< "$JSON")
if [ "$KEY" == null ]; then
VERSION=$(jq -r ".version" <<< "$JSON")
GIT_VERSION=$(jq -r ".git_version" <<< "$JSON")
if [ "$SHA" != null ]; then
KEY=$(cut -c1-4 - <<< "$SHA")
elif [ "$GIT_VERSION" != null ]; then
KEY="$GIT_VERSION"
elif [ "$VERSION" != null ]; then
KEY="$VERSION"
else
echo "No valid key could be determined for $package. Must define one of: key, sha, version, git_version"
continue
fi
fi
echo $KEY
echo "Downloading regular package $package, with key $KEY, from $DOWNLOAD"
# hash parsing
HASH_ALGO=$(jq -r ".hash_algo" <<< "$JSON")
[ "$HASH_ALGO" == null ] && HASH_ALGO=sha512
HASH=$(jq -r ".hash" <<< "$JSON")
if [ "$HASH" == null ]; then
HASH_SUFFIX="${HASH_ALGO}sum"
HASH_URL=$(jq -r ".hash_url" <<< "$JSON")
if [ "$HASH_URL" == null ]; then
HASH_URL="${DOWNLOAD}.${HASH_SUFFIX}"
fi
HASH=$(curl "$HASH_URL" -L -o -)
fi
download_package
done
rm -rf $TMP