Compare commits

...

17 Commits

Author SHA1 Message Date
wildcard 213a9a7813 [host_shaders] Store the value of results instead of discarding it
the function generated the value but we never stored it effectively discarding the result.
2026-02-04 22:13:11 +01:00
wildcard 48ba1f3f24 [vk, cmake] Bump minimum VulkanTools to 1.4.317 and add UnifiedImageLayouts (#3318)
It seems too easy, the specification does not state anything more to be done for it towork.
Requires performance testing on android.

Co-authored-by: DraVee <dravee@eden-emu.dev>
Co-authored-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3318
Reviewed-by: DraVee <dravee@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Co-authored-by: wildcard <wildcard@eden-emu.dev>
Co-committed-by: wildcard <wildcard@eden-emu.dev>
2026-02-04 19:15:10 +01:00
crueter d59fcf01bf [frontend] Generate web token at runtime (#3462)
Rather than having that hardcoded one like before. Also adds
infrastructure which should make it easier to setup defaults at runtime
(e.g. GPU stuff?)

Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3462
Reviewed-by: DraVee <dravee@eden-emu.dev>
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
2026-02-04 04:16:39 +01:00
PavelBARABANOV b8456394f1 [spirv] Fix F16 conversion on Android (skip NaN check) (#3458)
Fixes the yellow texture issue in Outer Wilds on Android.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3458
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: DraVee <dravee@eden-emu.dev>
Co-authored-by: PavelBARABANOV <pavelbarabanov94@gmail.com>
Co-committed-by: PavelBARABANOV <pavelbarabanov94@gmail.com>
2026-02-04 00:44:03 +01:00
wildcard a467dd1ba6 [vk] fix flickering caused by recent swizzle changes (#3457)
Co-authored-by: Maufeat <sahyno1996@gmail.com>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3457
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: DraVee <dravee@eden-emu.dev>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Co-authored-by: wildcard <wildcard@eden-emu.dev>
Co-committed-by: wildcard <wildcard@eden-emu.dev>
2026-02-04 00:37:58 +01:00
lizzie c156f4760f [meta] update codeowners (#3460)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3460
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2026-02-03 23:42:53 +01:00
MaranBr 13f11ebf49 [nvnflinger] Reimplement GetBufferHistory (#3394)
Reimplements GetBufferHistory, enabling tracking and retrieval of recent buffer states. This can improve rendering performance and stability in some games.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3394
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Co-authored-by: MaranBr <maranbr@outlook.com>
Co-committed-by: MaranBr <maranbr@outlook.com>
2026-02-03 18:25:15 +01:00
xbzk 6065e9aa09 [cmake] Reduce spam on builds for MSVC (#3454)
Fix for disable-warning-as-error flags set for MSVC not CLANG dripping into externals and causing override warnings on Ninja (cl)

Signed-off-by: xbzk <xbzk@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3454
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Co-authored-by: xbzk <xbzk@eden-emu.dev>
Co-committed-by: xbzk <xbzk@eden-emu.dev>
2026-02-03 13:53:07 +01:00
crueter 8ed0ed5828 [desktop] Add smooth scrolling and shift scrolling to game list (#3452)
Intercepts Qt's awful scroll wheel handling and passes it off to a
continuously self-integrating animation on the scroll bar positions.
Code kind of sucks right now and I think has a bit of a perf bottleneck,
but that's somewhat expected in the way I implemented it. Maybe look for
a better sln? QScroller?

Also while I was at it I added shift scrolling.

TODO:
- [ ] TouchGesture? With overshoot off
- [ ] Perf

Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3452
2026-02-02 19:07:12 +01:00
crueter 33067af283 [settings] Disable GPU logging by default (#3455)
Why was this enabled?

Signed-off-by: crueter <crueter@eden-emu.dev>
Co-authored-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3455
Reviewed-by: DraVee <dravee@eden-emu.dev>
2026-02-02 19:00:44 +01:00
crueter d0a054270e [cmake] Move back to individual handling of Vulkan deps (#3450)
VUL version mismatches are solved upstream. Anything else is user error,
not that the prior solution really helped let's be real.

Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3450
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: DraVee <dravee@eden-emu.dev>
2026-02-02 06:54:58 +01:00
crueter 2a3507c2b9 [cmake] Move Qt externals handling to root CMakeLists.txt, and move QuaZip to upstream (#3447)
QuaZip lets you disable QTextCodec now so we can use it.

Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3447
Reviewed-by: DraVee <dravee@eden-emu.dev>
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
2026-02-02 06:17:57 +01:00
crueter f71f43561d [externals] Update zlib, httplib, and catch2 (#3449)
whynot

Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3449
Reviewed-by: DraVee <dravee@eden-emu.dev>
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
2026-02-02 06:17:41 +01:00
crueter ecbfad4193 [externals] Update Boost to 1.90.0 (#3448)
Removes the need for those MSVC/ARM patches

Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3448
Reviewed-by: DraVee <dravee@eden-emu.dev>
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
2026-02-02 06:12:36 +01:00
nekle d76b2b5d26 [android] add toggle for quick settings to hide it and fix setting names (#3435)
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3435
Reviewed-by: DraVee <dravee@eden-emu.dev>
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Co-authored-by: nekle <nekle@protonmail.com>
Co-committed-by: nekle <nekle@protonmail.com>
2026-02-02 06:07:26 +01:00
crueter cd9527072d [frontend] fix nightly update checker again (#3446)
I ❤️ std::optional

Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3446
2026-02-01 22:27:49 +01:00
PavelBARABANOV ffdaf7369a [android] add qlaunch button (#3439)
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3439
Reviewed-by: DraVee <dravee@eden-emu.dev>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Co-authored-by: PavelBARABANOV <pavelbarabanov94@gmail.com>
Co-committed-by: PavelBARABANOV <pavelbarabanov94@gmail.com>
2026-02-01 22:03:35 +01:00
48 changed files with 884 additions and 424 deletions
-11
View File
@@ -1,11 +0,0 @@
--- a/libs/context/CMakeLists.txt 2025-09-08 00:42:31.303651800 -0400
+++ b/libs/context/CMakeLists.txt 2025-09-08 00:42:40.592184300 -0400
@@ -146,7 +146,7 @@
set(ASM_LANGUAGE ASM)
endif()
elseif(BOOST_CONTEXT_ASSEMBLER STREQUAL armasm)
- set(ASM_LANGUAGE ASM_ARMASM)
+ set(ASM_LANGUAGE ASM_MARMASM)
else()
set(ASM_LANGUAGE ASM_MASM)
endif()
-14
View File
@@ -1,14 +0,0 @@
diff --git a/libs/context/CMakeLists.txt b/libs/context/CMakeLists.txt
index 8210f65..0e59dd7 100644
--- a/libs/context/CMakeLists.txt
+++ b/libs/context/CMakeLists.txt
@@ -186,7 +186,8 @@ if(BOOST_CONTEXT_IMPLEMENTATION STREQUAL "fcontext")
set_property(SOURCE ${ASM_SOURCES} APPEND PROPERTY COMPILE_OPTIONS "/safeseh")
endif()
- else() # masm
+ # armasm doesn't support most of these options
+ elseif(NOT BOOST_CONTEXT_ASSEMBLER STREQUAL armasm) # masm
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set_property(SOURCE ${ASM_SOURCES} APPEND PROPERTY COMPILE_OPTIONS "-x" "assembler-with-cpp")
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
+23 -13
View File
@@ -1,52 +1,62 @@
From e1a946ffb79022d38351a0623f819a5419965c3e Mon Sep 17 00:00:00 2001 From 436fc1978c78edd085d99b33275b24be0ac96aa0 Mon Sep 17 00:00:00 2001
From: crueter <crueter@eden-emu.dev> From: crueter <crueter@eden-emu.dev>
Date: Fri, 24 Oct 2025 23:41:09 -0700 Date: Sun, 1 Feb 2026 16:21:10 -0500
Subject: [PATCH] [build] Fix MinGW missing GetAddrInfoExCancel definition Subject: [PATCH] Fix build on MinGW
MinGW does not define GetAddrInfoExCancel in its wstcpi whatever header, MinGW doesn't define GetAddrInfoExCancel.
so to get around this we can just load it with GetProcAddress et al.
Signed-off-by: crueter <crueter@eden-emu.dev> Signed-off-by: crueter <crueter@eden-emu.dev>
--- ---
httplib.h | 14 ++++++++++++-- httplib.h | 18 ++++++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-) 1 file changed, 16 insertions(+), 2 deletions(-)
diff --git a/httplib.h b/httplib.h diff --git a/httplib.h b/httplib.h
index e15ba44..90a76dc 100644 index ec8d2a2..5f9a510 100644
--- a/httplib.h --- a/httplib.h
+++ b/httplib.h +++ b/httplib.h
@@ -203,11 +203,13 @@ @@ -203,14 +203,17 @@
#error Sorry, Visual Studio versions prior to 2015 are not supported #error Sorry, Visual Studio versions prior to 2015 are not supported
#endif #endif
-#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "ws2_32.lib")
- -
#ifndef _SSIZE_T_DEFINED
using ssize_t = __int64; using ssize_t = __int64;
#define _SSIZE_T_DEFINED
#endif
#endif // _MSC_VER #endif // _MSC_VER
+#if defined(_MSC_VER) || defined(__MINGW32__) +#if defined(_MSC_VER) || defined(__MINGW32__)
+#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "ws2_32.lib")
+#endif +#endif
+
+ +
#ifndef S_ISREG #ifndef S_ISREG
#define S_ISREG(m) (((m) & S_IFREG) == S_IFREG) #define S_ISREG(m) (((m) & S_IFREG) == S_IFREG)
#endif // S_ISREG #endif // S_ISREG
@@ -3557,7 +3559,15 @@ inline int getaddrinfo_with_timeout(const char *node, const char *service, @@ -4528,7 +4531,17 @@ inline int getaddrinfo_with_timeout(const char *node, const char *service,
auto wait_result = auto wait_result =
::WaitForSingleObject(event, static_cast<DWORD>(timeout_sec * 1000)); ::WaitForSingleObject(event, static_cast<DWORD>(timeout_sec * 1000));
if (wait_result == WAIT_TIMEOUT) { if (wait_result == WAIT_TIMEOUT) {
+#ifdef __MINGW32__ +#ifdef __MINGW32__
+ typedef INT(WSAAPI * PFN_GETADDRINFOEXCANCEL)(HANDLE * CancelHandle); + typedef INT(WSAAPI * PFN_GETADDRINFOEXCANCEL)(HANDLE * CancelHandle);
+ auto wsdll = LoadLibraryW((wchar_t *)"ws2_32.lib"); + auto wsdll = LoadLibraryW((wchar_t *)"ws2_32.lib");
+ PFN_GETADDRINFOEXCANCEL GetAddrInfoExCancel = (PFN_GETADDRINFOEXCANCEL) GetProcAddress(wsdll, "GetAddrInfoExCancel"); + PFN_GETADDRINFOEXCANCEL GetAddrInfoExCancel =
+ (PFN_GETADDRINFOEXCANCEL)GetProcAddress(wsdll, "GetAddrInfoExCancel");
+ +
+ if (cancel_handle) { GetAddrInfoExCancel(&cancel_handle); } + if (cancel_handle) { GetAddrInfoExCancel(&cancel_handle); }
+#else +#else
if (cancel_handle) { ::GetAddrInfoExCancel(&cancel_handle); } if (cancel_handle) { ::GetAddrInfoExCancel(&cancel_handle); }
+#endif +#endif
+
::CloseHandle(event); ::CloseHandle(event);
return EAI_AGAIN; return EAI_AGAIN;
} }
-- @@ -13952,3 +13965,4 @@ inline SSL_CTX *Client::ssl_context() const {
2.51.0 } // namespace httplib
#endif // CPPHTTPLIB_HTTPLIB_H
+
--
2.51.2
+9 -1
View File
@@ -178,7 +178,9 @@ endif()
# Disable Warnings as Errors for MSVC # Disable Warnings as Errors for MSVC
if (MSVC AND NOT CXX_CLANG) if (MSVC AND NOT CXX_CLANG)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W3 /WX-") # This was dripping into spirv, being overriden, and causing cl flag override warning
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W3 /WX-")
set(CMAKE_CXX_FLAGS_INIT "${CMAKE_CXX_FLAGS_INIT} /W3 /WX-")
endif() endif()
# Set bundled sdl2/qt as dependent options. # Set bundled sdl2/qt as dependent options.
@@ -587,6 +589,7 @@ if (NOT YUZU_STATIC_ROOM)
find_package(sirit) find_package(sirit)
find_package(gamemode) find_package(gamemode)
find_package(mcl) find_package(mcl)
find_package(frozen)
if (ARCHITECTURE_riscv64) if (ARCHITECTURE_riscv64)
find_package(biscuit) find_package(biscuit)
@@ -693,6 +696,11 @@ if (ENABLE_QT)
set(QT_MAJOR_VERSION 6) set(QT_MAJOR_VERSION 6)
# Qt6 sets cxx_std_17 and we need to undo that # Qt6 sets cxx_std_17 and we need to undo that
set_target_properties(Qt6::Platform PROPERTIES INTERFACE_COMPILE_FEATURES "") set_target_properties(Qt6::Platform PROPERTIES INTERFACE_COMPILE_FEATURES "")
## Qt Externals ##
# QuaZip
AddJsonPackage(quazip)
endif() endif()
if (NOT YUZU_STATIC_ROOM AND NOT (YUZU_USE_BUNDLED_FFMPEG OR YUZU_USE_EXTERNAL_FFMPEG)) if (NOT YUZU_STATIC_ROOM AND NOT (YUZU_USE_BUNDLED_FFMPEG OR YUZU_USE_EXTERNAL_FFMPEG))
+129 -62
View File
@@ -41,6 +41,11 @@ function(cpm_utils_message level name message)
message(${level} "[CPMUtil] ${name}: ${message}") message(${level} "[CPMUtil] ${name}: ${message}")
endfunction() endfunction()
# propagate a variable to parent scope
macro(Propagate var)
set(${var} ${${var}} PARENT_SCOPE)
endmacro()
function(array_to_list array length out) function(array_to_list array length out)
math(EXPR range "${length} - 1") math(EXPR range "${length} - 1")
@@ -72,45 +77,68 @@ function(get_json_element object out member default)
set("${out}" "${outvar}" PARENT_SCOPE) set("${out}" "${outvar}" PARENT_SCOPE)
endfunction() endfunction()
# The preferred usage # Determine whether or not a package has a viable system candidate.
function(AddJsonPackage) function(SystemPackageViable JSON_NAME)
set(oneValueArgs string(JSON object GET "${CPMFILE_CONTENT}" "${JSON_NAME}")
NAME
# these are overrides that can be generated at runtime, parse_object(${object})
# so can be defined separately from the json
DOWNLOAD_ONLY
BUNDLED_PACKAGE)
set(multiValueArgs OPTIONS) string(REPLACE " " ";" find_args "${find_args}")
find_package(${package} ${version} ${find_args} QUIET NO_POLICY_SCOPE)
cmake_parse_arguments(JSON "" "${oneValueArgs}" "${multiValueArgs}" set(${pkg}_VIABLE ${${package}_FOUND} PARENT_SCOPE)
"${ARGN}") set(${pkg}_PACKAGE ${package} PARENT_SCOPE)
endfunction()
list(LENGTH ARGN argnLength) # Add several packages such that if one is bundled,
# all the rest must also be bundled.
function(AddDependentPackages)
set(_some_system OFF)
set(_some_bundled OFF)
# single name argument foreach(pkg ${ARGN})
if(argnLength EQUAL 1) SystemPackageViable(${pkg})
set(JSON_NAME "${ARGV0}")
if (${pkg}_VIABLE)
set(_some_system ON)
list(APPEND _system_pkgs ${${pkg}_PACKAGE})
else()
set(_some_bundled ON)
list(APPEND _bundled_pkgs ${${pkg}_PACKAGE})
endif()
endforeach()
if (_some_system AND _some_bundled)
foreach(pkg ${ARGN})
list(APPEND package_names ${${pkg}_PACKAGE})
endforeach()
string(REPLACE ";" ", " package_names "${package_names}")
string(REPLACE ";" ", " bundled_names "${_bundled_pkgs}")
foreach(sys ${_system_pkgs})
list(APPEND system_names ${sys}_FORCE_BUNDLED)
endforeach()
string(REPLACE ";" ", " system_names "${system_names}")
message(FATAL_ERROR "Partial dependency installation detected "
"for the following packages:\n${package_names}\n"
"You can solve this in one of two ways:\n"
"1. Install the following packages to your system if available:"
"\n\t${bundled_names}\n"
"2. Set the following variables to ON:"
"\n\t${system_names}\n"
"This may also be caused by a version mismatch, "
"such as one package being newer than the other.")
endif() endif()
if(NOT DEFINED CPMFILE_CONTENT) foreach(pkg ${ARGN})
cpm_utils_message(WARNING ${name} AddJsonPackage(${pkg})
"No cpmfile, AddJsonPackage is a no-op") endforeach()
return() endfunction()
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()
# json util
macro(parse_object object)
get_json_element("${object}" package package ${JSON_NAME}) get_json_element("${object}" package package ${JSON_NAME})
get_json_element("${object}" repo repo "") get_json_element("${object}" repo repo "")
get_json_element("${object}" ci ci OFF) get_json_element("${object}" ci ci OFF)
@@ -128,27 +156,7 @@ function(AddJsonPackage)
else() else()
set(disabled_platforms "") set(disabled_platforms "")
endif() endif()
else()
AddCIPackage(
VERSION ${version}
NAME ${name}
REPO ${repo}
PACKAGE ${package}
EXTENSION ${extension}
MIN_VERSION ${min_version}
DISABLED_PLATFORMS ${disabled_platforms})
# 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)
return()
endif()
get_json_element("${object}" hash hash "") get_json_element("${object}" hash hash "")
get_json_element("${object}" hash_suffix hash_suffix "") get_json_element("${object}" hash_suffix hash_suffix "")
get_json_element("${object}" sha sha "") get_json_element("${object}" sha sha "")
@@ -181,7 +189,8 @@ function(AddJsonPackage)
endif() endif()
if(artifact) if(artifact)
string(REPLACE "%VERSION%" "${version_replace}" artifact ${artifact}) string(REPLACE "%VERSION%" "${version_replace}"
artifact ${artifact})
string(REPLACE "%TAG%" "${tag}" artifact ${artifact}) string(REPLACE "%TAG%" "${tag}" artifact ${artifact})
endif() endif()
@@ -218,6 +227,65 @@ function(AddJsonPackage)
if(bundled STREQUAL "unset" AND DEFINED JSON_BUNDLED_PACKAGE) if(bundled STREQUAL "unset" AND DEFINED JSON_BUNDLED_PACKAGE)
set(bundled ${JSON_BUNDLED_PACKAGE}) set(bundled ${JSON_BUNDLED_PACKAGE})
endif() endif()
endif()
endmacro()
# The preferred usage
function(AddJsonPackage)
set(oneValueArgs
NAME
# these are overrides that can be generated at runtime,
# so can be defined separately from the json
DOWNLOAD_ONLY
BUNDLED_PACKAGE
FORCE_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()
parse_object(${object})
if(ci)
AddCIPackage(
VERSION ${version}
NAME ${name}
REPO ${repo}
PACKAGE ${package}
EXTENSION ${extension}
MIN_VERSION ${min_version}
DISABLED_PLATFORMS ${disabled_platforms})
else()
if (NOT DEFINED JSON_FORCE_BUNDLED_PACKAGE)
set(JSON_FORCE_BUNDLED_PACKAGE OFF)
endif()
AddPackage( AddPackage(
NAME "${package}" NAME "${package}"
@@ -232,6 +300,7 @@ function(AddJsonPackage)
OPTIONS "${options}" OPTIONS "${options}"
FIND_PACKAGE_ARGUMENTS "${find_args}" FIND_PACKAGE_ARGUMENTS "${find_args}"
BUNDLED_PACKAGE "${bundled}" BUNDLED_PACKAGE "${bundled}"
FORCE_BUNDLED_PACKAGE "${JSON_FORCE_BUNDLED_PACKAGE}"
SOURCE_SUBDIR "${source_subdir}" SOURCE_SUBDIR "${source_subdir}"
GIT_VERSION ${git_version} GIT_VERSION ${git_version}
@@ -239,15 +308,12 @@ function(AddJsonPackage)
ARTIFACT ${artifact} ARTIFACT ${artifact}
TAG ${tag}) TAG ${tag})
endif()
# pass stuff to parent scope # pass stuff to parent scope
set(${package}_ADDED "${${package}_ADDED}" Propagate(${package}_ADDED)
PARENT_SCOPE) Propagate(${package}_SOURCE_DIR)
set(${package}_SOURCE_DIR "${${package}_SOURCE_DIR}" Propagate(${package}_BINARY_DIR)
PARENT_SCOPE)
set(${package}_BINARY_DIR "${${package}_BINARY_DIR}"
PARENT_SCOPE)
endfunction() endfunction()
function(AddPackage) function(AddPackage)
@@ -343,7 +409,7 @@ function(AddPackage)
if(DEFINED PKG_ARGS_ARTIFACT) if(DEFINED PKG_ARGS_ARTIFACT)
set(pkg_url set(pkg_url
${pkg_git_url}/releases/download/${PKG_ARGS_TAG}/${PKG_ARGS_ARTIFACT}) "${pkg_git_url}/releases/download/${PKG_ARGS_TAG}/${PKG_ARGS_ARTIFACT}")
else() else()
set(pkg_url set(pkg_url
${pkg_git_url}/archive/refs/tags/${PKG_ARGS_TAG}.tar.gz) ${pkg_git_url}/archive/refs/tags/${PKG_ARGS_TAG}.tar.gz)
@@ -625,7 +691,8 @@ function(AddCIPackage)
endif() endif()
if (DEFINED pkgname AND NOT "${pkgname}" IN_LIST DISABLED_PLATFORMS) if (DEFINED pkgname AND NOT "${pkgname}" IN_LIST DISABLED_PLATFORMS)
set(ARTIFACT "${ARTIFACT_NAME}-${pkgname}-${ARTIFACT_VERSION}.${ARTIFACT_EXT}") set(ARTIFACT
"${ARTIFACT_NAME}-${pkgname}-${ARTIFACT_VERSION}.${ARTIFACT_EXT}")
AddPackage( AddPackage(
NAME ${ARTIFACT_PACKAGE} NAME ${ARTIFACT_PACKAGE}
+18 -7
View File
@@ -12,14 +12,12 @@
"repo": "boostorg/boost", "repo": "boostorg/boost",
"tag": "boost-%VERSION%", "tag": "boost-%VERSION%",
"artifact": "%TAG%-cmake.tar.xz", "artifact": "%TAG%-cmake.tar.xz",
"hash": "4fb7f6fde92762305aad8754d7643cd918dd1f3f67e104e9ab385b18c73178d72a17321354eb203b790b6702f2cf6d725a5d6e2dfbc63b1e35f9eb59fb42ece9", "hash": "6ae6e94664fe7f2fb01976b59b276ac5df8085c7503fa829d810fbfe495960cfec44fa2c36e2cb23480bc19c956ed199d4952b02639a00a6c07625d4e7130c2d",
"git_version": "1.89.0", "git_version": "1.90.0",
"version": "1.57", "version": "1.57",
"find_args": "CONFIG OPTIONAL_COMPONENTS headers context system fiber filesystem", "find_args": "CONFIG OPTIONAL_COMPONENTS headers context system fiber filesystem",
"patches": [ "patches": [
"0001-clang-cl.patch", "0001-clang-cl.patch"
"0002-use-marmasm.patch",
"0003-armasm-options.patch"
] ]
}, },
"fmt": { "fmt": {
@@ -48,9 +46,9 @@
"package": "ZLIB", "package": "ZLIB",
"repo": "madler/zlib", "repo": "madler/zlib",
"tag": "v%VERSION%", "tag": "v%VERSION%",
"hash": "8c9642495bafd6fad4ab9fb67f09b268c69ff9af0f4f20cf15dfc18852ff1f312bd8ca41de761b3f8d8e90e77d79f2ccacd3d4c5b19e475ecf09d021fdfe9088", "hash": "06eaa3a1eaaeb31f461a2283b03a91ed8eb2406e62cd97ea1c69836324909edeecd93edd03ff0bf593d9dde223e3376149134c5b1fe2e8688c258cadf8cd60ff",
"version": "1.2", "version": "1.2",
"git_version": "1.3.1", "git_version": "1.3.1.2",
"options": [ "options": [
"ZLIB_BUILD_SHARED OFF", "ZLIB_BUILD_SHARED OFF",
"ZLIB_INSTALL OFF" "ZLIB_INSTALL OFF"
@@ -103,5 +101,18 @@
"git_version": "1.4.335.0", "git_version": "1.4.335.0",
"artifact": "android-binaries-%VERSION%.zip", "artifact": "android-binaries-%VERSION%.zip",
"hash": "48167c4a17736301bd08f9290f41830443e1f18cce8ad867fc6f289b49e18b40e93c9850b377951af82f51b5b6d7313aa6a884fc5df79f5ce3df82696c1c1244" "hash": "48167c4a17736301bd08f9290f41830443e1f18cce8ad867fc6f289b49e18b40e93c9850b377951af82f51b5b6d7313aa6a884fc5df79f5ce3df82696c1c1244"
},
"quazip": {
"package": "QuaZip-Qt6",
"repo": "stachenov/quazip",
"sha": "2e95c9001b",
"hash": "609c240c7f029ac26a37d8fbab51bc16284e05e128b78b9b9c0e95d083538c36047a67d682759ac990e4adb0eeb90f04f1ea7fe2253bbda7e7e3bcce32e53dd8",
"version": "1.3",
"git_version": "1.5",
"options": [
"QUAZIP_QT_MAJOR_VERSION 6",
"QUAZIP_INSTALL OFF",
"QUAZIP_ENABLE_QTEXTCODEC OFF"
]
} }
} }
+1 -1
View File
@@ -18,7 +18,7 @@
src/web_service @AleksandrPopovich src/web_service @AleksandrPopovich
src/dynarmic @Lizzie src/dynarmic @Lizzie
src/core @Lizzie @Maufeat @PavelBARABANOV @MrPurple666 @JPikachu src/core @Lizzie @Maufeat @PavelBARABANOV @MrPurple666 @JPikachu
src/core/hle @Maufeat @PavelBARABANOV @SDK-Chan src/core/hle @Maufeat @PavelBARABANOV
src/core/arm @Lizzie @MrPurple666 src/core/arm @Lizzie @MrPurple666
src/*_room @AleksandrPopovich src/*_room @AleksandrPopovich
src/video_core @CamilleLaVey @MaranBr @Wildcard @weakboson src/video_core @CamilleLaVey @MaranBr @Wildcard @weakboson
+41
View File
@@ -0,0 +1,41 @@
# AddDependentPackage
Use `AddDependentPackage` when you have multiple packages that are required to all be from the system, OR bundled. This is useful in cases where e.g. versions must absolutely match.
## Versioning
Versioning must be handled by the package itself.
## Examples
### Vulkan
`cpmfile.json`
```json
{
"vulkan-headers": {
"repo": "KhronosGroup/Vulkan-Headers",
"package": "VulkanHeaders",
"version": "1.4.317",
"hash": "26e0ad8fa34ab65a91ca62ddc54cc4410d209a94f64f2817dcdb8061dc621539a4262eab6387e9b9aa421db3dbf2cf8e2a4b041b696d0d03746bae1f25191272",
"git_version": "1.4.342",
"tag": "v%VERSION%"
},
"vulkan-utility-libraries": {
"repo": "KhronosGroup/Vulkan-Utility-Libraries",
"package": "VulkanUtilityLibraries",
"hash": "8147370f964fd82c315d6bb89adeda30186098427bf3efaa641d36282d42a263f31e96e4586bfd7ae0410ff015379c19aa4512ba160630444d3d8553afd1ec14",
"git_version": "1.4.342",
"tag": "v%VERSION%"
}
}
```
`CMakeLists.txt`:
```cmake
AddDependentPackages(vulkan-headers vulkan-utility-libraries)
```
If Vulkan Headers are installed, but NOT Vulkan Utility Libraries, then CPMUtil will throw an error.
+4
View File
@@ -31,6 +31,10 @@ The core of CPMUtil is the [`AddPackage`](./AddPackage.md) function. [`AddPackag
[`AddJsonPackage`](./AddJsonPackage.md) is the recommended method of usage for CPMUtil. [`AddJsonPackage`](./AddJsonPackage.md) is the recommended method of usage for CPMUtil.
## AddDependentPackage
[`AddDependentPackage`](./AddDependentPackage.md) allows you to add multiple packages such that all of them must be from the system OR bundled.
## AddQt ## AddQt
[`AddQt`](./AddQt.md) adds a specific version of Qt to your project. [`AddQt`](./AddQt.md) adds a specific version of Qt to your project.
+4 -6
View File
@@ -83,13 +83,11 @@ endif()
# mcl # mcl
AddJsonPackage(mcl) AddJsonPackage(mcl)
# VulkanUtilityHeaders - pulls in headers and utility libs # Vulkan stuff
AddJsonPackage(vulkan-utility-headers) AddDependentPackages(vulkan-headers vulkan-utility-libraries)
# small hack # frozen
if (NOT VulkanUtilityLibraries_ADDED) AddJsonPackage(frozen)
find_package(VulkanHeaders 1.3.274 REQUIRED)
endif()
# DiscordRPC # DiscordRPC
if (USE_DISCORD_PRESENCE) if (USE_DISCORD_PRESENCE)
+25 -13
View File
@@ -28,8 +28,8 @@
"httplib": { "httplib": {
"repo": "yhirose/cpp-httplib", "repo": "yhirose/cpp-httplib",
"tag": "v%VERSION%", "tag": "v%VERSION%",
"hash": "e7a8877d489c97669a8ee536e1498575be921e558ed947253013fe6b67a49d4569eedd01f543caa70183b92d8ac0e8687d662a70d880954412e387317008a239", "hash": "a229e24cca4afe78e5c0aa2e482f15108ac34101fd8dbd927365f15e8c37dec4de38c5277d635017d692a5b320e1b929f8bfcc076f52b8e4dcdab8fe53bfdf2e",
"git_version": "0.28.0", "git_version": "0.30.1",
"find_args": "MODULE GLOBAL", "find_args": "MODULE GLOBAL",
"patches": [ "patches": [
"0001-mingw.patch" "0001-mingw.patch"
@@ -118,15 +118,6 @@
"git_version": "1.3.18", "git_version": "1.3.18",
"find_args": "MODULE" "find_args": "MODULE"
}, },
"vulkan-utility-headers": {
"package": "VulkanUtilityLibraries",
"repo": "scripts/VulkanUtilityHeaders",
"tag": "%VERSION%",
"git_version": "1.4.335",
"artifact": "VulkanUtilityHeaders.tar.zst",
"git_host": "git.crueter.xyz",
"hash": "16dac0e6586702580c4279e4cd37ffe3cf909c93eb31b5069da7af36436d47b270a9cbaac953bb66c22ed12ed67ffa096688599267f307dfb62be1bc09f79833"
},
"spirv-tools": { "spirv-tools": {
"package": "SPIRV-Tools", "package": "SPIRV-Tools",
"repo": "KhronosGroup/SPIRV-Tools", "repo": "KhronosGroup/SPIRV-Tools",
@@ -174,9 +165,9 @@
"package": "Catch2", "package": "Catch2",
"repo": "catchorg/Catch2", "repo": "catchorg/Catch2",
"tag": "v%VERSION%", "tag": "v%VERSION%",
"hash": "a95495142f915d6e9c2a23e80fe360343e9097680066a2f9d3037a070ba5f81ee5559a0407cc9e972dc2afae325873f1fc7ea07a64012c0f01aac6e549f03e3f", "hash": "acb3f463a7404d6a3bce52e474075cdadf9bb241d93feaf147c182d756e5a2f8bd412f4658ca186d15ab8fed36fc587d79ec311f55642d8e4ded16df9e213656",
"version": "3.0.1", "version": "3.0.1",
"git_version": "3.11.0", "git_version": "3.12.0",
"patches": [ "patches": [
"0001-solaris-isnan-fix.patch" "0001-solaris-isnan-fix.patch"
] ]
@@ -281,5 +272,26 @@
"tag": "%VERSION%", "tag": "%VERSION%",
"hash": "dc37a189a44ce8b5c988ca550582431a6c7eadfd3c6e709bee6277116ee803e714333e85c9e6cbb5c69346a14d6f2cc7ed96e8aa09cc5fb8a89f945059651db6", "hash": "dc37a189a44ce8b5c988ca550582431a6c7eadfd3c6e709bee6277116ee803e714333e85c9e6cbb5c69346a14d6f2cc7ed96e8aa09cc5fb8a89f945059651db6",
"version": "121125" "version": "121125"
},
"vulkan-headers": {
"repo": "KhronosGroup/Vulkan-Headers",
"package": "VulkanHeaders",
"version": "1.4.317",
"hash": "26e0ad8fa34ab65a91ca62ddc54cc4410d209a94f64f2817dcdb8061dc621539a4262eab6387e9b9aa421db3dbf2cf8e2a4b041b696d0d03746bae1f25191272",
"git_version": "1.4.342",
"tag": "v%VERSION%"
},
"vulkan-utility-libraries": {
"repo": "KhronosGroup/Vulkan-Utility-Libraries",
"package": "VulkanUtilityLibraries",
"hash": "8147370f964fd82c315d6bb89adeda30186098427bf3efaa641d36282d42a263f31e96e4586bfd7ae0410ff015379c19aa4512ba160630444d3d8553afd1ec14",
"git_version": "1.4.342",
"tag": "v%VERSION%"
},
"frozen": {
"package": "frozen",
"repo": "serge-sans-paille/frozen",
"sha": "61dce5ae18",
"hash": "b8dfe741c82bc178dfc9749d4ab5a130cee718d9ee7b71d9b547cf5f7f23027ed0152ad250012a8546399fcc1e12187efc68d89d6731256c4d2df7d04eef8d5c"
} }
} }
@@ -22,14 +22,6 @@ import org.yuzu.yuzu_emu.features.settings.model.AbstractShortSetting
import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
class QuickSettings(val emulationFragment: EmulationFragment) { class QuickSettings(val emulationFragment: EmulationFragment) {
// Kinda a crappy workaround to get a title from setting keys
// Idk how to do this witthout hardcoding every single one
private fun getSettingTitle(settingKey: String): String {
return settingKey.replace("_", " ").split(" ")
.joinToString(" ") { it.replaceFirstChar { c -> c.uppercase() } }
}
private fun saveSettings() { private fun saveSettings() {
if (emulationFragment.shouldUseCustom) { if (emulationFragment.shouldUseCustom) {
NativeConfig.savePerGameConfig() NativeConfig.savePerGameConfig()
@@ -60,6 +52,7 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
// settings // settings
fun addIntSetting( fun addIntSetting(
name: Int,
container: ViewGroup, container: ViewGroup,
setting: IntSetting, setting: IntSetting,
namesArrayId: Int, namesArrayId: Int,
@@ -73,7 +66,7 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
val expandIcon = itemView.findViewById<android.widget.ImageView>(R.id.expand_icon) val expandIcon = itemView.findViewById<android.widget.ImageView>(R.id.expand_icon)
val radioGroup = itemView.findViewById<RadioGroup>(R.id.radio_group) val radioGroup = itemView.findViewById<RadioGroup>(R.id.radio_group)
titleView.text = getSettingTitle(setting.key) titleView.text = YuzuApplication.appContext.getString(name)
val names = emulationFragment.resources.getStringArray(namesArrayId) val names = emulationFragment.resources.getStringArray(namesArrayId)
val values = emulationFragment.resources.getIntArray(valuesArrayId) val values = emulationFragment.resources.getIntArray(valuesArrayId)
@@ -115,6 +108,8 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
} }
fun addBooleanSetting( fun addBooleanSetting(
name: Int,
container: ViewGroup, container: ViewGroup,
setting: BooleanSetting setting: BooleanSetting
) { ) {
@@ -125,7 +120,7 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
val titleView = itemView.findViewById<TextView>(R.id.switch_title) val titleView = itemView.findViewById<TextView>(R.id.switch_title)
val switchView = itemView.findViewById<com.google.android.material.materialswitch.MaterialSwitch>(R.id.setting_switch) val switchView = itemView.findViewById<com.google.android.material.materialswitch.MaterialSwitch>(R.id.setting_switch)
titleView.text = getSettingTitle(setting.key) titleView.text = YuzuApplication.appContext.getString(name)
switchContainer.visibility = View.VISIBLE switchContainer.visibility = View.VISIBLE
switchView.isChecked = setting.getBoolean() switchView.isChecked = setting.getBoolean()
@@ -141,6 +136,7 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
} }
fun addSliderSetting( fun addSliderSetting(
name: Int,
container: ViewGroup, container: ViewGroup,
setting: AbstractSetting, setting: AbstractSetting,
minValue: Int = 0, minValue: Int = 0,
@@ -156,7 +152,7 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
val slider = itemView.findViewById<com.google.android.material.slider.Slider>(R.id.setting_slider) val slider = itemView.findViewById<com.google.android.material.slider.Slider>(R.id.setting_slider)
titleView.text = getSettingTitle(setting.key) titleView.text = YuzuApplication.appContext.getString(name)
sliderContainer.visibility = View.VISIBLE sliderContainer.visibility = View.VISIBLE
slider.valueFrom = minValue.toFloat() slider.valueFrom = minValue.toFloat()
@@ -24,6 +24,7 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
RENDERER_FORCE_MAX_CLOCK("force_max_clock"), RENDERER_FORCE_MAX_CLOCK("force_max_clock"),
RENDERER_ASYNCHRONOUS_SHADERS("use_asynchronous_shaders"), RENDERER_ASYNCHRONOUS_SHADERS("use_asynchronous_shaders"),
RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"), RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"),
ENABLE_BUFFER_HISTORY("enable_buffer_history"),
SYNC_MEMORY_OPERATIONS("sync_memory_operations"), SYNC_MEMORY_OPERATIONS("sync_memory_operations"),
BUFFER_REORDER_DISABLE("disable_buffer_reorder"), BUFFER_REORDER_DISABLE("disable_buffer_reorder"),
RENDERER_DEBUG("debug"), RENDERER_DEBUG("debug"),
@@ -36,6 +37,9 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
USE_CUSTOM_RTC("custom_rtc_enabled"), USE_CUSTOM_RTC("custom_rtc_enabled"),
BLACK_BACKGROUNDS("black_backgrounds"), BLACK_BACKGROUNDS("black_backgrounds"),
ENABLE_FOLDER_BUTTON("enable_folder_button"),
ENABLE_QLAUNCH_BUTTON("enable_qlaunch_button"),
ENABLE_UPDATE_CHECKS("enable_update_checks"), ENABLE_UPDATE_CHECKS("enable_update_checks"),
JOYSTICK_REL_CENTER("joystick_rel_center"), JOYSTICK_REL_CENTER("joystick_rel_center"),
DPAD_SLIDE("dpad_slide"), DPAD_SLIDE("dpad_slide"),
@@ -79,8 +83,9 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
GPU_LOG_VULKAN_CALLS("gpu_log_vulkan_calls"), GPU_LOG_VULKAN_CALLS("gpu_log_vulkan_calls"),
GPU_LOG_SHADER_DUMPS("gpu_log_shader_dumps"), GPU_LOG_SHADER_DUMPS("gpu_log_shader_dumps"),
GPU_LOG_MEMORY_TRACKING("gpu_log_memory_tracking"), GPU_LOG_MEMORY_TRACKING("gpu_log_memory_tracking"),
GPU_LOG_DRIVER_DEBUG("gpu_log_driver_debug"); GPU_LOG_DRIVER_DEBUG("gpu_log_driver_debug"),
ENABLE_QUICK_SETTINGS("enable_quick_settings");
// external fun isFrameSkippingEnabled(): Boolean // external fun isFrameSkippingEnabled(): Boolean
external fun isFrameInterpolationEnabled(): Boolean external fun isFrameInterpolationEnabled(): Boolean
@@ -60,6 +60,11 @@ abstract class SettingsItem(
return NativeInput.getStyleIndex(0) != NpadStyleIndex.Handheld return NativeInput.getStyleIndex(0) != NpadStyleIndex.Handheld
} }
// Can't edit enable_qlaunch_button if firmware is not available
if (setting.key == BooleanSetting.ENABLE_QLAUNCH_BUTTON.key) {
return NativeLibrary.isFirmwareAvailable()
}
// Can't edit settings that aren't saveable in per-game config even if they are switchable // Can't edit settings that aren't saveable in per-game config even if they are switchable
if (NativeConfig.isPerGameConfigLoaded() && !setting.isSaveable) { if (NativeConfig.isPerGameConfigLoaded() && !setting.isSaveable) {
return false return false
@@ -740,6 +745,13 @@ abstract class SettingsItem(
descriptionId = R.string.renderer_reactive_flushing_description descriptionId = R.string.renderer_reactive_flushing_description
) )
) )
put(
SwitchSetting(
BooleanSetting.ENABLE_BUFFER_HISTORY,
titleId = R.string.enable_buffer_history,
descriptionId = R.string.enable_buffer_history_description
)
)
put( put(
SwitchSetting( SwitchSetting(
BooleanSetting.SYNC_MEMORY_OPERATIONS, BooleanSetting.SYNC_MEMORY_OPERATIONS,
@@ -794,6 +806,27 @@ abstract class SettingsItem(
descriptionId = R.string.enable_update_checks_description, descriptionId = R.string.enable_update_checks_description,
) )
) )
put(
SwitchSetting(
BooleanSetting.ENABLE_QUICK_SETTINGS,
titleId = R.string.enable_quick_settings,
descriptionId = R.string.enable_quick_settings_description,
)
)
put(
SwitchSetting(
BooleanSetting.ENABLE_FOLDER_BUTTON,
titleId = R.string.enable_folder_button,
descriptionId = R.string.enable_folder_button_description,
)
)
put(
SwitchSetting(
BooleanSetting.ENABLE_QLAUNCH_BUTTON,
titleId = R.string.enable_qlaunch_button,
descriptionId = R.string.enable_qlaunch_button_description,
)
)
put( put(
SingleChoiceSetting( SingleChoiceSetting(
IntSetting.APP_LANGUAGE, IntSetting.APP_LANGUAGE,
@@ -275,6 +275,7 @@ class SettingsFragmentPresenter(
add(BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE.key) add(BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE.key)
add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key) add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key)
add(BooleanSetting.RENDERER_REACTIVE_FLUSHING.key) add(BooleanSetting.RENDERER_REACTIVE_FLUSHING.key)
add(BooleanSetting.ENABLE_BUFFER_HISTORY.key)
add(HeaderSetting(R.string.hacks)) add(HeaderSetting(R.string.hacks))
@@ -1074,6 +1075,8 @@ class SettingsFragmentPresenter(
add(BooleanSetting.ENABLE_UPDATE_CHECKS.key) add(BooleanSetting.ENABLE_UPDATE_CHECKS.key)
} }
add(BooleanSetting.ENABLE_QUICK_SETTINGS.key)
add(HeaderSetting(R.string.theme_and_color)) add(HeaderSetting(R.string.theme_and_color))
@@ -1191,6 +1194,13 @@ class SettingsFragmentPresenter(
descriptionId = R.string.use_black_backgrounds_description descriptionId = R.string.use_black_backgrounds_description
) )
) )
add(HeaderSetting(R.string.buttons))
add(BooleanSetting.ENABLE_FOLDER_BUTTON.key)
add(BooleanSetting.ENABLE_QLAUNCH_BUTTON.key)
if (!NativeLibrary.isFirmwareAvailable()) {
BooleanSetting.ENABLE_QLAUNCH_BUTTON.setBoolean(false)
}
} }
} }
@@ -690,8 +690,18 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}) })
binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED) binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
if (!BooleanSetting.ENABLE_QUICK_SETTINGS.getBoolean()) {
binding.drawerLayout.setDrawerLockMode(
DrawerLayout.LOCK_MODE_LOCKED_CLOSED,
binding.quickSettingsSheet
)
}
updateGameTitle() updateGameTitle()
binding.inGameMenu.menu.findItem(R.id.menu_quick_settings)?.isVisible =
BooleanSetting.ENABLE_QUICK_SETTINGS.getBoolean()
binding.inGameMenu.menu.findItem(R.id.menu_lock_drawer).apply { binding.inGameMenu.menu.findItem(R.id.menu_lock_drawer).apply {
val lockMode = IntSetting.LOCK_DRAWER.getInt() val lockMode = IntSetting.LOCK_DRAWER.getInt()
val titleId = if (lockMode == DrawerLayout.LOCK_MODE_LOCKED_CLOSED) { val titleId = if (lockMode == DrawerLayout.LOCK_MODE_LOCKED_CLOSED) {
@@ -749,7 +759,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
true true
} }
R.id.menu_quick_settings -> { if (BooleanSetting.ENABLE_QUICK_SETTINGS.getBoolean())
R.id.menu_quick_settings else 0 -> {
openQuickSettingsMenu() openQuickSettingsMenu()
true true
} }
@@ -1045,11 +1056,13 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
} }
quickSettings.addBooleanSetting( quickSettings.addBooleanSetting(
R.string.frame_limit_enable,
container, container,
BooleanSetting.RENDERER_USE_SPEED_LIMIT, BooleanSetting.RENDERER_USE_SPEED_LIMIT,
) )
quickSettings.addSliderSetting( quickSettings.addSliderSetting(
R.string.frame_limit_slider,
container, container,
ShortSetting.RENDERER_SPEED_LIMIT, ShortSetting.RENDERER_SPEED_LIMIT,
minValue = 0, minValue = 0,
@@ -1058,6 +1071,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
) )
quickSettings.addBooleanSetting( quickSettings.addBooleanSetting(
R.string.use_docked_mode,
container, container,
BooleanSetting.USE_DOCKED_MODE, BooleanSetting.USE_DOCKED_MODE,
) )
@@ -1065,6 +1079,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
quickSettings.addDivider(container) quickSettings.addDivider(container)
quickSettings.addIntSetting( quickSettings.addIntSetting(
R.string.renderer_accuracy,
container, container,
IntSetting.RENDERER_ACCURACY, IntSetting.RENDERER_ACCURACY,
R.array.rendererAccuracyNames, R.array.rendererAccuracyNames,
@@ -1073,6 +1088,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
quickSettings.addIntSetting( quickSettings.addIntSetting(
R.string.renderer_scaling_filter,
container, container,
IntSetting.RENDERER_SCALING_FILTER, IntSetting.RENDERER_SCALING_FILTER,
R.array.rendererScalingFilterNames, R.array.rendererScalingFilterNames,
@@ -1080,6 +1096,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
) )
quickSettings.addSliderSetting( quickSettings.addSliderSetting(
R.string.fsr_sharpness,
container, container,
IntSetting.FSR_SHARPENING_SLIDER, IntSetting.FSR_SHARPENING_SLIDER,
minValue = 0, minValue = 0,
@@ -1088,6 +1105,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
) )
quickSettings.addIntSetting( quickSettings.addIntSetting(
R.string.renderer_anti_aliasing,
container, container,
IntSetting.RENDERER_ANTI_ALIASING, IntSetting.RENDERER_ANTI_ALIASING,
R.array.rendererAntiAliasingNames, R.array.rendererAntiAliasingNames,
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.ui package org.yuzu.yuzu_emu.ui
@@ -13,6 +13,7 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import android.widget.PopupMenu import android.widget.PopupMenu
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
@@ -27,10 +28,14 @@ import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import org.yuzu.yuzu_emu.HomeNavigationDirections
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.adapters.GameAdapter import org.yuzu.yuzu_emu.adapters.GameAdapter
import org.yuzu.yuzu_emu.databinding.FragmentGamesBinding import org.yuzu.yuzu_emu.databinding.FragmentGamesBinding
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
import org.yuzu.yuzu_emu.model.AppletInfo
import org.yuzu.yuzu_emu.model.Game import org.yuzu.yuzu_emu.model.Game
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
@@ -173,10 +178,16 @@ class GamesFragment : Fragment() {
setupTopView() setupTopView()
updateButtonsVisibility()
binding.addDirectory.setOnClickListener { binding.addDirectory.setOnClickListener {
getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data) getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data)
} }
binding.launchQlaunch?.setOnClickListener {
launchQLaunch()
}
setInsets() setInsets()
} }
@@ -445,6 +456,47 @@ class GamesFragment : Fragment() {
} }
} }
private fun launchQLaunch() {
try {
val appletPath = NativeLibrary.getAppletLaunchPath(AppletInfo.QLaunch.entryId)
if (appletPath.isEmpty()) {
Toast.makeText(
requireContext(),
R.string.applets_error_applet,
Toast.LENGTH_SHORT
).show()
return
}
NativeLibrary.setCurrentAppletId(AppletInfo.QLaunch.appletId)
val qlaunchGame = Game(
title = getString(R.string.qlaunch_applet),
path = appletPath
)
val action = HomeNavigationDirections.actionGlobalEmulationActivity(qlaunchGame)
findNavController().navigate(action)
} catch (e: Exception) {
Toast.makeText(
requireContext(),
"Failed to launch QLaunch: ${e.message}",
Toast.LENGTH_SHORT
).show()
}
}
private fun updateButtonsVisibility() {
val showQLaunch = BooleanSetting.ENABLE_QLAUNCH_BUTTON.getBoolean()
val showFolder = BooleanSetting.ENABLE_FOLDER_BUTTON.getBoolean()
val isFirmwareAvailable = NativeLibrary.isFirmwareAvailable()
val shouldShowQLaunch = showQLaunch && isFirmwareAvailable
binding.launchQlaunch.visibility = if (shouldShowQLaunch) View.VISIBLE else View.GONE
binding.addDirectory.visibility = if (showFolder) View.VISIBLE else View.GONE
}
private fun setInsets() = private fun setInsets() =
ViewCompat.setOnApplyWindowInsetsListener( ViewCompat.setOnApplyWindowInsetsListener(
binding.root binding.root
@@ -498,6 +550,13 @@ class GamesFragment : Fragment() {
mlpFab.rightMargin = rightInset + fabPadding mlpFab.rightMargin = rightInset + fabPadding
binding.addDirectory.layoutParams = mlpFab binding.addDirectory.layoutParams = mlpFab
binding.launchQlaunch?.let { qlaunchButton ->
val mlpQLaunch = qlaunchButton.layoutParams as ViewGroup.MarginLayoutParams
mlpQLaunch.leftMargin = leftInset + fabPadding
mlpQLaunch.bottomMargin = barInsets.bottom + fabPadding
qlaunchButton.layoutParams = mlpQLaunch
}
val navInsets = windowInsets.getInsets(WindowInsetsCompat.Type.navigationBars()) val navInsets = windowInsets.getInsets(WindowInsetsCompat.Type.navigationBars())
val gestureInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemGestures()) val gestureInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemGestures())
val bottomInset = maxOf(navInsets.bottom, gestureInsets.bottom, cutoutInsets.bottom) val bottomInset = maxOf(navInsets.bottom, gestureInsets.bottom, cutoutInsets.bottom)
@@ -65,6 +65,10 @@ namespace AndroidSettings {
Settings::Setting<s32> app_language{linkage, 0, "app_language", Settings::Category::Android}; Settings::Setting<s32> app_language{linkage, 0, "app_language", Settings::Category::Android};
Settings::Setting<bool> enable_update_checks{linkage, true, "enable_update_checks", Settings::Setting<bool> enable_update_checks{linkage, true, "enable_update_checks",
Settings::Category::Android}; Settings::Category::Android};
Settings::Setting<bool> enable_folder_button{linkage, true, "enable_folder_button",
Settings::Category::Android};
Settings::Setting<bool> enable_qlaunch_button{linkage, false, "enable_qlaunch_button",
Settings::Category::Android};
// Input/performance overlay settings // Input/performance overlay settings
std::vector<OverlayControlData> overlay_control_data; std::vector<OverlayControlData> overlay_control_data;
@@ -198,9 +202,14 @@ namespace AndroidSettings {
Settings::Specialization::Default, true, true, Settings::Specialization::Default, true, true,
&show_soc_overlay}; &show_soc_overlay};
// MISC
Settings::Setting<bool> dont_show_driver_shader_warning{linkage, false, Settings::Setting<bool> dont_show_driver_shader_warning{linkage, false,
"dont_show_driver_shader_warning", "dont_show_driver_shader_warning",
Settings::Category::Android, Settings::Specialization::Default, true, true}; Settings::Category::Android, Settings::Specialization::Default, true, true};
Settings::Setting<bool> enable_quick_settings{linkage, true,
"enable_quick_settings",
Settings::Category::Android, Settings::Specialization::Default, true,
false};
}; };
extern Values values; extern Values values;
@@ -1,18 +1,19 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#include <string> #include <string>
#include <jni.h> #include <jni.h>
#include <common/fs/path_util.h>
#include "android_config.h" #include "android_config.h"
#include "android_settings.h" #include "android_settings.h"
#include "common/android/android_common.h" #include "common/android/android_common.h"
#include "common/android/id_cache.h" #include "common/android/id_cache.h"
#include "common/fs/path_util.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/settings.h" #include "common/settings.h"
#include "frontend_common/config.h" #include "frontend_common/config.h"
#include "frontend_common/settings_generator.h"
#include "native.h" #include "native.h"
std::unique_ptr<AndroidConfig> global_config; std::unique_ptr<AndroidConfig> global_config;
@@ -37,6 +38,7 @@ extern "C" {
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_initializeGlobalConfig(JNIEnv* env, jobject obj) { void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_initializeGlobalConfig(JNIEnv* env, jobject obj) {
global_config = std::make_unique<AndroidConfig>(); global_config = std::make_unique<AndroidConfig>();
FrontendCommon::GenerateSettings();
} }
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_unloadGlobalConfig(JNIEnv* env, jobject obj) { void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_unloadGlobalConfig(JNIEnv* env, jobject obj) {
@@ -220,6 +220,23 @@
android:textColor="?attr/colorOnPrimary" android:textColor="?attr/colorOnPrimary"
app:backgroundTint="?attr/colorPrimary" app:backgroundTint="?attr/colorPrimary"
app:iconTint="?attr/colorOnPrimary" app:iconTint="?attr/colorOnPrimary"
app:rippleColor="#99FFFFFF"
/>
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/launch_qlaunch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:contentDescription="Launch QLaunch"
android:text="@string/qlaunch_applet"
app:icon="@drawable/ic_home"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:textColor="?attr/colorOnPrimary"
app:backgroundTint="?attr/colorPrimary"
app:iconTint="?attr/colorOnPrimary"
app:rippleColor="#99FFFFFF"
/> />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
@@ -214,6 +214,22 @@
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout> </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/launch_qlaunch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:contentDescription="Launch QLaunch"
android:text="@string/qlaunch_applet"
app:icon="@drawable/ic_home"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:textColor="?attr/colorOnPrimary"
app:backgroundTint="?attr/colorPrimary"
app:iconTint="?attr/colorOnPrimary"
app:rippleColor="#99FFFFFF"
/>
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton <com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/add_directory" android:id="@+id/add_directory"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@@ -227,6 +243,7 @@
android:textColor="?attr/colorOnPrimary" android:textColor="?attr/colorOnPrimary"
app:backgroundTint="?attr/colorPrimary" app:backgroundTint="?attr/colorPrimary"
app:iconTint="?attr/colorOnPrimary" app:iconTint="?attr/colorOnPrimary"
app:rippleColor="#99FFFFFF"
/> />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
@@ -493,6 +493,8 @@
<string name="renderer_force_max_clock_description">Forces the GPU to run at the maximum possible clocks (thermal constraints will still be applied).</string> <string name="renderer_force_max_clock_description">Forces the GPU to run at the maximum possible clocks (thermal constraints will still be applied).</string>
<string name="renderer_reactive_flushing">Use reactive flushing</string> <string name="renderer_reactive_flushing">Use reactive flushing</string>
<string name="renderer_reactive_flushing_description">Improves rendering accuracy in some games at the cost of performance.</string> <string name="renderer_reactive_flushing_description">Improves rendering accuracy in some games at the cost of performance.</string>
<string name="enable_buffer_history">Enable buffer history</string>
<string name="enable_buffer_history_description">Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games.</string>
<string name="hacks">Hacks</string> <string name="hacks">Hacks</string>
@@ -556,13 +558,11 @@
<string name="flush_by_line_description">Flushes debugging logs on each line written, making debugging easier in cases of crashing or freezing.</string> <string name="flush_by_line_description">Flushes debugging logs on each line written, making debugging easier in cases of crashing or freezing.</string>
<!-- GPU Logging strings --> <!-- GPU Logging strings -->
<string name="gpu_logging">GPU Logging</string>
<string name="gpu_logging_header">GPU Logging</string> <string name="gpu_logging_header">GPU Logging</string>
<string name="gpu_logging_enabled">Enable GPU Logging</string> <string name="gpu_logging_enabled">Enable GPU Logging</string>
<string name="gpu_logging_enabled_description">Log GPU operations to eden_gpu.log for debugging Adreno drivers</string> <string name="gpu_logging_enabled_description">Log GPU operations to eden_gpu.log for debugging Adreno drivers</string>
<string name="gpu_log_level">Log Level</string> <string name="gpu_log_level">Log Level</string>
<string name="gpu_log_level_description">Detail level for GPU logs (higher = more detail, more overhead)</string> <string name="gpu_log_level_description">Detail level for GPU logs (higher = more detail, more overhead)</string>
<string name="gpu_logging_features">Logging Features</string>
<string name="gpu_log_vulkan_calls">Log Vulkan API Calls</string> <string name="gpu_log_vulkan_calls">Log Vulkan API Calls</string>
<string name="gpu_log_vulkan_calls_description">Track all Vulkan API calls in ring buffer</string> <string name="gpu_log_vulkan_calls_description">Track all Vulkan API calls in ring buffer</string>
<string name="gpu_log_shader_dumps">Dump Shaders</string> <string name="gpu_log_shader_dumps">Dump Shaders</string>
@@ -738,6 +738,8 @@
<string name="preferences_graphics">Graphics</string> <string name="preferences_graphics">Graphics</string>
<string name="preferences_graphics_description">Accuracy level, resolution, shader cache</string> <string name="preferences_graphics_description">Accuracy level, resolution, shader cache</string>
<string name="quick_settings">Quick Settings</string> <string name="quick_settings">Quick Settings</string>
<string name="enable_quick_settings">Enable Quick Settings</string>
<string name="enable_quick_settings_description">Allow access to quick settings menu via swipe and menu button</string>
<string name="preferences_audio">Audio</string> <string name="preferences_audio">Audio</string>
<string name="preferences_audio_description">Output engine, volume</string> <string name="preferences_audio_description">Output engine, volume</string>
<string name="preferences_controls">Controls</string> <string name="preferences_controls">Controls</string>
@@ -1163,6 +1165,12 @@
<string name="use_black_backgrounds">Black backgrounds</string> <string name="use_black_backgrounds">Black backgrounds</string>
<string name="use_black_backgrounds_description">When using the dark theme, apply black backgrounds.</string> <string name="use_black_backgrounds_description">When using the dark theme, apply black backgrounds.</string>
<!-- Buttons -->
<string name="enable_folder_button">Folder</string>
<string name="enable_folder_button_description">Show the button to add game folders</string>
<string name="enable_qlaunch_button">QLaunch</string>
<string name="enable_qlaunch_button_description">Show the button to launch QLaunch</string>
<!-- App Language --> <!-- App Language -->
<string name="app_language">App Language</string> <string name="app_language">App Language</string>
<string name="app_language_description">Change the language of the app interface</string> <string name="app_language_description">Change the language of the app interface</string>
+10 -2
View File
@@ -481,6 +481,14 @@ struct Values {
SwitchableSetting<bool> barrier_feedback_loops{linkage, true, "barrier_feedback_loops", SwitchableSetting<bool> barrier_feedback_loops{linkage, true, "barrier_feedback_loops",
Category::RendererAdvanced}; Category::RendererAdvanced};
SwitchableSetting<bool> enable_buffer_history{linkage,
false,
"enable_buffer_history",
Category::RendererAdvanced,
Specialization::Default,
true,
true};
// Renderer Hacks // // Renderer Hacks //
SwitchableSetting<GpuOverclock> fast_gpu_time{linkage, SwitchableSetting<GpuOverclock> fast_gpu_time{linkage,
GpuOverclock::Medium, GpuOverclock::Medium,
@@ -739,7 +747,7 @@ struct Values {
Setting<bool> disable_web_applet{linkage, true, "disable_web_applet", Category::Debugging}; Setting<bool> disable_web_applet{linkage, true, "disable_web_applet", Category::Debugging};
// GPU Logging // GPU Logging
Setting<bool> gpu_logging_enabled{linkage, true, "gpu_logging_enabled", Category::Debugging}; Setting<bool> gpu_logging_enabled{linkage, false, "gpu_logging_enabled", Category::Debugging};
SwitchableSetting<GpuLogLevel> gpu_log_level{linkage, GpuLogLevel::Standard, "gpu_log_level", SwitchableSetting<GpuLogLevel> gpu_log_level{linkage, GpuLogLevel::Standard, "gpu_log_level",
Category::Debugging}; Category::Debugging};
Setting<bool> gpu_log_vulkan_calls{linkage, true, "gpu_log_vulkan_calls", Category::Debugging}; Setting<bool> gpu_log_vulkan_calls{linkage, true, "gpu_log_vulkan_calls", Category::Debugging};
@@ -778,7 +786,7 @@ struct Values {
Category::WebService}; Category::WebService};
Setting<std::string> eden_username{linkage, "Eden", "eden_username", Setting<std::string> eden_username{linkage, "Eden", "eden_username",
Category::WebService}; Category::WebService};
Setting<std::string> eden_token{linkage, "njausoolxygtpvraofqunuufhmupriifnpfggjxefntlyglr", Setting<std::string> eden_token{linkage, "",
"eden_token", Category::WebService}; "eden_token", Category::WebService};
// Add-Ons // Add-Ons
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
@@ -99,11 +99,6 @@ Status BufferQueueConsumer::AcquireBuffer(BufferItem* out_buffer,
slots[slot].acquire_called = true; slots[slot].acquire_called = true;
slots[slot].needs_cleanup_on_release = false; slots[slot].needs_cleanup_on_release = false;
slots[slot].buffer_state = BufferState::Acquired; slots[slot].buffer_state = BufferState::Acquired;
// TODO: for now, avoid resetting the fence, so that when we next return this
// slot to the producer, it will wait for the fence to pass. We should fix this
// by properly waiting for the fence in the BufferItemConsumer.
// slots[slot].fence = Fence::NoFence();
} }
// If the buffer has previously been acquired by the consumer, set graphic_buffer to nullptr to // If the buffer has previously been acquired by the consumer, set graphic_buffer to nullptr to
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
@@ -17,6 +17,39 @@ BufferQueueCore::BufferQueueCore() = default;
BufferQueueCore::~BufferQueueCore() = default; BufferQueueCore::~BufferQueueCore() = default;
void BufferQueueCore::PushHistory(u64 frame_number, s64 queue_time, s64 presentation_time, BufferState state) {
std::lock_guard lk(buffer_history_mutex);
auto it = buffer_history_map.find(frame_number);
if (it != buffer_history_map.end()) {
it->second.state = state;
return;
}
buffer_history_map.emplace(frame_number, BufferHistoryInfo{
frame_number,
queue_time,
presentation_time,
state
});
buffer_history_order.push_back(frame_number);
if (buffer_history_order.size() > BUFFER_HISTORY_SIZE) {
u64 oldest_frame = buffer_history_order.front();
buffer_history_order.pop_front();
buffer_history_map.erase(oldest_frame);
}
}
void BufferQueueCore::UpdateHistory(u64 frame_number, BufferState state) {
std::lock_guard lk(buffer_history_mutex);
auto it = buffer_history_map.find(frame_number);
if (it != buffer_history_map.end()) {
it->second.state = state;
}
}
void BufferQueueCore::SignalDequeueCondition() { void BufferQueueCore::SignalDequeueCondition() {
dequeue_possible.store(true); dequeue_possible.store(true);
dequeue_condition.notify_all(); dequeue_condition.notify_all();
@@ -30,7 +63,6 @@ bool BufferQueueCore::WaitForDequeueCondition(std::unique_lock<std::mutex>& lk)
} }
s32 BufferQueueCore::GetMinUndequeuedBufferCountLocked(bool async) const { s32 BufferQueueCore::GetMinUndequeuedBufferCountLocked(bool async) const {
// If DequeueBuffer is allowed to error out, we don't have to add an extra buffer.
if (!use_async_buffer) { if (!use_async_buffer) {
return 0; return 0;
} }
@@ -55,8 +87,6 @@ s32 BufferQueueCore::GetMaxBufferCountLocked(bool async) const {
return override_max_buffer_count; return override_max_buffer_count;
} }
// Any buffers that are dequeued by the producer or sitting in the queue waiting to be consumed
// need to have their slots preserved.
for (s32 slot = max_buffer_count; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) { for (s32 slot = max_buffer_count; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) {
const auto state = slots[slot].buffer_state; const auto state = slots[slot].buffer_state;
if (state == BufferState::Queued || state == BufferState::Dequeued) { if (state == BufferState::Queued || state == BufferState::Dequeued) {
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
@@ -10,20 +10,31 @@
#pragma once #pragma once
#include <condition_variable> #include <condition_variable>
#include <deque>
#include <list> #include <list>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <set> #include <set>
#include <vector> #include <vector>
#include <unordered_map>
#include <algorithm>
#include "core/hle/service/nvnflinger/buffer_item.h" #include "core/hle/service/nvnflinger/buffer_item.h"
#include "core/hle/service/nvnflinger/buffer_queue_defs.h" #include "core/hle/service/nvnflinger/buffer_queue_defs.h"
#include "core/hle/service/nvnflinger/buffer_slot.h"
#include "core/hle/service/nvnflinger/pixel_format.h" #include "core/hle/service/nvnflinger/pixel_format.h"
#include "core/hle/service/nvnflinger/status.h" #include "core/hle/service/nvnflinger/status.h"
#include "core/hle/service/nvnflinger/window.h" #include "core/hle/service/nvnflinger/window.h"
namespace Service::android { namespace Service::android {
struct BufferHistoryInfo {
u64 frame_number{};
s64 queue_time{};
s64 presentation_time{};
BufferState state{};
};
class IConsumerListener; class IConsumerListener;
class IProducerListener; class IProducerListener;
@@ -33,10 +44,14 @@ class BufferQueueCore final {
public: public:
static constexpr s32 INVALID_BUFFER_SLOT = BufferItem::INVALID_BUFFER_SLOT; static constexpr s32 INVALID_BUFFER_SLOT = BufferItem::INVALID_BUFFER_SLOT;
static constexpr u32 BUFFER_HISTORY_SIZE = 8;
BufferQueueCore(); BufferQueueCore();
~BufferQueueCore(); ~BufferQueueCore();
void PushHistory(u64 frame_number, s64 queue_time, s64 presentation_time, BufferState state);
void UpdateHistory(u64 frame_number, BufferState state);
private: private:
void SignalDequeueCondition(); void SignalDequeueCondition();
bool WaitForDequeueCondition(std::unique_lock<std::mutex>& lk); bool WaitForDequeueCondition(std::unique_lock<std::mutex>& lk);
@@ -72,6 +87,11 @@ private:
const s32 max_acquired_buffer_count{}; // This is always zero on HOS const s32 max_acquired_buffer_count{}; // This is always zero on HOS
bool buffer_has_been_queued{}; bool buffer_has_been_queued{};
u64 frame_counter{}; u64 frame_counter{};
std::unordered_map<u64, BufferHistoryInfo> buffer_history_map{};
mutable std::mutex buffer_history_mutex{};
std::deque<u64> buffer_history_order;
u32 transform_hint{}; u32 transform_hint{};
bool is_allocating{}; bool is_allocating{};
mutable std::condition_variable_any is_allocating_condition; mutable std::condition_variable_any is_allocating_condition;
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
@@ -9,6 +9,7 @@
#include "common/assert.h" #include "common/assert.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/settings.h"
#include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_readable_event.h" #include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/kernel.h"
@@ -26,7 +27,7 @@ BufferQueueProducer::BufferQueueProducer(Service::KernelHelpers::ServiceContext&
std::shared_ptr<BufferQueueCore> buffer_queue_core_, std::shared_ptr<BufferQueueCore> buffer_queue_core_,
Service::Nvidia::NvCore::NvMap& nvmap_) Service::Nvidia::NvCore::NvMap& nvmap_)
: service_context{service_context_}, core{std::move(buffer_queue_core_)}, slots(core->slots), : service_context{service_context_}, core{std::move(buffer_queue_core_)}, slots(core->slots),
nvmap(nvmap_) { clock{Common::CreateOptimalClock()}, nvmap(nvmap_) {
buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent"); buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent");
} }
@@ -428,8 +429,7 @@ Status BufferQueueProducer::AttachBuffer(s32* out_slot,
return return_flags; return return_flags;
} }
Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input, Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input, QueueBufferOutput* output) {
QueueBufferOutput* output) {
s64 timestamp{}; s64 timestamp{};
bool is_auto_timestamp{}; bool is_auto_timestamp{};
Common::Rectangle<s32> crop; Common::Rectangle<s32> crop;
@@ -440,8 +440,7 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
s32 swap_interval{}; s32 swap_interval{};
Fence fence{}; Fence fence{};
input.Deflate(&timestamp, &is_auto_timestamp, &crop, &scaling_mode, &transform, input.Deflate(&timestamp, &is_auto_timestamp, &crop, &scaling_mode, &transform, &sticky_transform_, &async, &swap_interval, &fence);
&sticky_transform_, &async, &swap_interval, &fence);
switch (scaling_mode) { switch (scaling_mode) {
case NativeWindowScalingMode::Freeze: case NativeWindowScalingMode::Freeze:
@@ -455,10 +454,9 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
return Status::BadValue; return Status::BadValue;
} }
std::shared_ptr<IConsumerListener> frame_available_listener;
std::shared_ptr<IConsumerListener> frame_replaced_listener;
s32 callback_ticket{};
BufferItem item; BufferItem item;
std::shared_ptr<IConsumerListener> listener_available;
std::shared_ptr<IConsumerListener> listener_replaced;
{ {
std::scoped_lock lock{core->mutex}; std::scoped_lock lock{core->mutex};
@@ -469,127 +467,82 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
} }
const s32 max_buffer_count = core->GetMaxBufferCountLocked(async); const s32 max_buffer_count = core->GetMaxBufferCountLocked(async);
if (async && core->override_max_buffer_count) {
if (core->override_max_buffer_count < max_buffer_count) {
LOG_ERROR(Service_Nvnflinger, "async mode is invalid with "
"buffer count override");
return Status::BadValue;
}
}
if (slot < 0 || slot >= max_buffer_count) { if (slot < 0 || slot >= max_buffer_count) {
LOG_ERROR(Service_Nvnflinger, "slot index {} out of range [0, {})", slot, LOG_ERROR(Service_Nvnflinger, "slot {} out of range [0, {})", slot, max_buffer_count);
max_buffer_count);
return Status::BadValue; return Status::BadValue;
} else if (slots[slot].buffer_state != BufferState::Dequeued) { }
LOG_ERROR(Service_Nvnflinger, if (slots[slot].buffer_state != BufferState::Dequeued) {
"slot {} is not owned by the producer " LOG_ERROR(Service_Nvnflinger, "slot {} is not owned by producer", slot);
"(state = {})",
slot, slots[slot].buffer_state);
return Status::BadValue; return Status::BadValue;
} else if (!slots[slot].request_buffer_called) { }
LOG_ERROR(Service_Nvnflinger, if (!slots[slot].request_buffer_called) {
"slot {} was queued without requesting " LOG_ERROR(Service_Nvnflinger, "slot {} was queued without request", slot);
"a buffer",
slot);
return Status::BadValue; return Status::BadValue;
} }
LOG_DEBUG(Service_Nvnflinger,
"slot={} frame={} time={} crop=[{},{},{},{}] transform={} scale={}", slot,
core->frame_counter + 1, timestamp, crop.Left(), crop.Top(), crop.Right(),
crop.Bottom(), transform, scaling_mode);
const std::shared_ptr<GraphicBuffer>& graphic_buffer(slots[slot].graphic_buffer);
Common::Rectangle<s32> buffer_rect(graphic_buffer->Width(), graphic_buffer->Height());
Common::Rectangle<s32> cropped_rect;
[[maybe_unused]] const bool unused = crop.Intersect(buffer_rect, &cropped_rect);
if (cropped_rect != crop) {
LOG_ERROR(Service_Nvnflinger, "crop rect is not contained within the buffer in slot {}",
slot);
return Status::BadValue;
}
slots[slot].fence = fence;
slots[slot].buffer_state = BufferState::Queued;
++core->frame_counter; ++core->frame_counter;
slots[slot].buffer_state = BufferState::Queued;
slots[slot].frame_number = core->frame_counter; slots[slot].frame_number = core->frame_counter;
slots[slot].queue_time = timestamp;
slots[slot].presentation_time = clock->GetTimeNS().count();
slots[slot].fence = fence;
item.acquire_called = slots[slot].acquire_called; item.slot = slot;
item.graphic_buffer = slots[slot].graphic_buffer; item.graphic_buffer = slots[slot].graphic_buffer;
item.crop = crop; item.frame_number = core->frame_counter;
item.transform = transform & ~NativeWindowTransform::InverseDisplay;
item.transform_to_display_inverse =
(transform & NativeWindowTransform::InverseDisplay) != NativeWindowTransform::None;
item.scaling_mode = static_cast<u32>(scaling_mode);
item.timestamp = timestamp; item.timestamp = timestamp;
item.is_auto_timestamp = is_auto_timestamp; item.is_auto_timestamp = is_auto_timestamp;
item.frame_number = core->frame_counter; item.crop = crop;
item.slot = slot; item.transform = transform & ~NativeWindowTransform::InverseDisplay;
item.transform_to_display_inverse = (transform & NativeWindowTransform::InverseDisplay) != NativeWindowTransform::None;
item.scaling_mode = static_cast<u32>(scaling_mode);
item.fence = fence; item.fence = fence;
item.is_droppable = core->dequeue_buffer_cannot_block || async; item.is_droppable = core->dequeue_buffer_cannot_block || async;
item.swap_interval = swap_interval; item.swap_interval = swap_interval;
item.acquire_called = slots[slot].acquire_called;
sticky_transform = sticky_transform_; sticky_transform = sticky_transform_;
if (core->queue.empty()) { if (core->queue.empty()) {
// When the queue is empty, we can simply queue this buffer
core->queue.push_back(item); core->queue.push_back(item);
frame_available_listener = core->consumer_listener; listener_available = core->consumer_listener;
} else { } else {
// When the queue is not empty, we need to look at the front buffer auto front = core->queue.begin();
// state to see if we need to replace it if (front->is_droppable && core->StillTracking(*front)) {
auto front(core->queue.begin());
if (front->is_droppable) {
// If the front queued buffer is still being tracked, we first
// mark it as freed
if (core->StillTracking(*front)) {
slots[front->slot].buffer_state = BufferState::Free; slots[front->slot].buffer_state = BufferState::Free;
// Reset the frame number of the freed buffer so that it is the first in line to if (Settings::values.enable_buffer_history.GetValue()) {
// be dequeued again core->UpdateHistory(front->frame_number, BufferState::Free);
}
slots[front->slot].frame_number = 0; slots[front->slot].frame_number = 0;
} }
// Overwrite the droppable buffer with the incoming one
if (front->is_droppable) {
*front = item; *front = item;
frame_replaced_listener = core->consumer_listener; listener_replaced = core->consumer_listener;
} else { } else {
core->queue.push_back(item); core->queue.push_back(item);
frame_available_listener = core->consumer_listener; listener_available = core->consumer_listener;
} }
} }
if (Settings::values.enable_buffer_history.GetValue()) {
core->PushHistory(core->frame_counter, slots[slot].queue_time, slots[slot].presentation_time, BufferState::Queued);
}
core->buffer_has_been_queued = true; core->buffer_has_been_queued = true;
core->SignalDequeueCondition(); core->SignalDequeueCondition();
output->Inflate(core->default_width, core->default_height, core->transform_hint,
static_cast<u32>(core->queue.size()));
// Take a ticket for the callback functions output->Inflate(core->default_width, core->default_height, core->transform_hint, static_cast<u32>(core->queue.size()));
callback_ticket = next_callback_ticket++;
} }
// Don't send the GraphicBuffer through the callback, and don't send the slot number, since the
// consumer shouldn't need it
item.graphic_buffer.reset(); item.graphic_buffer.reset();
item.slot = BufferItem::INVALID_BUFFER_SLOT; item.slot = BufferItem::INVALID_BUFFER_SLOT;
// Call back without the main BufferQueue lock held, but with the callback lock held so we can if (listener_available) {
// ensure that callbacks occur in order listener_available->OnFrameAvailable(item);
{ } else if (listener_replaced) {
std::scoped_lock lock{callback_mutex}; listener_replaced->OnFrameReplaced(item);
while (callback_ticket != current_callback_ticket) {
callback_condition.wait(callback_mutex);
}
if (frame_available_listener != nullptr) {
frame_available_listener->OnFrameAvailable(item);
} else if (frame_replaced_listener != nullptr) {
frame_replaced_listener->OnFrameReplaced(item);
}
++current_callback_ticket;
callback_condition.notify_all();
} }
return Status::NoError; return Status::NoError;
@@ -810,6 +763,10 @@ Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot,
return Status::NoError; return Status::NoError;
} }
Kernel::KReadableEvent* BufferQueueProducer::GetNativeHandle(u32 type_id) {
return &buffer_wait_event->GetReadableEvent();
}
void BufferQueueProducer::Transact(u32 code, std::span<const u8> parcel_data, void BufferQueueProducer::Transact(u32 code, std::span<const u8> parcel_data,
std::span<u8> parcel_reply, u32 flags) { std::span<u8> parcel_reply, u32 flags) {
// Values used by BnGraphicBufferProducer onTransact // Values used by BnGraphicBufferProducer onTransact
@@ -929,9 +886,42 @@ void BufferQueueProducer::Transact(u32 code, std::span<const u8> parcel_data,
status = SetBufferCount(buffer_count); status = SetBufferCount(buffer_count);
break; break;
} }
case TransactionId::GetBufferHistory: case TransactionId::GetBufferHistory: {
LOG_DEBUG(Service_Nvnflinger, "(STUBBED) called, transaction=GetBufferHistory"); if (!Settings::values.enable_buffer_history.GetValue()) {
LOG_DEBUG(Service_Nvnflinger, "(STUBBED) called");
break; break;
}
LOG_DEBUG(Service_Nvnflinger, "called, transaction=GetBufferHistory");
const s32 request = parcel_in.Read<s32>();
if (request <= 0) {
parcel_out.Write(Status::BadValue);
parcel_out.Write<s32>(0);
break;
}
std::vector<BufferHistoryInfo> snapshot;
{
std::scoped_lock lk(core->buffer_history_mutex);
for (auto& [frame, info] : core->buffer_history_map) {
snapshot.push_back(info);
}
}
std::sort(snapshot.begin(), snapshot.end(), [](auto& a, auto& b){
return a.frame_number > b.frame_number;
});
const s32 limit = std::min(request, (s32)snapshot.size());
parcel_out.Write(Status::NoError);
parcel_out.Write<s32>(limit);
for (s32 i = 0; i < limit; ++i) {
parcel_out.Write(snapshot[i]);
}
break;
}
default: default:
ASSERT_MSG(false, "Unimplemented TransactionId {}", code); ASSERT_MSG(false, "Unimplemented TransactionId {}", code);
break; break;
@@ -944,8 +934,4 @@ void BufferQueueProducer::Transact(u32 code, std::span<const u8> parcel_data,
(std::min)(parcel_reply.size(), serialized.size())); (std::min)(parcel_reply.size(), serialized.size()));
} }
Kernel::KReadableEvent* BufferQueueProducer::GetNativeHandle(u32 type_id) {
return &buffer_wait_event->GetReadableEvent();
}
} // namespace Service::android } // namespace Service::android
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
@@ -14,6 +14,7 @@
#include <mutex> #include <mutex>
#include "common/common_funcs.h" #include "common/common_funcs.h"
#include "common/wall_clock.h"
#include "core/hle/service/nvdrv/nvdata.h" #include "core/hle/service/nvdrv/nvdata.h"
#include "core/hle/service/nvnflinger/binder.h" #include "core/hle/service/nvnflinger/binder.h"
#include "core/hle/service/nvnflinger/buffer_queue_defs.h" #include "core/hle/service/nvnflinger/buffer_queue_defs.h"
@@ -88,6 +89,7 @@ private:
s32 next_callback_ticket{}; s32 next_callback_ticket{};
s32 current_callback_ticket{}; s32 current_callback_ticket{};
std::condition_variable_any callback_condition; std::condition_variable_any callback_condition;
std::unique_ptr<Common::WallClock> clock;
Service::Nvidia::NvCore::NvMap& nvmap; Service::Nvidia::NvCore::NvMap& nvmap;
}; };
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
@@ -37,6 +37,7 @@ struct BufferSlot final {
bool needs_cleanup_on_release{}; bool needs_cleanup_on_release{};
bool attached_by_consumer{}; bool attached_by_consumer{};
bool is_preallocated{}; bool is_preallocated{};
s64 queue_time{}, presentation_time{};
}; };
} // namespace Service::android } // namespace Service::android
+6 -3
View File
@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project # SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2023 yuzu Emulator Project # SPDX-FileCopyrightText: 2023 yuzu Emulator Project
@@ -12,7 +12,8 @@ add_library(frontend_common STATIC
firmware_manager.cpp firmware_manager.cpp
data_manager.h data_manager.cpp data_manager.h data_manager.cpp
play_time_manager.cpp play_time_manager.cpp
play_time_manager.h) play_time_manager.h
settings_generator.h settings_generator.cpp)
if (ENABLE_UPDATE_CHECKER) if (ENABLE_UPDATE_CHECKER)
target_link_libraries(frontend_common PRIVATE httplib::httplib) target_link_libraries(frontend_common PRIVATE httplib::httplib)
@@ -29,4 +30,6 @@ if (ENABLE_UPDATE_CHECKER)
endif() endif()
create_target_directory_groups(frontend_common) create_target_directory_groups(frontend_common)
target_link_libraries(frontend_common PUBLIC core SimpleIni::SimpleIni PRIVATE common Boost::headers) target_link_libraries(frontend_common
PUBLIC core SimpleIni::SimpleIni frozen::frozen-headers
PRIVATE common Boost::headers)
@@ -0,0 +1,31 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <random>
#include <frozen/string.h>
#include "common/settings.h"
#include "settings_generator.h"
namespace FrontendCommon {
void GenerateSettings() {
static std::random_device rd;
// Web Token //
auto &token_setting = Settings::values.eden_token;
if (token_setting.GetValue().empty()) {
static constexpr const size_t token_length = 48;
static constexpr const frozen::string token_set = "abcdefghijklmnopqrstuvwxyz";
static std::uniform_int_distribution<int> token_dist(0, token_set.size() - 1);
std::string result;
for (size_t i = 0; i < token_length; ++i) {
size_t idx = token_dist(rd);
result += token_set[idx];
}
token_setting.SetValue(result);
}
}
}
+14
View File
@@ -0,0 +1,14 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
namespace FrontendCommon {
/**
* @brief GenerateSettings Generate platform-specific or randomized settings.
* Run this function at initialization time for your frontend.
*/
void GenerateSettings();
}
+1 -2
View File
@@ -176,6 +176,5 @@ std::optional<UpdateChecker::Update> UpdateChecker::GetUpdate() {
} }
empty: empty:
return UpdateChecker::Update{}; return std::nullopt;
} }
+2 -7
View File
@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project # SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
add_library(qt_common STATIC add_library(qt_common STATIC
@@ -78,14 +78,9 @@ target_compile_definitions(qt_common PUBLIC
QT_NO_URL_CAST_FROM_STRING QT_NO_URL_CAST_FROM_STRING
) )
add_subdirectory(externals)
# pass targets
find_package(frozen)
target_link_libraries(qt_common PRIVATE core Qt6::Core Qt6::Concurrent SimpleIni::SimpleIni QuaZip::QuaZip) target_link_libraries(qt_common PRIVATE core Qt6::Core Qt6::Concurrent SimpleIni::SimpleIni QuaZip::QuaZip)
target_link_libraries(qt_common PUBLIC frozen::frozen-headers) target_link_libraries(qt_common PUBLIC frozen::frozen-headers)
target_link_libraries(qt_common PRIVATE gamemode::headers) target_link_libraries(qt_common PRIVATE gamemode::headers frontend_common)
if (NOT APPLE AND ENABLE_OPENGL) if (NOT APPLE AND ENABLE_OPENGL)
target_compile_definitions(qt_common PUBLIC HAS_OPENGL) target_compile_definitions(qt_common PUBLIC HAS_OPENGL)
@@ -329,6 +329,10 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent)
barrier_feedback_loops, barrier_feedback_loops,
tr("Barrier feedback loops"), tr("Barrier feedback loops"),
tr("Improves rendering of transparency effects in specific games.")); tr("Improves rendering of transparency effects in specific games."));
INSERT(Settings,
enable_buffer_history,
tr("Enable buffer history"),
tr("Enables access to previous buffer states.\nThis option may improve rendering quality and performance consistency in some games."));
INSERT(Settings, INSERT(Settings,
fix_bloom_effects, fix_bloom_effects,
tr("Fix bloom effects"), tr("Fix bloom effects"),
-19
View File
@@ -1,19 +0,0 @@
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
include(CPMUtil)
# Disable tests/tools in all externals supporting the standard option name
set(BUILD_TESTING OFF)
# Build only static externals
set(BUILD_SHARED_LIBS OFF)
# Skip install rules for all externals
set_directory_properties(PROPERTIES EXCLUDE_FROM_ALL ON)
# QuaZip
AddJsonPackage(quazip)
# frozen
AddJsonPackage(frozen)
-18
View File
@@ -1,18 +0,0 @@
{
"quazip": {
"package": "QuaZip-Qt6",
"repo": "crueter/quazip-qt6",
"sha": "f838774d63",
"hash": "e8f950f47c1f358e2666f08517a9b5b06980677540d3836384e2c27ff5bb129b218f1502b03fdb207d7fd4cd56893f0a0d9094ba8309f19a49cb11e3bb911594",
"version": "1.3",
"options": [
"QUAZIP_INSTALL OFF"
]
},
"frozen": {
"package": "frozen",
"repo": "serge-sans-paille/frozen",
"sha": "61dce5ae18",
"hash": "b8dfe741c82bc178dfc9749d4ab5a130cee718d9ee7b71d9b547cf5f7f23027ed0152ad250012a8546399fcc1e12187efc68d89d6731256c4d2df7d04eef8d5c"
}
}
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -148,10 +151,13 @@ Id EmitConvertU32U64(EmitContext& ctx, Id value) {
} }
Id EmitConvertF16F32(EmitContext& ctx, Id value) { Id EmitConvertF16F32(EmitContext& ctx, Id value) {
#ifdef ANDROID
return ctx.OpFConvert(ctx.F16[1], value);
#else
const auto result = ctx.OpFConvert(ctx.F16[1], value); const auto result = ctx.OpFConvert(ctx.F16[1], value);
const auto isOverflowing = ctx.OpIsNan(ctx.U1, result); const auto isOverflowing = ctx.OpIsNan(ctx.U1, result);
return ctx.OpSelect(ctx.F16[1], isOverflowing, ctx.Constant(ctx.F16[1], 0), result); return ctx.OpSelect(ctx.F16[1], isOverflowing, ctx.Constant(ctx.F16[1], 0), result);
//return ctx.OpFConvert(ctx.F16[1], value); #endif
} }
Id EmitConvertF32F16(EmitContext& ctx, Id value) { Id EmitConvertF32F16(EmitContext& ctx, Id value) {
+1 -1
View File
@@ -331,7 +331,7 @@ target_include_directories(video_core PRIVATE ${HOST_SHADERS_INCLUDE})
target_link_libraries(video_core PRIVATE sirit::sirit) target_link_libraries(video_core PRIVATE sirit::sirit)
# Header-only stuff needed by all dependent targets # Header-only stuff needed by all dependent targets
target_link_libraries(video_core PUBLIC Vulkan::UtilityHeaders GPUOpen::VulkanMemoryAllocator) target_link_libraries(video_core PUBLIC Vulkan::Headers Vulkan::UtilityHeaders GPUOpen::VulkanMemoryAllocator)
if (ENABLE_NSIGHT_AFTERMATH) if (ENABLE_NSIGHT_AFTERMATH)
if (NOT DEFINED ENV{NSIGHT_AFTERMATH_SDK}) if (NOT DEFINED ENV{NSIGHT_AFTERMATH_SDK})
@@ -86,7 +86,7 @@ void WriteResults(uvec2 results[LOCAL_RESULTS]) {
const uvec2 accum = accumulated_data; const uvec2 accum = accumulated_data;
for (uint i = 0; i < LOCAL_RESULTS; i++) { for (uint i = 0; i < LOCAL_RESULTS; i++) {
uvec2 base_data = current_id * LOCAL_RESULTS + i < min_accumulation_base ? accum : uvec2(0, 0); uvec2 base_data = current_id * LOCAL_RESULTS + i < min_accumulation_base ? accum : uvec2(0, 0);
AddUint64(results[i], base_data); results[i] = AddUint64(results[i], base_data);
} }
for (uint i = 0; i < LOCAL_RESULTS; i++) { for (uint i = 0; i < LOCAL_RESULTS; i++) {
output_data[buffer_offset + current_id * LOCAL_RESULTS + i] = results[i]; output_data[buffer_offset + current_id * LOCAL_RESULTS + i] = results[i];
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
@@ -640,6 +640,7 @@ void ASTCDecoderPass::Assemble(Image& image, const StagingBufferRef& map,
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, image_barrier); VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, image_barrier);
}); });
scheduler.Finish();
} }
constexpr u32 BL3D_BINDING_SWIZZLE_TABLE = 0; constexpr u32 BL3D_BINDING_SWIZZLE_TABLE = 0;
@@ -1230,6 +1230,10 @@ void Device::RemoveUnsuitableExtensions() {
} }
RemoveExtensionFeatureIfUnsuitable(extensions.custom_border_color, features.custom_border_color, RemoveExtensionFeatureIfUnsuitable(extensions.custom_border_color, features.custom_border_color,
VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME); VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME);
// VK_KHR_unified_image_layouts
extensions.unified_image_layouts = features.unified_image_layouts.unifiedImageLayouts;
RemoveExtensionFeatureIfUnsuitable(extensions.unified_image_layouts, features.unified_image_layouts,
VK_KHR_UNIFIED_IMAGE_LAYOUTS_EXTENSION_NAME);
// VK_EXT_depth_bias_control // VK_EXT_depth_bias_control
extensions.depth_bias_control = extensions.depth_bias_control =
+3 -1
View File
@@ -70,7 +70,9 @@ VK_DEFINE_HANDLE(VmaAllocator)
FEATURE(KHR, PipelineExecutableProperties, PIPELINE_EXECUTABLE_PROPERTIES, \ FEATURE(KHR, PipelineExecutableProperties, PIPELINE_EXECUTABLE_PROPERTIES, \
pipeline_executable_properties) \ pipeline_executable_properties) \
FEATURE(KHR, WorkgroupMemoryExplicitLayout, WORKGROUP_MEMORY_EXPLICIT_LAYOUT, \ FEATURE(KHR, WorkgroupMemoryExplicitLayout, WORKGROUP_MEMORY_EXPLICIT_LAYOUT, \
workgroup_memory_explicit_layout) workgroup_memory_explicit_layout) \
FEATURE(KHR, UnifiedImageLayouts, UNIFIED_IMAGE_LAYOUTS, unified_image_layouts)
// Define miscellaneous extensions which may be used by the implementation here. // Define miscellaneous extensions which may be used by the implementation here.
#define FOR_EACH_VK_EXTENSION(EXTENSION) \ #define FOR_EACH_VK_EXTENSION(EXTENSION) \
+126 -41
View File
@@ -1,7 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#include "yuzu/game_list.h"
#include <QApplication> #include <QApplication>
#include <QDir> #include <QDir>
#include <QFileInfo> #include <QFileInfo>
@@ -11,22 +10,28 @@
#include <QJsonObject> #include <QJsonObject>
#include <QList> #include <QList>
#include <QMenu> #include <QMenu>
#include <QScroller>
#include <QScrollBar>
#include <QThreadPool> #include <QThreadPool>
#include <QToolButton> #include <QToolButton>
#include <QVariantAnimation>
#include <fmt/ranges.h>
#include <qnamespace.h>
#include <qscroller.h>
#include <qscrollerproperties.h>
#include "common/common_types.h" #include "common/common_types.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/core.h" #include "core/core.h"
#include "core/file_sys/patch_manager.h" #include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h" #include "core/file_sys/registered_cache.h"
#include "qt_common/util/game.h"
#include "qt_common/config/uisettings.h" #include "qt_common/config/uisettings.h"
#include "qt_common/util/game.h"
#include "yuzu/compatibility_list.h" #include "yuzu/compatibility_list.h"
#include "yuzu/game_list.h"
#include "yuzu/game_list_p.h" #include "yuzu/game_list_p.h"
#include "yuzu/game_list_worker.h" #include "yuzu/game_list_worker.h"
#include "yuzu/main_window.h" #include "yuzu/main_window.h"
#include "yuzu/util/controller_navigation.h" #include "yuzu/util/controller_navigation.h"
#include <fmt/ranges.h>
#include <regex>
GameListSearchField::KeyReleaseEater::KeyReleaseEater(GameList* gamelist_, QObject* parent) GameListSearchField::KeyReleaseEater::KeyReleaseEater(GameList* gamelist_, QObject* parent)
: QObject(parent), gamelist{gamelist_} {} : QObject(parent), gamelist{gamelist_} {}
@@ -328,6 +333,22 @@ GameList::GameList(FileSys::VirtualFilesystem vfs_, FileSys::ManualContentProvid
item_model = new QStandardItemModel(tree_view); item_model = new QStandardItemModel(tree_view);
tree_view->setModel(item_model); tree_view->setModel(item_model);
SetupScrollAnimation();
tree_view->viewport()->installEventFilter(this);
// touch gestures
tree_view->viewport()->grabGesture(Qt::SwipeGesture);
tree_view->viewport()->grabGesture(Qt::PanGesture);
// TODO: touch?
QScroller::grabGesture(tree_view->viewport(), QScroller::LeftMouseButtonGesture);
auto scroller = QScroller::scroller(tree_view->viewport());
QScrollerProperties props;
props.setScrollMetric(QScrollerProperties::HorizontalOvershootPolicy, QScrollerProperties::OvershootAlwaysOff);
props.setScrollMetric(QScrollerProperties::VerticalOvershootPolicy, QScrollerProperties::OvershootAlwaysOff);
scroller->setScrollerProperties(props);
tree_view->setAlternatingRowColors(true); tree_view->setAlternatingRowColors(true);
tree_view->setSelectionMode(QHeaderView::SingleSelection); tree_view->setSelectionMode(QHeaderView::SingleSelection);
tree_view->setSelectionBehavior(QHeaderView::SelectRows); tree_view->setSelectionBehavior(QHeaderView::SelectRows);
@@ -352,7 +373,7 @@ GameList::GameList(FileSys::VirtualFilesystem vfs_, FileSys::ManualContentProvid
connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu); connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu);
connect(tree_view, &QTreeView::expanded, this, &GameList::OnItemExpanded); connect(tree_view, &QTreeView::expanded, this, &GameList::OnItemExpanded);
connect(tree_view, &QTreeView::collapsed, this, &GameList::OnItemExpanded); connect(tree_view, &QTreeView::collapsed, this, &GameList::OnItemExpanded);
connect(controller_navigation, &ControllerNavigation::TriggerKeyboardEvent, connect(controller_navigation, &ControllerNavigation::TriggerKeyboardEvent, this,
[this](Qt::Key key) { [this](Qt::Key key) {
// Avoid pressing buttons while playing // Avoid pressing buttons while playing
if (system.IsPoweredOn()) { if (system.IsPoweredOn()) {
@@ -477,7 +498,7 @@ void GameList::DonePopulating(const QStringList& watch_list) {
UISettings::values.favorited_ids.size() == 0); UISettings::values.favorited_ids.size() == 0);
tree_view->setExpanded(item_model->invisibleRootItem()->child(0)->index(), tree_view->setExpanded(item_model->invisibleRootItem()->child(0)->index(),
UISettings::values.favorites_expanded.GetValue()); UISettings::values.favorites_expanded.GetValue());
for (const auto id : UISettings::values.favorited_ids) { for (const auto id : std::as_const(UISettings::values.favorited_ids)) {
AddFavorite(id); AddFavorite(id);
} }
@@ -613,73 +634,73 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
navigate_to_gamedb_entry->setVisible(it != compatibility_list.end() && program_id != 0); navigate_to_gamedb_entry->setVisible(it != compatibility_list.end() && program_id != 0);
connect(favorite, &QAction::triggered, [this, program_id]() { ToggleFavorite(program_id); }); connect(favorite, &QAction::triggered, this, [this, program_id]() { ToggleFavorite(program_id); });
connect(open_save_location, &QAction::triggered, [this, program_id, path]() { connect(open_save_location, &QAction::triggered, this, [this, program_id, path]() {
emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData, path); emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData, path);
}); });
connect(start_game, &QAction::triggered, connect(start_game, &QAction::triggered, this,
[this, path]() { emit BootGame(QString::fromStdString(path), StartGameType::Normal); }); [this, path]() { emit BootGame(QString::fromStdString(path), StartGameType::Normal); });
connect(start_game_global, &QAction::triggered, connect(start_game_global, &QAction::triggered, this,
[this, path]() { emit BootGame(QString::fromStdString(path), StartGameType::Global); }); [this, path]() { emit BootGame(QString::fromStdString(path), StartGameType::Global); });
connect(open_mod_location, &QAction::triggered, [this, program_id, path]() { connect(open_mod_location, &QAction::triggered, this, [this, program_id, path]() {
emit OpenFolderRequested(program_id, GameListOpenTarget::ModData, path); emit OpenFolderRequested(program_id, GameListOpenTarget::ModData, path);
}); });
connect(open_transferable_shader_cache, &QAction::triggered, connect(open_transferable_shader_cache, &QAction::triggered, this,
[this, program_id]() { emit OpenTransferableShaderCacheRequested(program_id); }); [this, program_id]() { emit OpenTransferableShaderCacheRequested(program_id); });
connect(remove_all_content, &QAction::triggered, [this, program_id]() { connect(remove_all_content, &QAction::triggered, this, [this, program_id]() {
emit RemoveInstalledEntryRequested(program_id, QtCommon::Game::InstalledEntryType::Game); emit RemoveInstalledEntryRequested(program_id, QtCommon::Game::InstalledEntryType::Game);
}); });
connect(remove_update, &QAction::triggered, [this, program_id]() { connect(remove_update, &QAction::triggered, this, [this, program_id]() {
emit RemoveInstalledEntryRequested(program_id, QtCommon::Game::InstalledEntryType::Update); emit RemoveInstalledEntryRequested(program_id, QtCommon::Game::InstalledEntryType::Update);
}); });
connect(remove_dlc, &QAction::triggered, [this, program_id]() { connect(remove_dlc, &QAction::triggered, this, [this, program_id]() {
emit RemoveInstalledEntryRequested(program_id, QtCommon::Game::InstalledEntryType::AddOnContent); emit RemoveInstalledEntryRequested(program_id, QtCommon::Game::InstalledEntryType::AddOnContent);
}); });
connect(remove_gl_shader_cache, &QAction::triggered, [this, program_id, path]() { connect(remove_gl_shader_cache, &QAction::triggered, this, [this, program_id, path]() {
emit RemoveFileRequested(program_id, QtCommon::Game::GameListRemoveTarget::GlShaderCache, path); emit RemoveFileRequested(program_id, QtCommon::Game::GameListRemoveTarget::GlShaderCache, path);
}); });
connect(remove_vk_shader_cache, &QAction::triggered, [this, program_id, path]() { connect(remove_vk_shader_cache, &QAction::triggered, this, [this, program_id, path]() {
emit RemoveFileRequested(program_id, QtCommon::Game::GameListRemoveTarget::VkShaderCache, path); emit RemoveFileRequested(program_id, QtCommon::Game::GameListRemoveTarget::VkShaderCache, path);
}); });
connect(remove_shader_cache, &QAction::triggered, [this, program_id, path]() { connect(remove_shader_cache, &QAction::triggered, this, [this, program_id, path]() {
emit RemoveFileRequested(program_id, QtCommon::Game::GameListRemoveTarget::AllShaderCache, path); emit RemoveFileRequested(program_id, QtCommon::Game::GameListRemoveTarget::AllShaderCache, path);
}); });
connect(remove_custom_config, &QAction::triggered, [this, program_id, path]() { connect(remove_custom_config, &QAction::triggered, this, [this, program_id, path]() {
emit RemoveFileRequested(program_id, QtCommon::Game::GameListRemoveTarget::CustomConfiguration, path); emit RemoveFileRequested(program_id, QtCommon::Game::GameListRemoveTarget::CustomConfiguration, path);
}); });
connect(set_play_time, &QAction::triggered, connect(set_play_time, &QAction::triggered, this,
[this, program_id]() { emit SetPlayTimeRequested(program_id); }); [this, program_id]() { emit SetPlayTimeRequested(program_id); });
connect(remove_play_time_data, &QAction::triggered, connect(remove_play_time_data, &QAction::triggered, this,
[this, program_id]() { emit RemovePlayTimeRequested(program_id); }); [this, program_id]() { emit RemovePlayTimeRequested(program_id); });
connect(remove_cache_storage, &QAction::triggered, [this, program_id, path] { connect(remove_cache_storage, &QAction::triggered, this, [this, program_id, path] {
emit RemoveFileRequested(program_id, QtCommon::Game::GameListRemoveTarget::CacheStorage, path); emit RemoveFileRequested(program_id, QtCommon::Game::GameListRemoveTarget::CacheStorage, path);
}); });
connect(dump_romfs, &QAction::triggered, [this, program_id, path]() { connect(dump_romfs, &QAction::triggered, this, [this, program_id, path]() {
emit DumpRomFSRequested(program_id, path, DumpRomFSTarget::Normal); emit DumpRomFSRequested(program_id, path, DumpRomFSTarget::Normal);
}); });
connect(dump_romfs_sdmc, &QAction::triggered, [this, program_id, path]() { connect(dump_romfs_sdmc, &QAction::triggered, this, [this, program_id, path]() {
emit DumpRomFSRequested(program_id, path, DumpRomFSTarget::SDMC); emit DumpRomFSRequested(program_id, path, DumpRomFSTarget::SDMC);
}); });
connect(verify_integrity, &QAction::triggered, connect(verify_integrity, &QAction::triggered, this,
[this, path]() { emit VerifyIntegrityRequested(path); }); [this, path]() { emit VerifyIntegrityRequested(path); });
connect(copy_tid, &QAction::triggered, connect(copy_tid, &QAction::triggered, this,
[this, program_id]() { emit CopyTIDRequested(program_id); }); [this, program_id]() { emit CopyTIDRequested(program_id); });
connect(navigate_to_gamedb_entry, &QAction::triggered, [this, program_id]() { connect(navigate_to_gamedb_entry, &QAction::triggered, this, [this, program_id]() {
emit NavigateToGamedbEntryRequested(program_id, compatibility_list); emit NavigateToGamedbEntryRequested(program_id, compatibility_list);
}); });
// TODO: Implement shortcut creation for macOS // TODO: Implement shortcut creation for macOS
#if !defined(__APPLE__) #if !defined(__APPLE__)
connect(create_desktop_shortcut, &QAction::triggered, [this, program_id, path]() { connect(create_desktop_shortcut, &QAction::triggered, this, [this, program_id, path]() {
emit CreateShortcut(program_id, path, QtCommon::Game::ShortcutTarget::Desktop); emit CreateShortcut(program_id, path, QtCommon::Game::ShortcutTarget::Desktop);
}); });
connect(create_applications_menu_shortcut, &QAction::triggered, [this, program_id, path]() { connect(create_applications_menu_shortcut, &QAction::triggered, this, [this, program_id, path]() {
emit CreateShortcut(program_id, path, QtCommon::Game::ShortcutTarget::Applications); emit CreateShortcut(program_id, path, QtCommon::Game::ShortcutTarget::Applications);
}); });
#endif #endif
connect(properties, &QAction::triggered, connect(properties, &QAction::triggered, this,
[this, path]() { emit OpenPerGameGeneralRequested(path); }); [this, path]() { emit OpenPerGameGeneralRequested(path); });
connect(ryujinx, &QAction::triggered, [this, program_id]() { emit LinkToRyujinxRequested(program_id); connect(ryujinx, &QAction::triggered, this, [this, program_id]() { emit LinkToRyujinxRequested(program_id);
}); });
}; };
@@ -693,11 +714,11 @@ void GameList::AddCustomDirPopup(QMenu& context_menu, QModelIndex selected) {
deep_scan->setCheckable(true); deep_scan->setCheckable(true);
deep_scan->setChecked(game_dir.deep_scan); deep_scan->setChecked(game_dir.deep_scan);
connect(deep_scan, &QAction::triggered, [this, &game_dir] { connect(deep_scan, &QAction::triggered, this, [this, &game_dir] {
game_dir.deep_scan = !game_dir.deep_scan; game_dir.deep_scan = !game_dir.deep_scan;
PopulateAsync(UISettings::values.game_dirs); PopulateAsync(UISettings::values.game_dirs);
}); });
connect(delete_dir, &QAction::triggered, [this, &game_dir, selected] { connect(delete_dir, &QAction::triggered, this, [this, &game_dir, selected] {
UISettings::values.game_dirs.removeOne(game_dir); UISettings::values.game_dirs.removeOne(game_dir);
item_model->invisibleRootItem()->removeRow(selected.row()); item_model->invisibleRootItem()->removeRow(selected.row());
OnTextChanged(search_field->filterText()); OnTextChanged(search_field->filterText());
@@ -716,7 +737,7 @@ void GameList::AddPermDirPopup(QMenu& context_menu, QModelIndex selected) {
move_up->setEnabled(row > 1); move_up->setEnabled(row > 1);
move_down->setEnabled(row < item_model->rowCount() - 2); move_down->setEnabled(row < item_model->rowCount() - 2);
connect(move_up, &QAction::triggered, [this, selected, row, game_dir_index] { connect(move_up, &QAction::triggered, this, [this, selected, row, game_dir_index] {
const int other_index = selected.sibling(row - 1, 0).data(GameListDir::GameDirRole).toInt(); const int other_index = selected.sibling(row - 1, 0).data(GameListDir::GameDirRole).toInt();
// swap the items in the settings // swap the items in the settings
std::swap(UISettings::values.game_dirs[game_dir_index], std::swap(UISettings::values.game_dirs[game_dir_index],
@@ -732,7 +753,7 @@ void GameList::AddPermDirPopup(QMenu& context_menu, QModelIndex selected) {
UISettings::values.game_dirs[other_index].expanded); UISettings::values.game_dirs[other_index].expanded);
}); });
connect(move_down, &QAction::triggered, [this, selected, row, game_dir_index] { connect(move_down, &QAction::triggered, this, [this, selected, row, game_dir_index] {
const int other_index = selected.sibling(row + 1, 0).data(GameListDir::GameDirRole).toInt(); const int other_index = selected.sibling(row + 1, 0).data(GameListDir::GameDirRole).toInt();
// swap the items in the settings // swap the items in the settings
std::swap(UISettings::values.game_dirs[game_dir_index], std::swap(UISettings::values.game_dirs[game_dir_index],
@@ -748,7 +769,7 @@ void GameList::AddPermDirPopup(QMenu& context_menu, QModelIndex selected) {
UISettings::values.game_dirs[other_index].expanded); UISettings::values.game_dirs[other_index].expanded);
}); });
connect(open_directory_location, &QAction::triggered, [this, game_dir_index] { connect(open_directory_location, &QAction::triggered, this, [this, game_dir_index] {
emit OpenDirectory( emit OpenDirectory(
QString::fromStdString(UISettings::values.game_dirs[game_dir_index].path)); QString::fromStdString(UISettings::values.game_dirs[game_dir_index].path));
}); });
@@ -757,8 +778,8 @@ void GameList::AddPermDirPopup(QMenu& context_menu, QModelIndex selected) {
void GameList::AddFavoritesPopup(QMenu& context_menu) { void GameList::AddFavoritesPopup(QMenu& context_menu) {
QAction* clear = context_menu.addAction(tr("Clear")); QAction* clear = context_menu.addAction(tr("Clear"));
connect(clear, &QAction::triggered, [this] { connect(clear, &QAction::triggered, this, [this] {
for (const auto id : UISettings::values.favorited_ids) { for (const auto id : std::as_const(UISettings::values.favorited_ids)) {
RemoveFavorite(id); RemoveFavorite(id);
} }
UISettings::values.favorited_ids.clear(); UISettings::values.favorited_ids.clear();
@@ -788,7 +809,7 @@ void GameList::LoadCompatibilityList() {
const QJsonDocument json = QJsonDocument::fromJson(content); const QJsonDocument json = QJsonDocument::fromJson(content);
const QJsonArray arr = json.array(); const QJsonArray arr = json.array();
for (const QJsonValue value : arr) { for (const QJsonValue &value : arr) {
const QJsonObject game = value.toObject(); const QJsonObject game = value.toObject();
const QString compatibility_key = QStringLiteral("compatibility"); const QString compatibility_key = QStringLiteral("compatibility");
@@ -800,7 +821,7 @@ void GameList::LoadCompatibilityList() {
const QString directory = game[QStringLiteral("directory")].toString(); const QString directory = game[QStringLiteral("directory")].toString();
const QJsonArray ids = game[QStringLiteral("releases")].toArray(); const QJsonArray ids = game[QStringLiteral("releases")].toArray();
for (const QJsonValue id_ref : ids) { for (const QJsonValue &id_ref : ids) {
const QJsonObject id_object = id_ref.toObject(); const QJsonObject id_object = id_ref.toObject();
const QString id = id_object[QStringLiteral("id")].toString(); const QString id = id_object[QStringLiteral("id")].toString();
@@ -919,7 +940,7 @@ void GameList::ToggleFavorite(u64 program_id) {
tree_view->setRowHidden(0, item_model->invisibleRootItem()->index(), true); tree_view->setRowHidden(0, item_model->invisibleRootItem()->index(), true);
} }
} }
SaveConfig(); emit SaveConfig();
} }
void GameList::AddFavorite(u64 program_id) { void GameList::AddFavorite(u64 program_id) {
@@ -989,6 +1010,70 @@ void GameListPlaceholder::mouseDoubleClickEvent(QMouseEvent* event) {
emit GameListPlaceholder::AddDirectory(); emit GameListPlaceholder::AddDirectory();
} }
void GameList::SetupScrollAnimation() {
auto setup = [this](QVariantAnimation* anim, QScrollBar* bar) {
// animation handles moving the bar instead of Qt's built in crap
anim->setEasingCurve(QEasingCurve::OutCubic);
anim->setDuration(200);
connect(anim, &QVariantAnimation::valueChanged, this, [bar](const QVariant& value) {
bar->setValue(value.toInt());
});
};
vertical_scroll = new QVariantAnimation(this);
horizontal_scroll = new QVariantAnimation(this);
setup(vertical_scroll, tree_view->verticalScrollBar());
setup(horizontal_scroll, tree_view->horizontalScrollBar());
}
bool GameList::eventFilter(QObject* obj, QEvent* event) {
if (obj == tree_view->viewport() && event->type() == QEvent::Wheel) {
QWheelEvent* wheelEvent = static_cast<QWheelEvent*>(event);
bool horizontal = wheelEvent->modifiers() & Qt::ShiftModifier;
int deltaX = wheelEvent->angleDelta().x();
int deltaY = wheelEvent->angleDelta().y();
// if shift is held do a horizontal scroll
if (horizontal && deltaY != 0 && deltaX == 0) {
deltaX = deltaY;
deltaY = 0;
}
// TODO(crueter): dedup this
if (deltaY != 0) {
if (vertical_scroll->state() == QAbstractAnimation::Stopped)
vertical_scroll_target = tree_view->verticalScrollBar()->value();
vertical_scroll_target -= deltaY;
vertical_scroll_target = qBound(0, vertical_scroll_target, tree_view->verticalScrollBar()->maximum());
vertical_scroll->stop();
vertical_scroll->setStartValue(tree_view->verticalScrollBar()->value());
vertical_scroll->setEndValue(vertical_scroll_target);
vertical_scroll->start();
}
if (deltaX != 0) {
if (horizontal_scroll->state() == QAbstractAnimation::Stopped)
horizontal_scroll_target = tree_view->horizontalScrollBar()->value();
horizontal_scroll_target -= deltaX;
horizontal_scroll_target = qBound(0, horizontal_scroll_target, tree_view->horizontalScrollBar()->maximum());
horizontal_scroll->stop();
horizontal_scroll->setStartValue(tree_view->horizontalScrollBar()->value());
horizontal_scroll->setEndValue(horizontal_scroll_target);
horizontal_scroll->start();
}
return true;
}
return QWidget::eventFilter(obj, event);
}
void GameListPlaceholder::changeEvent(QEvent* event) { void GameListPlaceholder::changeEvent(QEvent* event) {
if (event->type() == QEvent::LanguageChange) { if (event->type() == QEvent::LanguageChange) {
RetranslateUI(); RetranslateUI();
+10 -1
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2015 Citra Emulator Project // SPDX-FileCopyrightText: 2015 Citra Emulator Project
@@ -25,6 +25,7 @@
#include "yuzu/compatibility_list.h" #include "yuzu/compatibility_list.h"
#include "frontend_common/play_time_manager.h" #include "frontend_common/play_time_manager.h"
class QVariantAnimation;
namespace Core { namespace Core {
class System; class System;
} }
@@ -162,6 +163,14 @@ private:
ControllerNavigation* controller_navigation = nullptr; ControllerNavigation* controller_navigation = nullptr;
CompatibilityList compatibility_list; CompatibilityList compatibility_list;
QVariantAnimation* vertical_scroll = nullptr;
QVariantAnimation* horizontal_scroll = nullptr;
int vertical_scroll_target = 0;
int horizontal_scroll_target = 0;
void SetupScrollAnimation();
bool eventFilter(QObject* obj, QEvent* event) override;
friend class GameListSearchField; friend class GameListSearchField;
const PlayTime::PlayTimeManager& play_time_manager; const PlayTime::PlayTimeManager& play_time_manager;
+2
View File
@@ -3,6 +3,7 @@
// Qt on macOS doesn't define VMA shit // Qt on macOS doesn't define VMA shit
#include <boost/algorithm/string/split.hpp> #include <boost/algorithm/string/split.hpp>
#include "frontend_common/settings_generator.h"
#include "qt_common/qt_string_lookup.h" #include "qt_common/qt_string_lookup.h"
#if defined(QT_STATICPLUGIN) && !defined(__APPLE__) #if defined(QT_STATICPLUGIN) && !defined(__APPLE__)
#undef VMA_IMPLEMENTATION #undef VMA_IMPLEMENTATION
@@ -432,6 +433,7 @@ MainWindow::MainWindow(bool has_broken_vulkan)
Common::Log::Start(); Common::Log::Start();
LoadTranslation(); LoadTranslation();
FrontendCommon::GenerateSettings();
setAcceptDrops(true); setAcceptDrops(true);
ui->setupUi(this); ui->setupUi(this);
+1 -1
View File
@@ -23,7 +23,7 @@ MAXDEPTH=3
# For your project you'll want to change this to define what dirs you have cpmfiles in # For your project you'll want to change this to define what dirs you have cpmfiles in
# Remember to account for the MAXDEPTH variable! # Remember to account for the MAXDEPTH variable!
# Adding ./ before each will help to remove duplicates # Adding ./ before each will help to remove duplicates
CPMFILES=$(find . src -maxdepth "$MAXDEPTH" -name cpmfile.json | sort | uniq) CPMFILES=$(find . -maxdepth "$MAXDEPTH" -name cpmfile.json | sort | uniq)
# shellcheck disable=SC2016 # shellcheck disable=SC2016
PACKAGES=$(echo "$CPMFILES" | xargs jq -s 'reduce .[] as $item ({}; . * $item)') PACKAGES=$(echo "$CPMFILES" | xargs jq -s 'reduce .[] as $item ({}; . * $item)')