mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-15 13:16:43 +00:00
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4464c3dbcb | |||
| ebefa6ea19 | |||
| b6ee847947 | |||
| 6c16440996 | |||
| 09c583506b | |||
| 894add43f3 | |||
| d142b5dd6a | |||
| 0c2894eabf | |||
| d8a8169eb2 | |||
| 81c6e56713 | |||
| b4b41ee62c | |||
| 0d6a2158f0 | |||
| 09b6b3b71e | |||
| 629ebf1bde | |||
| c993bc01a4 | |||
| 6bdb03d8ac | |||
| 599ab16288 | |||
| 30a42c5a6a | |||
| 3aa0d46259 |
@@ -7,7 +7,6 @@
|
||||
# Build directory
|
||||
/[Bb]uild*/
|
||||
doc-build/
|
||||
out/
|
||||
AppDir/
|
||||
uruntime
|
||||
|
||||
|
||||
@@ -63,6 +63,7 @@ if (YUZU_STATIC_ROOM)
|
||||
set(Boost_USE_STATIC_LIBS ON)
|
||||
set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
|
||||
set(OPENSSL_USE_STATIC_LIBS ON)
|
||||
set(OpenSSL_FORCE_SYSTEM ON)
|
||||
|
||||
set(zstd_FORCE_BUNDLED ON)
|
||||
set(fmt_FORCE_BUNDLED ON)
|
||||
|
||||
+165
-180
@@ -1,7 +1,7 @@
|
||||
# SPDX-FileCopyrightText: Copyright 2026 crueter
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
set(CPM_SOURCE_CACHE "${PROJECT_SOURCE_DIR}/.cache/cpm" CACHE STRING "" FORCE)
|
||||
cmake_minimum_required(VERSION 3.31)
|
||||
|
||||
if(MSVC OR ANDROID OR IOS)
|
||||
set(BUNDLED_DEFAULT ON)
|
||||
@@ -9,15 +9,40 @@ else()
|
||||
set(BUNDLED_DEFAULT OFF)
|
||||
endif()
|
||||
|
||||
set(CPM_SOURCE_CACHE "${PROJECT_SOURCE_DIR}/.cache/cpm" CACHE STRING "" FORCE)
|
||||
|
||||
option(CPMUTIL_FORCE_BUNDLED
|
||||
"Force bundled packages for all CPM depdendencies" ${BUNDLED_DEFAULT})
|
||||
|
||||
option(CPMUTIL_FORCE_SYSTEM
|
||||
"Force system packages for all CPM dependencies (NOT RECOMMENDED)" OFF)
|
||||
"Force system packages for all CPM dependencies" OFF)
|
||||
|
||||
set(CPMUTIL_PATCH_DIR "${PROJECT_SOURCE_DIR}/.patch" CACHE STRING
|
||||
"Directory containing patches for packages")
|
||||
|
||||
cmake_minimum_required(VERSION 3.22)
|
||||
include(CPM)
|
||||
|
||||
# Rudimentary target architecture detection
|
||||
if (NOT DEFINED ARCHITECTURE)
|
||||
string(TOLOWER ${CMAKE_SYSTEM_PROCESSOR} processor)
|
||||
if (processor MATCHES "x86|amd64")
|
||||
set(CPMUTIL_AMD64 ON)
|
||||
elseif(processor MATCHES "^aarch64|^arm64|^armv8\.*")
|
||||
set(CPMUTIL_ARM64 ON)
|
||||
elseif(processor MATCHES "riscv")
|
||||
set(CPMUTIL_RISCV64 ON)
|
||||
endif()
|
||||
else()
|
||||
# This block exists for compatibility with my own DetectArchitecture.cmake.
|
||||
if (ARCHITECTURE_x86_64)
|
||||
set(CPMUTIL_AMD64 ON)
|
||||
elseif(ARCHITECTURE_arm64)
|
||||
set(CPMUTIL_ARM64 ON)
|
||||
elseif(ARCHITECTURE_riscv64)
|
||||
set(CPMUTIL_RISCV64 ON)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# cpmfile parsing
|
||||
set(CPMUTIL_JSON_FILE "${CMAKE_CURRENT_SOURCE_DIR}/cpmfile.json")
|
||||
|
||||
@@ -70,7 +95,6 @@ function(get_json_element object out member default)
|
||||
|
||||
if(out_type STREQUAL "ARRAY")
|
||||
string(JSON _len LENGTH "${object}" ${member})
|
||||
# array_to_list("${outvar}" ${_len} outvar)
|
||||
set("${out}_LENGTH" "${_len}" PARENT_SCOPE)
|
||||
endif()
|
||||
|
||||
@@ -148,11 +172,12 @@ macro(parse_object object)
|
||||
get_json_element("${object}" repo repo "")
|
||||
get_json_element("${object}" ci ci OFF)
|
||||
get_json_element("${object}" version version "")
|
||||
get_json_element("${object}" min_version min_version "")
|
||||
get_json_element("${object}" git_host git_host "github.com")
|
||||
|
||||
if(ci)
|
||||
get_json_element("${object}" name name "${JSON_NAME}")
|
||||
get_json_element("${object}" extension extension "tar.zst")
|
||||
get_json_element("${object}" min_version min_version "")
|
||||
get_json_element("${object}" raw_disabled disabled_platforms "")
|
||||
|
||||
if(raw_disabled)
|
||||
@@ -163,14 +188,10 @@ macro(parse_object object)
|
||||
endif()
|
||||
else()
|
||||
get_json_element("${object}" hash hash "")
|
||||
get_json_element("${object}" hash_suffix hash_suffix "")
|
||||
get_json_element("${object}" sha sha "")
|
||||
get_json_element("${object}" url url "")
|
||||
get_json_element("${object}" key key "")
|
||||
get_json_element("${object}" tag tag "")
|
||||
get_json_element("${object}" artifact artifact "")
|
||||
get_json_element("${object}" git_version git_version "")
|
||||
get_json_element("${object}" git_host git_host "")
|
||||
get_json_element("${object}" source_subdir source_subdir "")
|
||||
get_json_element("${object}" bundled bundled "unset")
|
||||
get_json_element("${object}" find_args find_args "")
|
||||
@@ -178,23 +199,17 @@ macro(parse_object object)
|
||||
|
||||
# okay here comes the fun part: REPLACEMENTS!
|
||||
# first: tag gets %VERSION% replaced if applicable,
|
||||
# with either git_version (preferred) or version
|
||||
# with version
|
||||
# second: artifact gets %VERSION% and %TAG% replaced
|
||||
# accordingly (same rules for VERSION)
|
||||
|
||||
if(git_version)
|
||||
set(version_replace ${git_version})
|
||||
else()
|
||||
set(version_replace ${version})
|
||||
endif()
|
||||
|
||||
# TODO(crueter): fmt module for cmake
|
||||
if(tag)
|
||||
string(REPLACE "%VERSION%" "${version_replace}" tag ${tag})
|
||||
string(REPLACE "%VERSION%" "${version}" tag ${tag})
|
||||
endif()
|
||||
|
||||
if(artifact)
|
||||
string(REPLACE "%VERSION%" "${version_replace}"
|
||||
string(REPLACE "%VERSION%" "${version}"
|
||||
artifact ${artifact})
|
||||
string(REPLACE "%TAG%" "${tag}" artifact ${artifact})
|
||||
endif()
|
||||
@@ -207,7 +222,7 @@ macro(parse_object object)
|
||||
string(JSON _patch GET "${raw_patches}" "${IDX}")
|
||||
|
||||
set(full_patch
|
||||
"${PROJECT_SOURCE_DIR}/.patch/${JSON_NAME}/${_patch}")
|
||||
"${CPMUTIL_PATCH_DIR}/${JSON_NAME}/${_patch}")
|
||||
if(NOT EXISTS ${full_patch})
|
||||
cpm_utils_message(FATAL_ERROR ${JSON_NAME}
|
||||
"specifies patch ${full_patch} which does not exist")
|
||||
@@ -242,14 +257,16 @@ function(AddJsonPackage)
|
||||
|
||||
# these are overrides that can be generated at runtime,
|
||||
# so can be defined separately from the json
|
||||
BUNDLED_PACKAGE
|
||||
FORCE_BUNDLED_PACKAGE)
|
||||
|
||||
set(multiValueArgs OPTIONS)
|
||||
|
||||
set(optionArgs MODULE_PATH DOWNLOAD_ONLY)
|
||||
|
||||
cmake_parse_arguments(JSON "${optionArgs}" "${oneValueArgs}" "${multiValueArgs}"
|
||||
cmake_parse_arguments(JSON
|
||||
"${optionArgs}"
|
||||
"${oneValueArgs}"
|
||||
"${multiValueArgs}"
|
||||
"${ARGN}")
|
||||
|
||||
list(LENGTH ARGN argnLength)
|
||||
@@ -260,8 +277,8 @@ function(AddJsonPackage)
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED CPMFILE_CONTENT)
|
||||
cpm_utils_message(WARNING ${name}
|
||||
"No cpmfile, AddJsonPackage is a no-op")
|
||||
cpm_utils_message(FATAL_ERROR ${name}
|
||||
"No cpmfile present")
|
||||
return()
|
||||
endif()
|
||||
|
||||
@@ -288,13 +305,16 @@ function(AddJsonPackage)
|
||||
|
||||
if(ci)
|
||||
AddCIPackage(
|
||||
VERSION ${version}
|
||||
NAME ${name}
|
||||
REPO ${repo}
|
||||
PACKAGE ${package}
|
||||
EXTENSION ${extension}
|
||||
MIN_VERSION ${min_version}
|
||||
DISABLED_PLATFORMS ${disabled_platforms}
|
||||
VERSION "${version}"
|
||||
NAME "${name}"
|
||||
REPO "${repo}"
|
||||
PACKAGE "${package}"
|
||||
EXTENSION "${extension}"
|
||||
MIN_VERSION "${min_version}"
|
||||
DISABLED_PLATFORMS "${disabled_platforms}"
|
||||
|
||||
GIT_HOST "${git_host}"
|
||||
|
||||
${EXTRA_ARGS})
|
||||
else()
|
||||
if (NOT DEFINED JSON_FORCE_BUNDLED_PACKAGE)
|
||||
@@ -304,12 +324,11 @@ function(AddJsonPackage)
|
||||
AddPackage(
|
||||
NAME "${package}"
|
||||
VERSION "${version}"
|
||||
MIN_VERSION "${min_version}"
|
||||
URL "${url}"
|
||||
HASH "${hash}"
|
||||
HASH_SUFFIX "${hash_suffix}"
|
||||
SHA "${sha}"
|
||||
REPO "${repo}"
|
||||
KEY "${key}"
|
||||
PATCHES "${patches}"
|
||||
OPTIONS "${options}"
|
||||
FIND_PACKAGE_ARGUMENTS "${find_args}"
|
||||
@@ -317,11 +336,11 @@ function(AddJsonPackage)
|
||||
FORCE_BUNDLED_PACKAGE "${JSON_FORCE_BUNDLED_PACKAGE}"
|
||||
SOURCE_SUBDIR "${source_subdir}"
|
||||
|
||||
GIT_VERSION "${git_version}"
|
||||
GIT_HOST "${git_host}"
|
||||
|
||||
ARTIFACT "${artifact}"
|
||||
TAG "${tag}"
|
||||
|
||||
${EXTRA_ARGS})
|
||||
endif()
|
||||
|
||||
@@ -339,25 +358,19 @@ function(AddPackage)
|
||||
set(oneValueArgs
|
||||
NAME
|
||||
VERSION
|
||||
GIT_VERSION
|
||||
MIN_VERSION
|
||||
GIT_HOST
|
||||
|
||||
REPO
|
||||
TAG
|
||||
ARTIFACT
|
||||
SHA
|
||||
BRANCH
|
||||
|
||||
HASH
|
||||
HASH_SUFFIX
|
||||
HASH_URL
|
||||
HASH_ALGO
|
||||
|
||||
URL
|
||||
GIT_URL
|
||||
SOURCE_SUBDIR
|
||||
|
||||
KEY
|
||||
SOURCE_SUBDIR
|
||||
BUNDLED_PACKAGE
|
||||
FORCE_BUNDLED_PACKAGE
|
||||
FIND_PACKAGE_ARGUMENTS)
|
||||
@@ -366,7 +379,10 @@ function(AddPackage)
|
||||
|
||||
set(optionArgs MODULE_PATH DOWNLOAD_ONLY)
|
||||
|
||||
cmake_parse_arguments(PKG_ARGS "${optionArgs}" "${oneValueArgs}" "${multiValueArgs}"
|
||||
cmake_parse_arguments(PKG_ARGS
|
||||
"${optionArgs}"
|
||||
"${oneValueArgs}"
|
||||
"${multiValueArgs}"
|
||||
"${ARGN}")
|
||||
|
||||
if(NOT DEFINED PKG_ARGS_NAME)
|
||||
@@ -385,6 +401,7 @@ function(AddPackage)
|
||||
set(CPM_${PKG_ARGS_NAME}_SOURCE ${${PKG_ARGS_NAME}_CUSTOM_DIR})
|
||||
endif()
|
||||
|
||||
# TODO: See if this can be delegated to subshells
|
||||
if(NOT DEFINED PKG_ARGS_GIT_HOST)
|
||||
set(git_host github.com)
|
||||
else()
|
||||
@@ -393,42 +410,22 @@ function(AddPackage)
|
||||
|
||||
if(DEFINED PKG_ARGS_URL)
|
||||
set(pkg_url ${PKG_ARGS_URL})
|
||||
|
||||
if(DEFINED PKG_ARGS_REPO)
|
||||
set(pkg_git_url https://${git_host}/${PKG_ARGS_REPO})
|
||||
else()
|
||||
if(DEFINED PKG_ARGS_GIT_URL)
|
||||
set(pkg_git_url ${PKG_ARGS_GIT_URL})
|
||||
else()
|
||||
set(pkg_git_url ${pkg_url})
|
||||
endif()
|
||||
endif()
|
||||
set(pkg_git_url ${pkg_url})
|
||||
elseif(DEFINED PKG_ARGS_REPO)
|
||||
set(pkg_git_url https://${git_host}/${PKG_ARGS_REPO})
|
||||
|
||||
if(DEFINED PKG_ARGS_TAG)
|
||||
set(pkg_key ${PKG_ARGS_TAG})
|
||||
|
||||
if(DEFINED PKG_ARGS_ARTIFACT)
|
||||
set(pkg_url
|
||||
"${pkg_git_url}/releases/download/${PKG_ARGS_TAG}/${PKG_ARGS_ARTIFACT}")
|
||||
else()
|
||||
set(pkg_url
|
||||
${pkg_git_url}/archive/refs/tags/${PKG_ARGS_TAG}.tar.gz)
|
||||
endif()
|
||||
elseif(DEFINED PKG_ARGS_SHA)
|
||||
if(DEFINED PKG_ARGS_SHA)
|
||||
set(pkg_url "${pkg_git_url}/archive/${PKG_ARGS_SHA}.tar.gz")
|
||||
else()
|
||||
if(DEFINED PKG_ARGS_BRANCH)
|
||||
set(PKG_BRANCH ${PKG_ARGS_BRANCH})
|
||||
elseif(DEFINED PKG_ARGS_TAG)
|
||||
set(tag "${PKG_ARGS_TAG}")
|
||||
if(DEFINED PKG_ARGS_ARTIFACT)
|
||||
set(artifact "${PKG_ARGS_ARTIFACT}")
|
||||
set(pkg_url
|
||||
"${pkg_git_url}/releases/download/${tag}/${artifact}")
|
||||
else()
|
||||
cpm_utils_message(WARNING ${PKG_ARGS_NAME}
|
||||
"REPO defined but no TAG, SHA, BRANCH, or URL"
|
||||
"specified, defaulting to master")
|
||||
set(PKG_BRANCH master)
|
||||
set(pkg_url
|
||||
"${pkg_git_url}/archive/refs/tags/${tag}.tar.gz")
|
||||
endif()
|
||||
|
||||
set(pkg_url ${pkg_git_url}/archive/refs/heads/${PKG_BRANCH}.tar.gz)
|
||||
endif()
|
||||
else()
|
||||
cpm_utils_message(FATAL_ERROR ${PKG_ARGS_NAME}
|
||||
@@ -437,75 +434,24 @@ function(AddPackage)
|
||||
|
||||
cpm_utils_message(DEBUG ${PKG_ARGS_NAME} "Download URL is ${pkg_url}")
|
||||
|
||||
if(NOT DEFINED PKG_ARGS_KEY)
|
||||
if(DEFINED PKG_ARGS_SHA)
|
||||
string(SUBSTRING ${PKG_ARGS_SHA} 0 4 pkg_key)
|
||||
cpm_utils_message(DEBUG ${PKG_ARGS_NAME}
|
||||
"No custom key defined, using ${pkg_key} from sha")
|
||||
elseif(DEFINED PKG_ARGS_GIT_VERSION)
|
||||
set(pkg_key ${PKG_ARGS_GIT_VERSION})
|
||||
cpm_utils_message(DEBUG ${PKG_ARGS_NAME}
|
||||
"No custom key defined, using ${pkg_key}")
|
||||
elseif(DEFINED PKG_ARGS_TAG)
|
||||
set(pkg_key ${PKG_ARGS_TAG})
|
||||
cpm_utils_message(DEBUG ${PKG_ARGS_NAME}
|
||||
"No custom key defined, using ${pkg_key}")
|
||||
elseif(DEFINED PKG_ARGS_VERSION)
|
||||
set(pkg_key ${PKG_ARGS_VERSION})
|
||||
cpm_utils_message(DEBUG ${PKG_ARGS_NAME}
|
||||
"No custom key defined, using ${pkg_key}")
|
||||
else()
|
||||
cpm_utils_message(WARNING ${PKG_ARGS_NAME}
|
||||
"Could not determine cache key, using CPM defaults")
|
||||
endif()
|
||||
if(DEFINED PKG_ARGS_SHA)
|
||||
string(SUBSTRING ${PKG_ARGS_SHA} 0 4 pkg_key)
|
||||
elseif(DEFINED PKG_ARGS_VERSION)
|
||||
set(pkg_key ${PKG_ARGS_VERSION})
|
||||
elseif(DEFINED PKG_ARGS_TAG)
|
||||
set(pkg_key ${PKG_ARGS_TAG})
|
||||
elseif(DEFINED PKG_ARGS_MIN_VERSION)
|
||||
set(pkg_key ${PKG_ARGS_MIN_VERSION})
|
||||
else()
|
||||
set(pkg_key ${PKG_ARGS_KEY})
|
||||
endif()
|
||||
|
||||
if(DEFINED PKG_ARGS_HASH_ALGO)
|
||||
set(hash_algo ${PKG_ARGS_HASH_ALGO})
|
||||
else()
|
||||
set(hash_algo SHA512)
|
||||
cpm_utils_message(FATAL_ERROR ${PKG_ARGS_NAME}
|
||||
"Could not determine cache key")
|
||||
endif()
|
||||
|
||||
if(DEFINED PKG_ARGS_HASH)
|
||||
set(pkg_hash "${hash_algo}=${PKG_ARGS_HASH}")
|
||||
elseif(DEFINED PKG_ARGS_HASH_SUFFIX)
|
||||
# funny sanity check
|
||||
string(TOLOWER ${hash_algo} hash_algo_lower)
|
||||
string(TOLOWER ${PKG_ARGS_HASH_SUFFIX} suffix_lower)
|
||||
if(NOT ${suffix_lower} MATCHES ${hash_algo_lower})
|
||||
cpm_utils_message(WARNING
|
||||
"Hash algorithm and hash suffix do not match, errors may occur")
|
||||
endif()
|
||||
|
||||
set(hash_url ${pkg_url}.${PKG_ARGS_HASH_SUFFIX})
|
||||
elseif(DEFINED PKG_ARGS_HASH_URL)
|
||||
set(hash_url ${PKG_ARGS_HASH_URL})
|
||||
set(pkg_hash "SHA512=${PKG_ARGS_HASH}")
|
||||
else()
|
||||
cpm_utils_message(WARNING ${PKG_ARGS_NAME}
|
||||
"No hash or hash URL found")
|
||||
endif()
|
||||
|
||||
if(DEFINED hash_url)
|
||||
set(outfile ${CMAKE_CURRENT_BINARY_DIR}/${PKG_ARGS_NAME}.hash)
|
||||
|
||||
# TODO(crueter): This is kind of a bad solution
|
||||
# because "technically" the hash is invalidated each week
|
||||
# but it works for now kjsdnfkjdnfjksdn
|
||||
string(TOLOWER ${PKG_ARGS_NAME} lowername)
|
||||
if(NOT EXISTS ${outfile} AND NOT EXISTS
|
||||
${CPM_SOURCE_CACHE}/${lowername}/${pkg_key})
|
||||
file(DOWNLOAD ${hash_url} ${outfile})
|
||||
endif()
|
||||
|
||||
if(EXISTS ${outfile})
|
||||
file(READ ${outfile} pkg_hash_tmp)
|
||||
endif()
|
||||
|
||||
if(DEFINED ${pkg_hash_tmp})
|
||||
set(pkg_hash "${hash_algo}=${pkg_hash_tmp}")
|
||||
endif()
|
||||
cpm_utils_message(FATAL_ERROR ${PKG_ARGS_NAME}
|
||||
"No hash defined")
|
||||
endif()
|
||||
|
||||
macro(set_precedence local force)
|
||||
@@ -545,9 +491,9 @@ function(AddPackage)
|
||||
set_precedence(ON OFF)
|
||||
endif()
|
||||
|
||||
if(DEFINED PKG_ARGS_VERSION)
|
||||
if(DEFINED PKG_ARGS_MIN_VERSION)
|
||||
list(APPEND EXTRA_ARGS
|
||||
VERSION ${PKG_ARGS_VERSION})
|
||||
VERSION ${PKG_ARGS_MIN_VERSION})
|
||||
endif()
|
||||
|
||||
if (PKG_ARGS_FIND_PACKAGE_ARGUMENTS)
|
||||
@@ -575,10 +521,10 @@ function(AddPackage)
|
||||
endif()
|
||||
|
||||
CPMAddPackage(
|
||||
NAME "${PKG_ARGS_NAME}"
|
||||
URL "${pkg_url}"
|
||||
URL_HASH "${pkg_hash}"
|
||||
CUSTOM_CACHE_KEY "${pkg_key}"
|
||||
NAME ${PKG_ARGS_NAME}
|
||||
URL ${pkg_url}
|
||||
URL_HASH ${pkg_hash}
|
||||
CUSTOM_CACHE_KEY ${pkg_key}
|
||||
|
||||
EXCLUDE_FROM_ALL ON
|
||||
|
||||
@@ -593,15 +539,15 @@ function(AddPackage)
|
||||
if(DEFINED PKG_ARGS_SHA)
|
||||
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS
|
||||
${PKG_ARGS_SHA})
|
||||
elseif(DEFINED PKG_ARGS_GIT_VERSION)
|
||||
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS
|
||||
${PKG_ARGS_GIT_VERSION})
|
||||
elseif(DEFINED PKG_ARGS_TAG)
|
||||
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS
|
||||
${PKG_ARGS_TAG})
|
||||
elseif(DEFINED PKG_ARGS_VERSION)
|
||||
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS
|
||||
${PKG_ARGS_VERSION})
|
||||
elseif(DEFINED PKG_ARGS_TAG)
|
||||
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS
|
||||
${PKG_ARGS_TAG})
|
||||
elseif(DEFINED PKG_ARGS_MIN_VERSION)
|
||||
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS
|
||||
${PKG_ARGS_MIN_VERSION})
|
||||
else()
|
||||
cpm_utils_message(WARNING ${PKG_ARGS_NAME}
|
||||
"Package has no specified sha, tag, or version")
|
||||
@@ -638,7 +584,8 @@ function(AddCIPackage)
|
||||
REPO
|
||||
PACKAGE
|
||||
EXTENSION
|
||||
MIN_VERSION)
|
||||
MIN_VERSION
|
||||
GIT_HOST)
|
||||
|
||||
set(multiValueArgs DISABLED_PLATFORMS)
|
||||
|
||||
@@ -650,6 +597,7 @@ function(AddCIPackage)
|
||||
"${multiValueArgs}"
|
||||
${ARGN})
|
||||
|
||||
# TODO: use cpm_utils_message
|
||||
if(NOT DEFINED PKG_ARGS_VERSION)
|
||||
message(FATAL_ERROR "[CPMUtil] VERSION is required")
|
||||
endif()
|
||||
@@ -675,6 +623,12 @@ function(AddCIPackage)
|
||||
set(ARTIFACT_EXT ${PKG_ARGS_EXTENSION})
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED PKG_ARGS_GIT_HOST)
|
||||
set(ARTIFACT_GIT_HOST "github.com")
|
||||
else()
|
||||
set(ARTIFACT_GIT_HOST "${PKG_ARGS_GIT_HOST}")
|
||||
endif()
|
||||
|
||||
if(DEFINED PKG_ARGS_MIN_VERSION)
|
||||
set(ARTIFACT_MIN_VERSION ${PKG_ARGS_MIN_VERSION})
|
||||
endif()
|
||||
@@ -689,49 +643,81 @@ function(AddCIPackage)
|
||||
set(ARTIFACT_REPO ${PKG_ARGS_REPO})
|
||||
set(ARTIFACT_PACKAGE ${PKG_ARGS_PACKAGE})
|
||||
|
||||
if(MSVC AND ARCHITECTURE_x86_64)
|
||||
set(pkgname windows-amd64)
|
||||
elseif(MSVC AND ARCHITECTURE_arm64)
|
||||
set(pkgname windows-arm64)
|
||||
elseif(MINGW AND ARCHITECTURE_x86_64)
|
||||
set(pkgname mingw-amd64)
|
||||
elseif(MINGW AND ARCHITECTURE_arm64)
|
||||
set(pkgname mingw-arm64)
|
||||
elseif(ANDROID AND ARCHITECTURE_x86_64)
|
||||
set(pkgname android-x86_64)
|
||||
elseif(ANDROID AND ARCHITECTURE_arm64)
|
||||
set(pkgname android-aarch64)
|
||||
elseif(PLATFORM_SUN)
|
||||
set(pkgname solaris-amd64)
|
||||
elseif(PLATFORM_FREEBSD)
|
||||
set(pkgname freebsd-amd64)
|
||||
elseif(PLATFORM_LINUX AND ARCHITECTURE_x86_64)
|
||||
set(pkgname linux-amd64)
|
||||
elseif(PLATFORM_LINUX AND ARCHITECTURE_arm64)
|
||||
set(pkgname linux-aarch64)
|
||||
elseif(APPLE AND NOT IOS)
|
||||
set(pkgname macos-universal)
|
||||
elseif(IOS AND ARCHITECTURE_arm64)
|
||||
set(pkgname ios-aarch64)
|
||||
# TODO: Use amd64/aarch64 naming for everything.
|
||||
# Also drop macos universal
|
||||
|
||||
if (MSVC)
|
||||
set(platname windows)
|
||||
elseif(MINGW)
|
||||
set(platname mingw)
|
||||
elseif(ANDROID)
|
||||
set(platname android)
|
||||
elseif(LINUX)
|
||||
set(platname linux)
|
||||
elseif(IOS)
|
||||
set(platname ios)
|
||||
elseif(APPLE)
|
||||
set(platname macos)
|
||||
else()
|
||||
cpm_utils_message(WARNING ${PKG_ARGS_NAME}
|
||||
"Unsupported platform ${CMAKE_SYSTEM_NAME} for CI packages")
|
||||
endif()
|
||||
|
||||
if (DEFINED pkgname AND NOT "${pkgname}" IN_LIST DISABLED_PLATFORMS)
|
||||
if (APPLE AND NOT IOS)
|
||||
set(archname universal)
|
||||
elseif((WIN32 OR LINUX) AND CPMUTIL_AMD64)
|
||||
set(archname amd64)
|
||||
elseif(WIN32 AND CPMUTIL_ARM64)
|
||||
set(archname arm64)
|
||||
elseif((IOS OR LINUX OR ANDROID) AND CPMUTIL_ARM64)
|
||||
set(archname aarch64)
|
||||
elseif(ANDROID AND CPMUTIL_AMD64)
|
||||
set(archname x86_64)
|
||||
else()
|
||||
cpm_utils_message(WARNING ${PKG_ARGS_NAME}
|
||||
"Unsupported platform/arch combo for CI packages")
|
||||
endif()
|
||||
|
||||
if (DEFINED platname AND DEFINED archname)
|
||||
set(pkgname ${platname}-${archname})
|
||||
endif()
|
||||
|
||||
if (DEFINED pkgname
|
||||
AND NOT "${pkgname}" IN_LIST DISABLED_PLATFORMS)
|
||||
set(ARTIFACT
|
||||
"${ARTIFACT_NAME}-${pkgname}-${ARTIFACT_VERSION}.${ARTIFACT_EXT}")
|
||||
|
||||
if (PKG_ARGS_MODULE_PATH)
|
||||
list(APPEND EXTRA_ARGS MODULE_PATH)
|
||||
set(EXTRA_ARGS MODULE_PATH)
|
||||
endif()
|
||||
|
||||
# download sha512sum file
|
||||
# TODO:
|
||||
set(sha512sum_url
|
||||
"https://${ARTIFACT_GIT_HOST}/${ARTIFACT_REPO}/releases/download/v${ARTIFACT_VERSION}/${ARTIFACT}.sha512sum")
|
||||
set(sha512sum_file
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/.cpmutil_${ARTIFACT}_sha512sum")
|
||||
|
||||
file(DOWNLOAD "${sha512sum_url}" "${sha512sum_file}"
|
||||
STATUS sha512sum_status)
|
||||
list(GET sha512sum_status 0 sha512sum_error)
|
||||
|
||||
if(sha512sum_error)
|
||||
message(FATAL_ERROR "[CPMUtil] Failed to download sha512sum "
|
||||
"for ${ARTIFACT_NAME} from ${sha512sum_url}")
|
||||
endif()
|
||||
|
||||
file(READ "${sha512sum_file}" sha512sum_hash)
|
||||
string(STRIP "${sha512sum_hash}" sha512sum_hash)
|
||||
file(REMOVE "${sha512sum_file}")
|
||||
|
||||
AddPackage(
|
||||
NAME ${ARTIFACT_PACKAGE}
|
||||
REPO ${ARTIFACT_REPO}
|
||||
TAG "v${ARTIFACT_VERSION}"
|
||||
GIT_VERSION ${ARTIFACT_VERSION}
|
||||
MIN_VERSION ${ARTIFACT_VERSION}
|
||||
ARTIFACT ${ARTIFACT}
|
||||
|
||||
KEY "${pkgname}-${ARTIFACT_VERSION}"
|
||||
HASH_SUFFIX sha512sum
|
||||
HASH ${sha512sum_hash}
|
||||
FORCE_BUNDLED_PACKAGE ON
|
||||
${EXTRA_ARGS})
|
||||
|
||||
@@ -761,7 +747,6 @@ function(AddQt repo version)
|
||||
REPO ${repo}
|
||||
DISABLED_PLATFORMS
|
||||
android-x86_64 android-aarch64
|
||||
freebsd-amd64 solaris-amd64 openbsd-amd64
|
||||
MODULE_PATH)
|
||||
|
||||
find_package(Qt6 REQUIRED PATHS ${Qt6_SOURCE_DIR} NO_DEFAULT_PATH)
|
||||
|
||||
+368
-111
@@ -1,18 +1,229 @@
|
||||
{
|
||||
"biscuit": {
|
||||
"hash": "1229f345b014f7ca544dedb4edb3311e41ba736f9aa9a67f88b5f26f3c983288c6bb6cdedcfb0b8a02c63088a37e6a0d7ba97d9c2a4d721b213916327cffe28a",
|
||||
"min_version": "0.9.1",
|
||||
"repo": "lioncash/biscuit",
|
||||
"tag": "v%VERSION%",
|
||||
"version": "0.19.0"
|
||||
},
|
||||
"boost": {
|
||||
"artifact": "%TAG%-cmake.tar.xz",
|
||||
"find_args": "CONFIG OPTIONAL_COMPONENTS headers context system fiber filesystem",
|
||||
"hash": "6ae6e94664fe7f2fb01976b59b276ac5df8085c7503fa829d810fbfe495960cfec44fa2c36e2cb23480bc19c956ed199d4952b02639a00a6c07625d4e7130c2d",
|
||||
"min_version": "1.57",
|
||||
"package": "Boost",
|
||||
"patches": [
|
||||
"0001-clang-cl.patch"
|
||||
],
|
||||
"repo": "boostorg/boost",
|
||||
"tag": "boost-%VERSION%",
|
||||
"version": "1.90.0"
|
||||
},
|
||||
"boost_headers": {
|
||||
"bundled": true,
|
||||
"hash": "4ef845775e2277a8104ded6ddf749aa262ce52cf8438042869a048f9a0156dd772fbbcfa74efa1378fecef339b7286f6fe4b4feb5c45d49966b35d08e3e83507",
|
||||
"repo": "boostorg/headers",
|
||||
"tag": "boost-%VERSION%",
|
||||
"version": "1.90.0"
|
||||
},
|
||||
"catch2": {
|
||||
"hash": "7eea385d79d88a5690cde131fe7ccda97d5c54ea09d6f515000d7bf07c828809d61c1ac99912c1ee507cf933f61c1c47ecdcc45df7850ffa82714034b0fccf35",
|
||||
"min_version": "3.0.1",
|
||||
"package": "Catch2",
|
||||
"patches": [
|
||||
"0001-solaris-isnan-fix.patch"
|
||||
],
|
||||
"repo": "catchorg/Catch2",
|
||||
"tag": "v%VERSION%",
|
||||
"version": "3.13.0"
|
||||
},
|
||||
"cpp-jwt": {
|
||||
"find_args": "CONFIG",
|
||||
"hash": "d11cbd5ddb3197b4c5ca15679bcd76a49963e7b530b7dd132db91e042925efa20dfb2c24ccfbe7ef82a7012af80deff0f72ee25851312ae80381a462df8534b8",
|
||||
"min_version": "1.4",
|
||||
"options": [
|
||||
"CPP_JWT_USE_VENDORED_NLOHMANN_JSON OFF"
|
||||
],
|
||||
"patches": [
|
||||
"0001-fix-missing-decl.patch"
|
||||
],
|
||||
"repo": "arun11299/cpp-jwt",
|
||||
"sha": "7f24eb4c32",
|
||||
"version": "1.5.1"
|
||||
},
|
||||
"cubeb": {
|
||||
"find_args": "CONFIG",
|
||||
"hash": "8a4bcb2f83ba590f52c66626e895304a73eb61928dbc57777e1822e55378e3568366f17f9da4b80036cc2ef4ea9723c32abf6e7d9bbe00fb03654f0991596ab0",
|
||||
"options": [
|
||||
"USE_SANITIZERS OFF",
|
||||
"BUILD_TESTS OFF",
|
||||
"BUILD_TOOLS OFF",
|
||||
"BUNDLE_SPEEX ON"
|
||||
],
|
||||
"repo": "mozilla/cubeb",
|
||||
"sha": "fa02160712",
|
||||
"version": "0.0.0"
|
||||
},
|
||||
"discord-rpc": {
|
||||
"find_args": "MODULE",
|
||||
"hash": "8213c43dcb0f7d479f5861091d111ed12fbdec1e62e6d729d65a4bc181d82f48a35d5fd3cd5c291f2393ac7c9681eabc6b76609755f55376284c8a8d67e148f3",
|
||||
"package": "DiscordRPC",
|
||||
"repo": "eden-emulator/discord-rpc",
|
||||
"sha": "0d8b2d6a37",
|
||||
"version": "3.4.1"
|
||||
},
|
||||
"enet": {
|
||||
"find_args": "MODULE",
|
||||
"hash": "a0d2fa8c957704dd49e00a726284ac5ca034b50b00d2b20a94fa1bbfbb80841467834bfdc84aa0ed0d6aab894608fd6c86c3b94eee46343f0e6d9c22e391dbf9",
|
||||
"min_version": "1.3",
|
||||
"repo": "lsalzman/enet",
|
||||
"tag": "v%VERSION%",
|
||||
"version": "1.3.18"
|
||||
},
|
||||
"ffmpeg": {
|
||||
"bundled": true,
|
||||
"hash": "ed177621176b3961bdcaa339187d3a7688c1c8b060b79c4bb0257cbc67ad7021ae5d5adca5303b45625abbbe3d9aafdd87ce777b8690ac295290d744c875489a",
|
||||
"repo": "FFmpeg/FFmpeg",
|
||||
"sha": "c7b5f1537d",
|
||||
"version": "8.0.1"
|
||||
},
|
||||
"ffmpeg-ci": {
|
||||
"ci": true,
|
||||
"min_version": "4.1",
|
||||
"name": "ffmpeg",
|
||||
"package": "FFmpeg",
|
||||
"repo": "crueter-ci/FFmpeg",
|
||||
"version": "8.0.1-c7b5f1537d"
|
||||
},
|
||||
"fmt": {
|
||||
"hash": "f0da82c545b01692e9fd30fdfb613dbb8dd9716983dcd0ff19ac2a8d36f74beb5540ef38072fdecc1e34191b3682a8542ecbf3a61ef287dbba0a2679d4e023f2",
|
||||
"min_version": "8",
|
||||
"repo": "fmtlib/fmt",
|
||||
"tag": "%VERSION%",
|
||||
"version": "12.1.0"
|
||||
},
|
||||
"frozen": {
|
||||
"hash": "b8dfe741c82bc178dfc9749d4ab5a130cee718d9ee7b71d9b547cf5f7f23027ed0152ad250012a8546399fcc1e12187efc68d89d6731256c4d2df7d04eef8d5c",
|
||||
"package": "frozen",
|
||||
"repo": "serge-sans-paille/frozen",
|
||||
"sha": "61dce5ae18",
|
||||
"version": "1.2.0"
|
||||
},
|
||||
"gamemode": {
|
||||
"find_args": "MODULE",
|
||||
"hash": "e87ec14ed3e826d578ebf095c41580069dda603792ba91efa84f45f4571a28f4d91889675055fd6f042d7dc25b0b9443daf70963ae463e38b11bcba95f4c65a9",
|
||||
"min_version": "1.7",
|
||||
"repo": "FeralInteractive/gamemode",
|
||||
"sha": "ce6fe122f3",
|
||||
"version": "1.8.2"
|
||||
},
|
||||
"httplib": {
|
||||
"find_args": "MODULE GLOBAL",
|
||||
"hash": "159ed94965018f2a371d45a3bfc1961e5fb1549e501ded70a6b4532d7fe99d0579c18b5195aff6e35f96f399b426cea2650ec9fb75ef80d4c9edeccb51f2e6c9",
|
||||
"options": [
|
||||
"HTTPLIB_REQUIRE_OPENSSL ON",
|
||||
"HTTPLIB_DISABLE_MACOSX_AUTOMATIC_ROOT_CERTIFICATES ON"
|
||||
],
|
||||
"patches": [
|
||||
"0001-mingw.patch"
|
||||
],
|
||||
"repo": "yhirose/cpp-httplib",
|
||||
"tag": "v%VERSION%",
|
||||
"version": "0.46.0"
|
||||
},
|
||||
"lagoon": {
|
||||
"hash": "b9380f99c6effaeccc6d8f81d4942e852c11ad28613df637e155451556ae5826f93765bee57a5c87a9740d2bd1db463ad0f55a947772fe9d57eeabae3efa373e",
|
||||
"repo": "loongson-community/lagoon",
|
||||
"tag": "%VERSION%",
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"libadrenotools": {
|
||||
"hash": "f6526620cb752876edc5ed4c0925d57b873a8218ee09ad10859ee476e9333259784f61c1dcc55a2bcba597352d18aff22cd2e4c1925ec2ae94074e09d7da2265",
|
||||
"patches": [
|
||||
"0001-linkerns-cpm.patch"
|
||||
],
|
||||
"repo": "eden-emulator/libadrenotools",
|
||||
"sha": "8ba23b42d7",
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"libusb": {
|
||||
"find_args": "MODULE",
|
||||
"hash": "98c5f7940ff06b25c9aa65aa98e23de4c79a4c1067595f4c73cc145af23a1c286639e1ba11185cd91bab702081f307b973f08a4c9746576dc8d01b3620a3aeb5",
|
||||
"patches": [
|
||||
"0001-netbsd-gettime.patch"
|
||||
],
|
||||
"repo": "libusb/libusb",
|
||||
"tag": "v%VERSION%",
|
||||
"version": "1.0.29"
|
||||
},
|
||||
"llvm-mingw": {
|
||||
"artifact": "clang-rt-builtins.tar.zst",
|
||||
"git_host": "git.eden-emu.dev",
|
||||
"hash": "d902392caf94e84f223766e2cc51ca5fab6cae36ab8dc6ef9ef6a683ab1c483bfcfe291ef0bd38ab16a4ecc4078344fa8af72da2f225ab4c378dee23f6186181",
|
||||
"repo": "eden-emu/llvm-mingw",
|
||||
"tag": "%VERSION%",
|
||||
"version": "20250828"
|
||||
},
|
||||
"lz4": {
|
||||
"hash": "35c21a5d9cfb5bbf314a5321d02b36819491d2ee3cf8007030ca09d13ca4dae672247b7aeab553e973093604fc48221cb03dc92197c6efe8fc3746891363fdab",
|
||||
"name": "lz4",
|
||||
"repo": "lz4/lz4",
|
||||
"sha": "ebb370ca83",
|
||||
"source_subdir": "build/cmake",
|
||||
"version": "1.10.0"
|
||||
},
|
||||
"moltenvk": {
|
||||
"artifact": "MoltenVK-macOS.tar",
|
||||
"bundled": true,
|
||||
"hash": "5695b36ca5775819a71791557fcb40a4a5ee4495be6b8442e0b666d0c436bec02aae68cc6210183f7a5c986bdbec0e117aecfad5396e496e9c2fd5c89133a347",
|
||||
"repo": "V380-Ori/Ryujinx.MoltenVK",
|
||||
"tag": "v%VERSION%-ryujinx",
|
||||
"version": "1.4.1"
|
||||
},
|
||||
"nlohmann": {
|
||||
"hash": "6cc1e86261f8fac21cc17a33da3b6b3c3cd5c116755651642af3c9e99bb3538fd42c1bd50397a77c8fb6821bc62d90e6b91bcdde77a78f58f2416c62fc53b97d",
|
||||
"min_version": "3.8",
|
||||
"package": "nlohmann_json",
|
||||
"repo": "nlohmann/json",
|
||||
"tag": "v%VERSION%",
|
||||
"version": "3.12.0"
|
||||
},
|
||||
"oaknut": {
|
||||
"hash": "9697e80a7d5d9bcb3ce51051a9a24962fb90ca79d215f1f03ae6b58da8ba13a63b5dda1b4dde3d26ac6445029696b8ef2883f4e5a777b342bba01283ed293856",
|
||||
"min_version": "2.0.1",
|
||||
"repo": "eden-emulator/oaknut",
|
||||
"tag": "v%VERSION%",
|
||||
"version": "2.0.3"
|
||||
},
|
||||
"oboe": {
|
||||
"bundled": true,
|
||||
"hash": "ce4011afe7345370d4ead3b891cd69a5ef224b129535783586c0ca75051d303ed446e6c7f10bde8da31fff58d6e307f1732a3ffd03b249f9ef1fd48fd4132715",
|
||||
"repo": "google/oboe",
|
||||
"tag": "%VERSION%",
|
||||
"version": "1.10.0"
|
||||
},
|
||||
"openssl": {
|
||||
"hash": "29002ce50cb95a4f4f1d0e9d3f684401fbd4eac34203dc2eef3b6334af5d44aa46bf788b63a6f5c139c383eafb7269ae87a58a9a3ad5912903b9773e545ccc0a",
|
||||
"min_version": "3.0.0",
|
||||
"package": "OpenSSL",
|
||||
"patches": [
|
||||
"0001-add-bundled-cert.patch"
|
||||
],
|
||||
"repo": "openssl/openssl",
|
||||
"tag": "openssl-%VERSION%",
|
||||
"version": "3.6.2"
|
||||
},
|
||||
"openssl-ci": {
|
||||
"ci": true,
|
||||
"package": "OpenSSL",
|
||||
"min_version": "3.0.0",
|
||||
"name": "openssl",
|
||||
"package": "OpenSSL",
|
||||
"repo": "crueter-ci/OpenSSL",
|
||||
"version": "4.0.0-11b7b6ea3b",
|
||||
"min_version": "3"
|
||||
"version": "4.0.0-11b7b6ea3b"
|
||||
},
|
||||
"openssl-cmake": {
|
||||
"repo": "jimmy-park/openssl-cmake",
|
||||
"hash": "2cc185c924fd70e7d886257ca0caa42b3b8f7f712f2052b4f94dde74759e27022de76178460e18c9bdfc57c366583999e198fbb6052d4e7d91c099d15a0ca63e",
|
||||
"git_version": "3.6.2",
|
||||
"tag": "%VERSION%",
|
||||
"bundled": true,
|
||||
"hash": "2cc185c924fd70e7d886257ca0caa42b3b8f7f712f2052b4f94dde74759e27022de76178460e18c9bdfc57c366583999e198fbb6052d4e7d91c099d15a0ca63e",
|
||||
"options": [
|
||||
"OPENSSL_CONFIGURE_OPTIONS threads"
|
||||
],
|
||||
@@ -21,127 +232,173 @@
|
||||
"0002-use-ccache.patch",
|
||||
"0003-use-cmake-compiler-flags.patch",
|
||||
"0004-use-shell-wrapper.patch"
|
||||
]
|
||||
},
|
||||
"openssl": {
|
||||
"repo": "openssl/openssl",
|
||||
"package": "OpenSSL",
|
||||
"min_version": "3",
|
||||
"version": "3",
|
||||
"hash": "29002ce50cb95a4f4f1d0e9d3f684401fbd4eac34203dc2eef3b6334af5d44aa46bf788b63a6f5c139c383eafb7269ae87a58a9a3ad5912903b9773e545ccc0a",
|
||||
"git_version": "3.6.2",
|
||||
"tag": "openssl-%VERSION%",
|
||||
"patches": [
|
||||
"0001-add-bundled-cert.patch"
|
||||
]
|
||||
},
|
||||
"boost": {
|
||||
"package": "Boost",
|
||||
"repo": "boostorg/boost",
|
||||
"tag": "boost-%VERSION%",
|
||||
"artifact": "%TAG%-cmake.tar.xz",
|
||||
"hash": "6ae6e94664fe7f2fb01976b59b276ac5df8085c7503fa829d810fbfe495960cfec44fa2c36e2cb23480bc19c956ed199d4952b02639a00a6c07625d4e7130c2d",
|
||||
"git_version": "1.90.0",
|
||||
"version": "1.57",
|
||||
"find_args": "CONFIG OPTIONAL_COMPONENTS headers context system fiber filesystem",
|
||||
"patches": [
|
||||
"0001-clang-cl.patch"
|
||||
]
|
||||
},
|
||||
"fmt": {
|
||||
"repo": "fmtlib/fmt",
|
||||
],
|
||||
"repo": "jimmy-park/openssl-cmake",
|
||||
"tag": "%VERSION%",
|
||||
"hash": "f0da82c545b01692e9fd30fdfb613dbb8dd9716983dcd0ff19ac2a8d36f74beb5540ef38072fdecc1e34191b3682a8542ecbf3a61ef287dbba0a2679d4e023f2",
|
||||
"version": "8",
|
||||
"git_version": "12.1.0"
|
||||
},
|
||||
"lz4": {
|
||||
"name": "lz4",
|
||||
"repo": "lz4/lz4",
|
||||
"sha": "ebb370ca83",
|
||||
"hash": "35c21a5d9cfb5bbf314a5321d02b36819491d2ee3cf8007030ca09d13ca4dae672247b7aeab553e973093604fc48221cb03dc92197c6efe8fc3746891363fdab",
|
||||
"source_subdir": "build/cmake"
|
||||
},
|
||||
"nlohmann": {
|
||||
"package": "nlohmann_json",
|
||||
"repo": "nlohmann/json",
|
||||
"tag": "v%VERSION%",
|
||||
"hash": "6cc1e86261f8fac21cc17a33da3b6b3c3cd5c116755651642af3c9e99bb3538fd42c1bd50397a77c8fb6821bc62d90e6b91bcdde77a78f58f2416c62fc53b97d",
|
||||
"version": "3.8",
|
||||
"git_version": "3.12.0"
|
||||
},
|
||||
"zlib": {
|
||||
"package": "ZLIB",
|
||||
"repo": "madler/zlib",
|
||||
"tag": "v%VERSION%",
|
||||
"hash": "16fea4df307a68cf0035858abe2fd550250618a97590e202037acd18a666f57afc10f8836cbbd472d54a0e76539d0e558cb26f059d53de52ff90634bbf4f47d4",
|
||||
"version": "1.2",
|
||||
"git_version": "1.3.2",
|
||||
"options": [
|
||||
"ZLIB_BUILD_SHARED OFF",
|
||||
"ZLIB_INSTALL OFF"
|
||||
]
|
||||
},
|
||||
"zstd": {
|
||||
"repo": "facebook/zstd",
|
||||
"sha": "b8d6101fba",
|
||||
"hash": "cc5ad4b119a9c2ea57f0b71eeff01113bb506e0d17000159c5409cb8236d22e38c52d5e9e97e7947a4bf1b2dfc44b6c503ab2d9aedbd59458435c6a2849cb029",
|
||||
"version": "1.5",
|
||||
"source_subdir": "build/cmake",
|
||||
"find_args": "MODULE",
|
||||
"options": [
|
||||
"ZSTD_BUILD_SHARED OFF"
|
||||
]
|
||||
"version": "3.6.2"
|
||||
},
|
||||
"opus": {
|
||||
"package": "Opus",
|
||||
"repo": "xiph/opus",
|
||||
"sha": "a3f0ec02b3",
|
||||
"hash": "9506147b0de35befda8633ff272981cc2575c860874791bd455b752f797fd7dbd1079f0ba42ccdd7bb1fe6773fa5e84b3d75667c2883dd1fb2d0e4a5fa4f8387",
|
||||
"version": "1.3",
|
||||
"find_args": "MODULE",
|
||||
"hash": "9506147b0de35befda8633ff272981cc2575c860874791bd455b752f797fd7dbd1079f0ba42ccdd7bb1fe6773fa5e84b3d75667c2883dd1fb2d0e4a5fa4f8387",
|
||||
"min_version": "1.3",
|
||||
"options": [
|
||||
"OPUS_PRESUME_NEON ON"
|
||||
],
|
||||
"package": "Opus",
|
||||
"patches": [
|
||||
"0001-disable-clang-runtime-neon.patch",
|
||||
"0002-no-install.patch"
|
||||
]
|
||||
},
|
||||
"boost_headers": {
|
||||
"repo": "boostorg/headers",
|
||||
"sha": "95930ca8f5",
|
||||
"hash": "8a07d7a6f0065587d3005a83481a794704ae22e773b9f336fbd89ed230aaa7b4c86c03edcbae30bba8b3e20839c3131eaa2dceac037ef811533ef4eadc53b15b",
|
||||
"bundled": true
|
||||
},
|
||||
"llvm-mingw": {
|
||||
"repo": "eden-emu/llvm-mingw",
|
||||
"git_host": "git.eden-emu.dev",
|
||||
"tag": "%VERSION%",
|
||||
"version": "20250828",
|
||||
"artifact": "clang-rt-builtins.tar.zst",
|
||||
"hash": "d902392caf94e84f223766e2cc51ca5fab6cae36ab8dc6ef9ef6a683ab1c483bfcfe291ef0bd38ab16a4ecc4078344fa8af72da2f225ab4c378dee23f6186181"
|
||||
},
|
||||
"vulkan-validation-layers": {
|
||||
"package": "VVL",
|
||||
"repo": "KhronosGroup/Vulkan-ValidationLayers",
|
||||
"tag": "vulkan-sdk-%VERSION%",
|
||||
"git_version": "1.4.341.0",
|
||||
"artifact": "android-binaries-%VERSION%.zip",
|
||||
"hash": "8812ae84cbe49e6a3418ade9c458d3be6d74a3dffd319d4502007b564d580998056e8190414368ec11b27bc83993c7a0dad713c31bcc3d9553b51243efee3753"
|
||||
],
|
||||
"repo": "xiph/opus",
|
||||
"sha": "a3f0ec02b3",
|
||||
"version": "1.5.2"
|
||||
},
|
||||
"quazip": {
|
||||
"package": "QuaZip-Qt6",
|
||||
"repo": "stachenov/quazip",
|
||||
"sha": "2e95c9001b",
|
||||
"hash": "609c240c7f029ac26a37d8fbab51bc16284e05e128b78b9b9c0e95d083538c36047a67d682759ac990e4adb0eeb90f04f1ea7fe2253bbda7e7e3bcce32e53dd8",
|
||||
"version": "1.3",
|
||||
"git_version": "1.5",
|
||||
"min_version": "1.3",
|
||||
"options": [
|
||||
"QUAZIP_QT_MAJOR_VERSION 6",
|
||||
"QUAZIP_INSTALL OFF",
|
||||
"QUAZIP_ENABLE_QTEXTCODEC OFF",
|
||||
"QUAZIP_BZIP2 OFF"
|
||||
]
|
||||
],
|
||||
"package": "QuaZip-Qt6",
|
||||
"repo": "stachenov/quazip",
|
||||
"sha": "2e95c9001b",
|
||||
"version": "1.5"
|
||||
},
|
||||
"sdl3": {
|
||||
"hash": "df5a323af7ac366661a3c0e887969c72584d232f3cc211419d59b0487b620b6b2859d4549c9e8df002ee489290062e466fcfddf7edc0872a37b1f2845e81c0f3",
|
||||
"min_version": "3.2.10",
|
||||
"package": "SDL3",
|
||||
"repo": "libsdl-org/SDL",
|
||||
"tag": "release-%VERSION%",
|
||||
"version": "3.4.8"
|
||||
},
|
||||
"sdl3-ci": {
|
||||
"ci": true,
|
||||
"min_version": "3.2.10",
|
||||
"name": "SDL3",
|
||||
"package": "SDL3",
|
||||
"repo": "crueter-ci/SDL3",
|
||||
"version": "3.4.8-d57c3b685c"
|
||||
},
|
||||
"simpleini": {
|
||||
"find_args": "MODULE",
|
||||
"hash": "b937c18a7b6277d77ca7ebfb216af4984810f77af4c32d101b7685369a4bd5eb61406223f82698e167e6311a728d07415ab59639fdf19eff71ad6dc2abfda989",
|
||||
"package": "SimpleIni",
|
||||
"repo": "brofield/simpleini",
|
||||
"tag": "v%VERSION%",
|
||||
"version": "4.25"
|
||||
},
|
||||
"sirit": {
|
||||
"artifact": "sirit-source-%VERSION%.tar.zst",
|
||||
"find_args": "CONFIG",
|
||||
"options": [
|
||||
"SIRIT_USE_SYSTEM_SPIRV_HEADERS ON"
|
||||
],
|
||||
"repo": "eden-emulator/sirit",
|
||||
"tag": "v%VERSION%",
|
||||
"version": "1.0.5",
|
||||
"hash": "10b3ff60bdcad428bb4f54360ff749212333a1d24c0b3ed99e466b1bfcf99d2db6cf596c0f965854a2095dfef9b7ce4e045edb070fa9f76eb3b295ab03a4a293"
|
||||
},
|
||||
"sirit-ci": {
|
||||
"ci": true,
|
||||
"name": "sirit",
|
||||
"package": "sirit",
|
||||
"repo": "eden-emulator/sirit",
|
||||
"version": "1.0.5"
|
||||
},
|
||||
"spirv-headers": {
|
||||
"hash": "cae8cd179c9013068876908fecc1d158168310ad6ac250398a41f0f5206ceff6469e2aaeab9c820bce9d1b08950c725c89c46e94b89a692be9805432cf749396",
|
||||
"options": [
|
||||
"SPIRV_WERROR OFF"
|
||||
],
|
||||
"package": "SPIRV-Headers",
|
||||
"repo": "KhronosGroup/SPIRV-Headers",
|
||||
"sha": "04f10f650d"
|
||||
},
|
||||
"tzdb": {
|
||||
"artifact": "%VERSION%.tar.gz",
|
||||
"git_host": "git.eden-emu.dev",
|
||||
"hash": "cce65a12bf90f4ead43b24a0b95dfad77ac3d9bfbaaf66c55e6701346e7a1e44ca5d2f23f47ee35ee02271eb1082bf1762af207aad9fb236f1c8476812d008ed",
|
||||
"package": "nx_tzdb",
|
||||
"repo": "eden-emu/tzdb_to_nx",
|
||||
"tag": "%VERSION%",
|
||||
"version": "230326"
|
||||
},
|
||||
"unordered-dense": {
|
||||
"bundled": true,
|
||||
"find_args": "CONFIG",
|
||||
"hash": "d2106f6640f6bfb81755e4b8bfb64982e46ec4a507cacdb38f940123212ccf35a20b43c70c6f01d7bfb8c246d1a16f7845d8052971949cea9def1475e3fa02c8",
|
||||
"package": "unordered_dense",
|
||||
"patches": [
|
||||
"0001-avoid-memset-when-clearing-an-empty-table.patch"
|
||||
],
|
||||
"repo": "martinus/unordered_dense",
|
||||
"sha": "7b55cab841",
|
||||
"version": "4.8.1"
|
||||
},
|
||||
"vulkan-headers": {
|
||||
"hash": "d2846ea228415772645eea4b52a9efd33e6a563043dd3de059e798be6391a8f0ca089f455ae420ff22574939ed0f48ed7c6ff3d5a9987d5231dbf3b3f89b484b",
|
||||
"min_version": "1.4.317",
|
||||
"package": "VulkanHeaders",
|
||||
"repo": "KhronosGroup/Vulkan-Headers",
|
||||
"tag": "v%VERSION%",
|
||||
"version": "1.4.345"
|
||||
},
|
||||
"vulkan-memory-allocator": {
|
||||
"find_args": "CONFIG",
|
||||
"hash": "deb5902ef8db0e329fbd5f3f4385eb0e26bdd9f14f3a2334823fb3fe18f36bc5d235d620d6e5f6fe3551ec3ea7038638899db8778c09f6d5c278f5ff95c3344b",
|
||||
"package": "VulkanMemoryAllocator",
|
||||
"repo": "GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator",
|
||||
"tag": "v%VERSION%",
|
||||
"version": "3.3.0"
|
||||
},
|
||||
"vulkan-utility-libraries": {
|
||||
"hash": "114f6b237a6dcba923ccc576befb5dea3f1c9b3a30de7dc741f234a831d1c2d52d8a224afb37dd57dffca67ac0df461eaaab6a5ab5e503b393f91c166680c3e1",
|
||||
"package": "VulkanUtilityLibraries",
|
||||
"repo": "KhronosGroup/Vulkan-Utility-Libraries",
|
||||
"tag": "v%VERSION%",
|
||||
"version": "1.4.345"
|
||||
},
|
||||
"vulkan-validation-layers": {
|
||||
"artifact": "android-binaries-%VERSION%.zip",
|
||||
"hash": "8812ae84cbe49e6a3418ade9c458d3be6d74a3dffd319d4502007b564d580998056e8190414368ec11b27bc83993c7a0dad713c31bcc3d9553b51243efee3753",
|
||||
"package": "VVL",
|
||||
"repo": "KhronosGroup/Vulkan-ValidationLayers",
|
||||
"tag": "vulkan-sdk-%VERSION%",
|
||||
"version": "1.4.341.0"
|
||||
},
|
||||
"xbyak": {
|
||||
"hash": "b6475276b2faaeb315734ea8f4f8bd87ededcee768961b39679bee547e7f3e98884d8b7851e176d861dab30a80a76e6ea302f8c111483607dde969b4797ea95a",
|
||||
"package": "xbyak",
|
||||
"repo": "herumi/xbyak",
|
||||
"tag": "v%VERSION%",
|
||||
"version": "7.35.2"
|
||||
},
|
||||
"zlib": {
|
||||
"hash": "16fea4df307a68cf0035858abe2fd550250618a97590e202037acd18a666f57afc10f8836cbbd472d54a0e76539d0e558cb26f059d53de52ff90634bbf4f47d4",
|
||||
"min_version": "1.2",
|
||||
"options": [
|
||||
"ZLIB_BUILD_SHARED OFF",
|
||||
"ZLIB_INSTALL OFF"
|
||||
],
|
||||
"package": "ZLIB",
|
||||
"repo": "madler/zlib",
|
||||
"tag": "v%VERSION%",
|
||||
"version": "1.3.2"
|
||||
},
|
||||
"zstd": {
|
||||
"find_args": "MODULE",
|
||||
"hash": "cc5ad4b119a9c2ea57f0b71eeff01113bb506e0d17000159c5409cb8236d22e38c52d5e9e97e7947a4bf1b2dfc44b6c503ab2d9aedbd59458435c6a2849cb029",
|
||||
"min_version": "1.5",
|
||||
"options": [
|
||||
"ZSTD_BUILD_SHARED OFF"
|
||||
],
|
||||
"repo": "facebook/zstd",
|
||||
"sha": "b8d6101fba",
|
||||
"source_subdir": "build/cmake",
|
||||
"version": "1.5.7"
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+302
-297
File diff suppressed because it is too large
Load Diff
Vendored
+302
-297
File diff suppressed because it is too large
Load Diff
Vendored
+302
-297
File diff suppressed because it is too large
Load Diff
Vendored
+302
-297
File diff suppressed because it is too large
Load Diff
Vendored
+302
-297
File diff suppressed because it is too large
Load Diff
Vendored
+302
-297
File diff suppressed because it is too large
Load Diff
Vendored
+302
-297
File diff suppressed because it is too large
Load Diff
Vendored
+302
-297
File diff suppressed because it is too large
Load Diff
Vendored
+302
-297
File diff suppressed because it is too large
Load Diff
Vendored
+302
-297
File diff suppressed because it is too large
Load Diff
Vendored
+302
-297
File diff suppressed because it is too large
Load Diff
Vendored
+302
-297
File diff suppressed because it is too large
Load Diff
Vendored
+302
-297
File diff suppressed because it is too large
Load Diff
Vendored
+620
-603
File diff suppressed because it is too large
Load Diff
Vendored
+302
-297
File diff suppressed because it is too large
Load Diff
Vendored
+302
-297
File diff suppressed because it is too large
Load Diff
Vendored
+302
-297
File diff suppressed because it is too large
Load Diff
Vendored
+302
-297
File diff suppressed because it is too large
Load Diff
Vendored
+302
-297
File diff suppressed because it is too large
Load Diff
Vendored
+320
-314
File diff suppressed because it is too large
Load Diff
Vendored
+302
-297
File diff suppressed because it is too large
Load Diff
Vendored
+302
-297
File diff suppressed because it is too large
Load Diff
Vendored
+302
-297
File diff suppressed because it is too large
Load Diff
Vendored
+302
-297
File diff suppressed because it is too large
Load Diff
Vendored
+302
-297
File diff suppressed because it is too large
Load Diff
Vendored
+302
-297
File diff suppressed because it is too large
Load Diff
Vendored
+302
-297
File diff suppressed because it is too large
Load Diff
+320
@@ -0,0 +1,320 @@
|
||||
# CPMUtil
|
||||
|
||||
CPMUtil is a wrapper around CPM that aims to reduce boilerplate and add useful utility functions to make dependency management a piece of cake.
|
||||
|
||||
- [CPMUtil](#cpmutil)
|
||||
- [Global Options](#global-options)
|
||||
- [About](#about)
|
||||
- [Common Properties](#common-properties)
|
||||
- [Standard Packages](#standard-packages)
|
||||
- [Versioning](#versioning)
|
||||
- [Patches](#patches)
|
||||
- [Pre-built CI Packages](#pre-built-ci-packages)
|
||||
- [Usage](#usage)
|
||||
- [Addendum: Cache Storage](#addendum-cache-storage)
|
||||
- [Addendum: Making Patches](#addendum-making-patches)
|
||||
- [Addendum: Package Identification Lists](#addendum-package-identification-lists)
|
||||
- [Addendum: Notes for Packagers](#addendum-notes-for-packagers)
|
||||
- [Network Sandbox](#network-sandbox)
|
||||
- [Unsandboxed](#unsandboxed)
|
||||
- [Addendum: Dependent Packages](#addendum-dependent-packages)
|
||||
- [Example: Vulkan](#example-vulkan)
|
||||
- [Addendum: Module Path Packages](#addendum-module-path-packages)
|
||||
- [Example: OpenSSL](#example-openssl)
|
||||
- [Addendum: Adding Qt](#addendum-adding-qt)
|
||||
|
||||
## Global Options
|
||||
|
||||
- `CPMUTIL_FORCE_SYSTEM` (default `OFF`): Require all CPM dependencies to use system packages. NOT RECOMMENDED!
|
||||
- You may optionally override this (section)
|
||||
- `CPMUTIL_FORCE_BUNDLED` (default `ON` on MSVC and Android, `OFF` elsewhere): Require all CPM dependencies to use bundled packages.
|
||||
- `CPMUTIL_PATCH_DIR` (default `${PROJECT_SOURCE_DIR}/.patch`): Path to patches used in packages. Stored as `<PATCH DIR>/json-package-name/0001-patch-name.patch`, etc.
|
||||
- `CPM_SOURCE_CACHE` (default `${PROJECT_SOURCE_DIR}/.cache/cpm`): Where downloaded dependencies get stored.
|
||||
|
||||
## About
|
||||
|
||||
CPMUtil works by defining dependencies in a JSON file, `cpmfile.json`, and calling `AddJsonPackage`. These dependencies generally must define, at minimum:
|
||||
|
||||
- The repository and Git host
|
||||
- A release artifact, commit, or tag archive to download
|
||||
- A SHA512 sum for the downloaded artifact
|
||||
|
||||
And may optionally define other properties like:
|
||||
|
||||
- The minimum version for system packages
|
||||
- The package name used for system packages (this defaults to the json key if undefined)
|
||||
- In-tree source patches
|
||||
- Options passed to CMake
|
||||
- Options passed to find_package
|
||||
|
||||
For instance:
|
||||
|
||||
```json
|
||||
"fmt": {
|
||||
"repo": "fmtlib/fmt",
|
||||
"tag": "12.1.0",
|
||||
"hash": "f0da82c545b01692e9fd30fdfb613dbb8dd9716983dcd0ff19ac2a8d36f74beb5540ef38072fdecc1e34191b3682a8542ecbf3a61ef287dbba0a2679d4e023f2",
|
||||
"min_version": "8",
|
||||
"options": [
|
||||
"FMT_TEST ON"
|
||||
],
|
||||
"patches": [
|
||||
"0001-disable-reference-copy.patch"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Calling `AddJsonPackage(fmt)`:
|
||||
|
||||
- Searches for a system package named `fmt` of version 8 or higher
|
||||
- If found, uses the system package and caches it for future use
|
||||
- If not found:
|
||||
- Downloads fmt 12.1.0 from the GitHub Archive into `.cache/cpm/fmt/12.1.0`
|
||||
- Verifies the hash
|
||||
- Applies the `0001-disable-reference-copy.patch` patch to the source tree
|
||||
- Sets `FMT_TEST` to `ON`
|
||||
- Adds the downloaded directory to CMake
|
||||
- Now, future `find_package(fmt)` calls will use the downloaded package
|
||||
|
||||
There are two types of packages CPMUtil can define: standard and prebuilt CI packages. Some properties are common to both types, however.
|
||||
|
||||
## Common Properties
|
||||
|
||||
These JSON properties are used by standard and CI packages alike.
|
||||
|
||||
- `package`: The package name used by `find_package` to check for the existence of a system package.
|
||||
- If unset, defaults to the JSON key
|
||||
- `repo`: The Git repository the package is stored in, if applicable.
|
||||
- `version`: The version of the package to download. This is required.
|
||||
- `min_version`: The minimum required version of the package, if a system package is desired.
|
||||
- `git_host`: The Git host the package is stored in, if applicable. Defaults to `github.com`.
|
||||
|
||||
## Standard Packages
|
||||
|
||||
Normal packages, like the prior `fmt` example, *must* also define:
|
||||
|
||||
- `hash`: The SHA512 hash of the downloaded artifact. CPMUtil generally computes this for you.
|
||||
- A valid version/URL identifier:
|
||||
- `url`: Download from a raw URL.
|
||||
- `sha`: A short or fully-qualified Git commit sha. CPMUtil recommends using 10-character wide shas.
|
||||
- `tag`: A Git tag. See [Versioning](#versioning) for its relation to `version`.
|
||||
- `artifact`: A GitHub/Forgejo/Gitea release artifact (requires `tag`). See [Versioning](#versioning) for its relation to `tag` and `version`.
|
||||
|
||||
The following are optional to define:
|
||||
|
||||
- `source_subdir`: A subdirectory containing the `CMakeLists.txt` to configure a project. Useful for projects like `zstd`.
|
||||
- `bundled`: Force the usage of a bundled package. Useful for packages where the system package is broken or nonexistent; e.g. including external fragment shaders.
|
||||
- `find_args`: Additional arguments passed to `find_package`, e.g. `MODULE`
|
||||
- `patches`: Array of in-tree patches to apply to the downloaded source code. See [#Patches](TODO).
|
||||
- `options`: Array of CMake options to apply before configuring the package, e.g. `"FMT_TEST ON"`.
|
||||
|
||||
### Versioning
|
||||
|
||||
When using tags or artifacts, it may be cumbersome to repeat the version multiple times; especially if it's constantly changing. For this purpose, `tag` and `artifact` both support basic version text replacement.
|
||||
|
||||
`tag` can use `%VERSION%` to have its version replaced with the `version` defined for the package, e.g. for OpenSSL; when downloading, `tag` will evaluate to `openssl-3.6.2`:
|
||||
|
||||
```json
|
||||
"openssl": {
|
||||
"repo": "openssl/openssl",
|
||||
"version": "3.6.2",
|
||||
"tag": "openssl-%VERSION%"
|
||||
}
|
||||
```
|
||||
|
||||
`artifact` also supports `%VERSION%` replacement, and can also use `%TAG%` to be replaced by the computed tag. Take this Boost definition:
|
||||
|
||||
```json
|
||||
"boost": {
|
||||
"repo": "boostorg/boost",
|
||||
"tag": "boost-%VERSION%",
|
||||
"version": "1.90.0"
|
||||
}
|
||||
```
|
||||
|
||||
Boost's artifact for this version is stored in `boost-1.90.0-cmake.tar.xz`. Notice that the computed tag,`boost-1.90.0`, is in the name of the artifact! Thus, `artifact` can be either:
|
||||
|
||||
- `boost-%VERSION%-cmake.tar.xz`
|
||||
- Or, even simpler: `%TAG%-cmake.tar.xz`
|
||||
|
||||
Future updates need only change the `version` identifier, and the artifact and tag will automatically be updated!
|
||||
|
||||
### Patches
|
||||
|
||||
CPMUtil is able to apply in-place source tree patches to downloaded packages. These are defined in JSON as an array of names, preferably using `git-format-patch`'s scheme of `<4 digit number>-patch-name.patch`. These are stored in `<CPMUTIL_PATCH_DIR>/<json-key>` (remember that `CPMUTIL_PATCH_DIR` defaults to `$ROOT/.patch`); e.g. `boost` patches would be in `.patch/boost`. Let's say we've made three patches and want to add them; in the Boost JSON definition, we would add:
|
||||
|
||||
```json
|
||||
"patches": [
|
||||
"0001-fix-clang-cl-compilation.patch",
|
||||
"0002-fix-msvc-arm64-compilation.patch",
|
||||
"0003-fix-bsd-linking.patch"
|
||||
]
|
||||
```
|
||||
|
||||
Then, when Boost is downloaded, it will apply these patches in order to the source tree.
|
||||
|
||||
To learn how to make patches, see [Addendum: Making Patches](#addendum-making-patches).
|
||||
|
||||
## Pre-built CI Packages
|
||||
|
||||
The definition and usage of CI packages is subject to change in the very near future.
|
||||
|
||||
CI packages are, in essence, prebuilt binary distributions for libraries. They exist for a few reasons:
|
||||
|
||||
- Creating static libraries for system packages that normally lack them, e.g. Qt/SDL
|
||||
- Reducing duplicated compilation effort on rarely-changing externals, e.g. SDL
|
||||
- Creating debloated prebuilt packages specifically for your project to reduce binary size, e.g. FFmpeg
|
||||
|
||||
CPMUtil is specifically designed to work with a small subset of prebuilt CI packages; namely, those that follow the format of the [crueter-ci spec](https://github.com/crueter-ci/spec/blob/master/README.md).
|
||||
|
||||
To use them, you must add `ci: true` to your package definition. Alongside the common properties, CI packages define the following:
|
||||
|
||||
- `name`: The artifacts' name prefix (required), e.g. `openssl`
|
||||
- `extension`: The artifacts' extension (default `tar.zst`)
|
||||
- `disabled_platforms`: CPMUtil-supported platforms that are not built into this repository.
|
||||
- This is subject to change.
|
||||
|
||||
**Note that `package` is subject to removal here.**
|
||||
|
||||
Example:
|
||||
|
||||
```json
|
||||
"sdl2": {
|
||||
"repo": "crueter-ci/SDL2",
|
||||
"package": "SDL2",
|
||||
"min_version": "2.26.4",
|
||||
"ci": true,
|
||||
"version": "2.32.10-a65111bd2d",
|
||||
"artifact": "SDL2"
|
||||
},
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
Once you've defined your package in `cpmfile.json`, simply call `AddJsonPackage(<JSON key>)` and go from there! Specific instructions differ between individual packages, so you're on your own from here.
|
||||
|
||||
If you're only concerned with basic usage, you can stop reading. For more advanced use cases and package management, read these addenda.
|
||||
|
||||
## Addendum: Cache Storage
|
||||
|
||||
CPMUtil stores downloaded packages within `.cache/cpm` by default (see `CPM_SOURCE_CACHE`). Subdirectories stored within are lowercase representations of the `find_package` name for the package; for instance, a `vulkan-headers` definition with `package: "VulkanHeaders"` would be stored in `.cache/cpm/vulkanheaders`.
|
||||
|
||||
Within these subdirectories, additional directories are created for each individual version:
|
||||
|
||||
- A four-character shorthand of `sha`, if defined
|
||||
- If `sha` is not defined, the fully qualified `version` is used
|
||||
|
||||
CI packages use `<platform>-<architecture>-<version>` unconditionally.
|
||||
|
||||
## Addendum: Making Patches
|
||||
|
||||
CPMUtil has a dedicated command for making patches. You're recommended to have Git and a command line editor installed, but CPMUtil is able to work without either. To do so, follow these steps, noting your package's JSON key:
|
||||
|
||||
- Clean-fetch your package: `tools/cpmutil.sh package reset <package>`
|
||||
- Make any necessary modifications to the package source.
|
||||
- You can access the package source directory via `tools/cpmutil.sh package dir <package>`.
|
||||
- Create the patch: `tools/cpmutil.sh package patch <package>`
|
||||
- Follow the on-screen prompts. If you have Git installed, an editor will be opened so you can type your commit message. If not, just type a one-line description.
|
||||
|
||||
And you're done! CPMUtil will automatically create and name the patch, and add it to the list of patches in the JSON definition.
|
||||
|
||||
## Addendum: Package Identification Lists
|
||||
|
||||
CPMUtil will create three lists of dependencies where `AddPackage` or similar was used. Each is in order of addition.
|
||||
|
||||
- `CPM_PACKAGE_NAMES`: The names of packages included by CPMUtil
|
||||
- `CPM_PACKAGE_URLS`: The URLs to project/repo pages of packages
|
||||
- `CPM_PACKAGE_SHAS`: Short version identifiers for each package
|
||||
- If the package was included as a system package, `(system)` is appended thereafter
|
||||
- Packages whose versions can't be deduced will be left as `unknown`.
|
||||
|
||||
For an example of how this might be implemented in an application, see Eden's implementation:
|
||||
|
||||
- [`dep_hashes.h.in`](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/src/dep_hashes.h.in)
|
||||
- [`GenerateDepHashes.cmake`](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CMakeModules/GenerateDepHashes.cmake)
|
||||
- [`deps_dialog.cpp`](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/src/yuzu/deps_dialog.cpp)
|
||||
|
||||
## Addendum: Notes for Packagers
|
||||
|
||||
If you are packaging a project that uses CPMUtil, read this!
|
||||
|
||||
### Network Sandbox
|
||||
|
||||
For sandboxed environments (e.g. Gentoo, nixOS) you must install all dependencies to the system beforehand and set `-DCPMUTIL_FORCE_SYSTEM=ON`. If a dependency is missing, get creating!
|
||||
|
||||
Alternatively, if CPMUtil pulls in a package that has no suitable way to install or use a system version, download it separately and pass `-DPackageName_DIR=/path/to/downloaded/dir` (e.g. shaders)
|
||||
|
||||
### Unsandboxed
|
||||
|
||||
For others (AUR, MPR, etc). CPMUtil will handle everything for you, including if some of the project's dependencies are missing from your distribution's repositories. See [`eden-git`](https://aur.archlinux.org/cgit/aur.git/tree/PKGBUILD?h=eden-git) for an example.
|
||||
|
||||
## Addendum: Dependent Packages
|
||||
|
||||
Consider the following scenario: the Vulkan Headers and Vulkan Utility libraries are both pulled in by my project. In order for both to compile cleanly, their versions *must* match. However, a user may have the Vulkan Headers installed, but *not* the Vulkan Utility Libraries! This can cause a version mismatch where the Utility Libraries expect a much newer version of the Vulkan Headers than the user has installed.
|
||||
|
||||
To solve this, CPMUtil has an `AddDependentPackages` command. This takes a list of JSON package keys that *must* either ALL be installed to the system, or ALL be bundled.
|
||||
|
||||
### Example: Vulkan
|
||||
|
||||
Using the prior Vulkan example:
|
||||
|
||||
```json
|
||||
"vulkan-headers": {
|
||||
"repo": "KhronosGroup/Vulkan-Headers",
|
||||
"package": "VulkanHeaders",
|
||||
"min_version": "1.4.317",
|
||||
"version": "1.4.342",
|
||||
"tag": "v%VERSION%"
|
||||
},
|
||||
"vulkan-utility-libraries": {
|
||||
"repo": "KhronosGroup/Vulkan-Utility-Libraries",
|
||||
"package": "VulkanUtilityLibraries",
|
||||
"version": "1.4.342",
|
||||
"tag": "v%VERSION%"
|
||||
}
|
||||
```
|
||||
|
||||
In CMake:
|
||||
|
||||
```cmake
|
||||
AddDependentPackages(vulkan-headers vulkan-utility-libraries)
|
||||
```
|
||||
|
||||
Possible scenarios:
|
||||
|
||||
- The user has both Vulkan Headers and Vulkan Utility Libraries installed to the system, and both are new enough.
|
||||
- Configuration proceeds without issue.
|
||||
- The user has neither installed, or has a too-old version of Vulkan Headers installed
|
||||
- Configuration proceeds without issue.
|
||||
- The user has a valid Vulkan Headers installed, but not Vulkan Utility Libraries.
|
||||
- CPMUtil instructs the user to either force bundled Vulkan Headers, or install Vulkan Utility Libraries.
|
||||
- The user has both installed, but Vulkan Headers are too old.
|
||||
- CPMUtil instructs the user to install a valid version of Vulkan Headers, or force bundled Vulkan Utility Libraries.
|
||||
|
||||
## Addendum: Module Path Packages
|
||||
|
||||
Sometimes, a prebuilt CI package may be packed in such a way that it's meant to be used in the context of a system install (e.g. pkgconfig or CMakeConfig files). In this case, CPMUtil normally will be unable to configure the downloaded subdirectory. To solve this, you can use `AddJsonPackage`'s `MODULE_PATH` mode, which adds the downloaded source directory to the `CMAKE_MODULE_PATH`.
|
||||
|
||||
### Example: OpenSSL
|
||||
|
||||
Say an OpenSSL CI package is packed to contain its CMake config files rather than a root `CMakeLists.txt`; in this case, you would call:
|
||||
|
||||
```cmake
|
||||
AddJsonPackage(NAME openssl MODULE_PATH)
|
||||
```
|
||||
|
||||
The `NAME` argument is also required, as the parsing is different from the standard single-argument function signature.
|
||||
|
||||
From here, calling `find_package(OpenSSL)` will use the bundled OpenSSL.
|
||||
|
||||
## Addendum: Adding Qt
|
||||
|
||||
If you'd like to use customized Qt builds, CPMUtil provides a convenience function that allows you to add Qt builds. This usage and setup is subject to change.
|
||||
|
||||
See [crueter-ci/Qt](https://github.com/crueter-ci/Qt) for an example of how one might build customized Qt. To add a Qt build to your project, use `AddQt(<repository> <version>)`, e.g.:
|
||||
|
||||
```cmake
|
||||
AddQt(QDash-CI/Qt 6.11.1)
|
||||
```
|
||||
|
||||
Then, call `find_package(Qt6 ...)` and it will pull Qt from your downloaded source.
|
||||
@@ -1,21 +0,0 @@
|
||||
# AddPackage
|
||||
|
||||
- `VERSION` (required): The version to get (the tag will be `v${VERSION}`)
|
||||
- `NAME` (required): Name used within the artifacts
|
||||
- `REPO` (required): CI repository, e.g. `crueter-ci/OpenSSL`
|
||||
- `PACKAGE` (required): `find_package` package name
|
||||
- `EXTENSION`: Artifact extension (default `tar.zst`)
|
||||
- `MIN_VERSION`: Minimum version for `find_package`. Only used if platform does not support this package as a bundled artifact
|
||||
- `DISABLED_PLATFORMS`: List of platforms that lack artifacts for this package. Options:
|
||||
- `windows-amd64`
|
||||
- `windows-arm64`
|
||||
- `mingw-amd64`
|
||||
- `mingw-arm64`
|
||||
- `android-x86_64`
|
||||
- `android-aarch64`
|
||||
- `solaris-amd64`
|
||||
- `freebsd-amd64`
|
||||
- `linux-amd64`
|
||||
- `linux-aarch64`
|
||||
- `macos-universal`
|
||||
- `ios-aarch64`
|
||||
@@ -1,41 +0,0 @@
|
||||
# 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.
|
||||
@@ -1,105 +0,0 @@
|
||||
# AddJsonPackage
|
||||
|
||||
In each directory that utilizes `CPMUtil`, there must be a `cpmfile.json` that defines dependencies in a similar manner to the individual calls.
|
||||
|
||||
The cpmfile is an object of objects, with each sub-object being named according to the package's identifier, e.g. `openssl`, which can then be fetched with `AddJsonPackage(<identifier>)`. Options are designed to map closely to the argument names, and are always strings unless otherwise specified.
|
||||
<!-- TOC -->
|
||||
- [Options](#options)
|
||||
- [Examples](#examples)
|
||||
<!-- /TOC -->
|
||||
|
||||
## Options
|
||||
|
||||
- `package` -> `NAME` (`PACKAGE` for CI), defaults to the object key
|
||||
- `repo` -> `REPO`
|
||||
- `version` -> `VERSION`
|
||||
- `ci` (bool)
|
||||
|
||||
If `ci` is `false`:
|
||||
|
||||
- `hash` -> `HASH`
|
||||
- `hash_suffix` -> `HASH_SUFFIX`
|
||||
- `sha` -> `SHA`
|
||||
- `key` -> `KEY`
|
||||
- `tag` -> `TAG`
|
||||
- If the tag contains `%VERSION%`, that part will be replaced by the `git_version`, OR `version` if `git_version` is not specified
|
||||
- `url` -> `URL`
|
||||
- `artifact` -> `ARTIFACT`
|
||||
- If the artifact contains `%VERSION%`, that part will be replaced by the `git_version`, OR `version` if `git_version` is not specified
|
||||
- If the artifact contains `%TAG%`, that part will be replaced by the `tag` (with its replacement already done)
|
||||
- `git_version` -> `GIT_VERSION`
|
||||
- `git_host` -> `GIT_HOST`
|
||||
- `source_subdir` -> `SOURCE_SUBDIR`
|
||||
- `bundled` -> `BUNDLED_PACKAGE`
|
||||
- `find_args` -> `FIND_PACKAGE_ARGUMENTS`
|
||||
- `download_only` -> `DOWNLOAD_ONLY`
|
||||
- `patches` -> `PATCHES` (array)
|
||||
- `options` -> `OPTIONS` (array)
|
||||
- `skip_updates`: Tells `check-updates.sh` to not check for new updates on this package.
|
||||
|
||||
Other arguments aren't currently supported. If you wish to add them, see the `AddJsonPackage` function in `CMakeModules/CPMUtil.cmake`.
|
||||
|
||||
If `ci` is `true`:
|
||||
|
||||
- `name` -> `NAME`, defaults to the object key
|
||||
- `extension` -> `EXTENSION`, defaults to `tar.zst`
|
||||
- `min_version` -> `MIN_VERSION`
|
||||
- `extension` -> `EXTENSION`
|
||||
- `disabled_platforms` -> `DISABLED_PLATFORMS` (array)
|
||||
|
||||
## Examples
|
||||
|
||||
In order: OpenSSL CI, Boost (tag + artifact), Opus (options + find_args), discord-rpc (sha + options + patches).
|
||||
|
||||
```json
|
||||
{
|
||||
"openssl": {
|
||||
"ci": true,
|
||||
"package": "OpenSSL",
|
||||
"name": "openssl",
|
||||
"repo": "crueter-ci/OpenSSL",
|
||||
"version": "3.6.0",
|
||||
"min_version": "1.1.1",
|
||||
"disabled_platforms": [
|
||||
"macos-universal",
|
||||
"ios-aarch64"
|
||||
]
|
||||
},
|
||||
"boost": {
|
||||
"package": "Boost",
|
||||
"repo": "boostorg/boost",
|
||||
"tag": "boost-%VERSION%",
|
||||
"artifact": "%TAG%-cmake.7z",
|
||||
"hash": "e5b049e5b61964480ca816395f63f95621e66cb9bcf616a8b10e441e0e69f129e22443acb11e77bc1e8170f8e4171b9b7719891efc43699782bfcd4b3a365f01",
|
||||
"git_version": "1.88.0",
|
||||
"version": "1.57"
|
||||
},
|
||||
"opus": {
|
||||
"package": "Opus",
|
||||
"repo": "xiph/opus",
|
||||
"sha": "5ded705cf4",
|
||||
"hash": "0dc89e58ddda1f3bc6a7037963994770c5806c10e66f5cc55c59286fc76d0544fe4eca7626772b888fd719f434bc8a92f792bdb350c807968b2ac14cfc04b203",
|
||||
"version": "1.3",
|
||||
"find_args": "MODULE",
|
||||
"options": [
|
||||
"OPUS_BUILD_TESTING OFF",
|
||||
"OPUS_BUILD_PROGRAMS OFF",
|
||||
"OPUS_INSTALL_PKG_CONFIG_MODULE OFF",
|
||||
"OPUS_INSTALL_CMAKE_CONFIG_MODULE OFF"
|
||||
]
|
||||
},
|
||||
"discord-rpc": {
|
||||
"repo": "discord/discord-rpc",
|
||||
"sha": "963aa9f3e5",
|
||||
"hash": "386e1344e9a666d730f2d335ee3aef1fd05b1039febefd51aa751b705009cc764411397f3ca08dffd46205c72f75b235c870c737b2091a4ed0c3b061f5919bde",
|
||||
"options": [
|
||||
"BUILD_EXAMPLES OFF"
|
||||
],
|
||||
"patches": [
|
||||
"0001-cmake-version.patch",
|
||||
"0002-no-clang-format.patch",
|
||||
"0003-fix-cpp17.patch"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -1,118 +0,0 @@
|
||||
# `AddPackage`
|
||||
|
||||
<!-- TOC -->
|
||||
- [Identification/Fetching](#identificationfetching)
|
||||
- [Hashing](#hashing)
|
||||
- [Other Options](#other-options)
|
||||
- [Extra Variables](#extra-variables)
|
||||
- [System/Bundled Packages](#systembundled-packages)
|
||||
- [Identification](#identification)
|
||||
<!-- /TOC -->
|
||||
|
||||
## Identification/Fetching
|
||||
|
||||
- `NAME` (required): The package name (must be the same as the `find_package` name if applicable)
|
||||
- `VERSION`: The minimum version of this package that can be used on the system
|
||||
- `GIT_VERSION`: The "version" found within git
|
||||
- `URL`: The URL to fetch.
|
||||
- `REPO`: The repo to use (`owner/repo`).
|
||||
- `GIT_HOST`: The Git host to use
|
||||
- Defaults to `github.com`. Do not include the protocol, as HTTPS is enforced.
|
||||
- `TAG`: The tag to fetch, if applicable.
|
||||
- `ARTIFACT`: The name of the artifact, if applicable.
|
||||
- `SHA`: Commit sha to fetch, if applicable.
|
||||
- `BRANCH`: Branch to fetch, if applicable.
|
||||
|
||||
The following configurations are supported, in descending order of precedence:
|
||||
|
||||
- `URL`: Bare URL download, useful for custom artifacts
|
||||
- If this is set, `GIT_URL` or `REPO` should be set to allow the dependency viewer to link to the project's Git repository.
|
||||
- If this is NOT set, `REPO` must be defined.
|
||||
- `REPO + TAG + ARTIFACT`: GitHub release artifact
|
||||
- The final download URL will be `https://github.com/${REPO}/releases/download/${TAG}/${ARTIFACT}`
|
||||
- Useful for prebuilt libraries and prefetched archives
|
||||
- `REPO + TAG`: GitHub tag archive
|
||||
- The final download URL will be `https://github.com/${REPO}/archive/refs/tags/${TAG}.tar.gz`
|
||||
- Useful for pinning to a specific tag, better for build identification
|
||||
- `REPO + SHA`: GitHub commit archive
|
||||
- The final download URL will be `https://github.com/${REPO}/archive/${SHA}.zip`
|
||||
- Useful for pinning to a specific commit
|
||||
- `REPO + BRANCH`: GitHub branch archive
|
||||
- The final download URL will be `https://github.com/${REPO}/archive/refs/heads/${BRANCH}.zip`
|
||||
- Generally not recommended unless the branch is frozen
|
||||
- `REPO`: GitHub master archive
|
||||
- The final download URL will be `https://github.com/${REPO}/archive/refs/heads/master.zip`
|
||||
- Generally not recommended unless the project is dead
|
||||
|
||||
## Hashing
|
||||
|
||||
Hashing is used for verifying downloads. It's highly recommended to use these.
|
||||
|
||||
- `HASH_ALGO` (default `SHA512`): Hash algorithm to use
|
||||
|
||||
Hashing strategies, descending order of precedence:
|
||||
|
||||
- `HASH`: Bare hash verification, useful for static downloads e.g. commit archives
|
||||
- `HASH_SUFFIX`: Download the hash as `${DOWNLOAD_URL}.${HASH_SUFFIX}`
|
||||
- The downloaded hash *must* match the hash algorithm and contain nothing but the hash; no filenames or extra content.
|
||||
- `HASH_URL`: Download the hash from a separate URL
|
||||
|
||||
## Other Options
|
||||
|
||||
- `KEY`: Custom cache key to use (stored as `.cache/cpm/${packagename_lower}/${key}`)
|
||||
- Default is based on, in descending order of precedence:
|
||||
- First 4 characters of the sha
|
||||
- `GIT_VERSION`
|
||||
- Tag
|
||||
- `VERSION`
|
||||
- Otherwise, CPM defaults will be used. This is not recommended as it doesn't produce reproducible caches
|
||||
- `DOWNLOAD_ONLY`: Whether or not to configure the downloaded package via CMake
|
||||
- Useful to turn `OFF` if the project doesn't use CMake
|
||||
- `SOURCE_SUBDIR`: Subdirectory of the project containing a CMakeLists.txt file
|
||||
- `FIND_PACKAGE_ARGUMENTS`: Arguments to pass to the `find_package` call
|
||||
- `BUNDLED_PACKAGE`: Set to `ON` to default to the bundled package
|
||||
- `FORCE_BUNDLED_PACKAGE`: Set to `ON` to force the usage of the bundled package, regardless of CPMUTIL_FORCE_SYSTEM or `<package>_FORCE_SYSTEM`
|
||||
- `OPTIONS`: Options to pass to the configuration of the package
|
||||
- `PATCHES`: Patches to apply to the package, stored in `.patch/${packagename_lower}/0001-patch-name.patch` and so on
|
||||
- Other arguments can be passed to CPM as well
|
||||
|
||||
## Extra Variables
|
||||
|
||||
For each added package, users may additionally force usage of the system/bundled package.
|
||||
|
||||
- `${package}_DIR`: Path to a separately-downloaded copy of the package. Note that versioning is not checked!
|
||||
- `${package}_FORCE_SYSTEM`: Require the package to be installed on the system
|
||||
- `${package}_FORCE_BUNDLED`: Force the package to be fetched and use the bundled version
|
||||
|
||||
## System/Bundled Packages
|
||||
|
||||
Descending order of precedence:
|
||||
|
||||
- If `${package}_FORCE_SYSTEM` is true, requires the package to be on the system
|
||||
- If `${package}_FORCE_BUNDLED` is true, forcefully uses the bundled package
|
||||
- If `CPMUTIL_FORCE_SYSTEM` is true, requires the package to be on the system
|
||||
- If `CPMUTIL_FORCE_BUNDLED` is true, forcefully uses the bundled package
|
||||
- If the `BUNDLED_PACKAGE` argument is true, forcefully uses the bundled package
|
||||
- Otherwise, CPM will search for the package first, and if not found, will use the bundled package
|
||||
|
||||
## Identification
|
||||
|
||||
All dependencies must be identifiable in some way for usage in the dependency viewer. Lists are provided in descending order of precedence.
|
||||
|
||||
URLs:
|
||||
|
||||
- `GIT_URL`
|
||||
- `REPO` as a Git repository
|
||||
- You may optionally specify `GIT_HOST` to use a custom host, e.g. `GIT_HOST git.crueter.xyz`. Note that the git host MUST be GitHub-like in its artifact/archive downloads, e.g. Forgejo
|
||||
- If `GIT_HOST` is unspecified, defaults to `github.com`
|
||||
- `URL`
|
||||
|
||||
Versions (bundled):
|
||||
|
||||
- `SHA`
|
||||
- `GIT_VERSION`
|
||||
- `VERSION`
|
||||
- `TAG`
|
||||
- "unknown"
|
||||
|
||||
If the package is a system package, AddPackage will attempt to determine the package version and append `(system)` to the identifier. Otherwise, it will be marked as `unknown (system)`
|
||||
@@ -1,28 +0,0 @@
|
||||
# AddQt
|
||||
|
||||
Simply call `AddQt(<Qt Version>)` before any Qt `find_package` calls and everything will be set up for you. On Linux, the bundled Qt library is built as a shared library, and provided you have OpenSSL and X11, everything should just work.
|
||||
|
||||
On Windows, MinGW, and MacOS, Qt is bundled as a static library. No further action is needed, as the provided libraries automatically integrate the Windows/Cocoa plugins, alongside the corresponding Multimedia and Network plugins.
|
||||
|
||||
## Modules
|
||||
|
||||
The following modules are bundled into these Qt builds:
|
||||
|
||||
- Base (Gui, Core, Widgets, Network)
|
||||
- Multimedia
|
||||
- Declarative (Quick, QML)
|
||||
- Linux: Wayland client
|
||||
|
||||
Each platform has the corresponding QPA built in and set as the default as well. This means you don't need to add `Q_IMPORT_PLUGIN`!
|
||||
|
||||
## Example
|
||||
|
||||
See an example in the [`tests/qt`](https://git.crueter.xyz/CMake/CPMUtil/src/branch/master/tests/qt/CMakeLists.txt) directory.
|
||||
|
||||
## Versions
|
||||
|
||||
The following versions have available builds:
|
||||
|
||||
- 6.9.3
|
||||
|
||||
See [`crueter-ci/Qt`](https://github.com/crueter-ci/Qt) for an updated list at any time.
|
||||
@@ -1,70 +0,0 @@
|
||||
# CPMUtil
|
||||
|
||||
CPMUtil is a wrapper around CPM that aims to reduce boilerplate and add useful utility functions to make dependency management a piece of cake.
|
||||
|
||||
Global Options:
|
||||
|
||||
- `CPMUTIL_FORCE_SYSTEM` (default `OFF`): Require all CPM dependencies to use system packages. NOT RECOMMENDED!
|
||||
- You may optionally override this (section)
|
||||
- `CPMUTIL_FORCE_BUNDLED` (default `ON` on MSVC and Android, `OFF` elsewhere): Require all CPM dependencies to use bundled packages.
|
||||
|
||||
You are highly encouraged to read AddPackage first, even if you plan to only interact with CPMUtil via `AddJsonPackage`.
|
||||
|
||||
- [AddPackage](#addpackage)
|
||||
- [AddCIPackage](#addcipackage)
|
||||
- [AddJsonPackage](#addjsonpackage)
|
||||
- [AddQt](#addqt)
|
||||
- [Lists](#lists)
|
||||
- [For Packagers](#for-packagers)
|
||||
- [Network Sandbox](#network-sandbox)
|
||||
- [Unsandboxed](#unsandboxed)
|
||||
|
||||
## AddPackage
|
||||
|
||||
The core of CPMUtil is the [`AddPackage`](./AddPackage.md) function. [`AddPackage`](./AddPackage.md) itself is fully CMake-based, and largely serves as an interface between CPM and the rest of CPMUtil.
|
||||
|
||||
## AddCIPackage
|
||||
|
||||
[`AddCIPackage`](./AddCIPackage.md) adds a package that follows [crueter's CI repository spec](https://github.com/crueter-ci).
|
||||
|
||||
## AddJsonPackage
|
||||
|
||||
[`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.md) adds a specific version of Qt to your project.
|
||||
|
||||
## Lists
|
||||
|
||||
CPMUtil will create three lists of dependencies where `AddPackage` or similar was used. Each is in order of addition.
|
||||
|
||||
- `CPM_PACKAGE_NAMES`: The names of packages included by CPMUtil
|
||||
- `CPM_PACKAGE_URLS`: The URLs to project/repo pages of packages
|
||||
- `CPM_PACKAGE_SHAS`: Short version identifiers for each package
|
||||
- If the package was included as a system package, `(system)` is appended thereafter
|
||||
- Packages whose versions can't be deduced will be left as `unknown`.
|
||||
|
||||
For an example of how this might be implemented in an application, see Eden's implementation:
|
||||
|
||||
- [`dep_hashes.h.in`](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/src/dep_hashes.h.in)
|
||||
- [`GenerateDepHashes.cmake`](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CMakeModules/GenerateDepHashes.cmake)
|
||||
- [`deps_dialog.cpp`](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/src/yuzu/deps_dialog.cpp)
|
||||
|
||||
## For Packagers
|
||||
|
||||
If you are packaging a project that uses CPMUtil, read this!
|
||||
|
||||
### Network Sandbox
|
||||
|
||||
For sandboxed environments (e.g. Gentoo, nixOS) you must install all dependencies to the system beforehand and set `-DCPMUTIL_FORCE_SYSTEM=ON`. If a dependency is missing, get creating!
|
||||
|
||||
Alternatively, if CPMUtil pulls in a package that has no suitable way to install or use a system version, download it separately and pass `-DPackageName_DIR=/path/to/downloaded/dir` (e.g. shaders)
|
||||
|
||||
### Unsandboxed
|
||||
|
||||
For others (AUR, MPR, etc). CPMUtil will handle everything for you, including if some of the project's dependencies are missing from your distribution's repositories. That is pretty much half the reason I created this behemoth, after all.
|
||||
Vendored
+1
-2
@@ -6,8 +6,7 @@
|
||||
|
||||
# TODO(crueter): A lot of this should be moved to the root.
|
||||
# otherwise we have to do weird shenanigans with library linking and stuff
|
||||
|
||||
include(CPMUtil)
|
||||
# Or just add a CPMUtil thing to propagate packages
|
||||
|
||||
# Explicitly declare this option here to propagate to the oaknut CPM call
|
||||
option(DYNARMIC_TESTS "Build tests" ${BUILD_TESTING})
|
||||
|
||||
Vendored
-249
@@ -1,249 +0,0 @@
|
||||
{
|
||||
"vulkan-memory-allocator": {
|
||||
"package": "VulkanMemoryAllocator",
|
||||
"repo": "GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator",
|
||||
"tag": "v%VERSION%",
|
||||
"hash": "deb5902ef8db0e329fbd5f3f4385eb0e26bdd9f14f3a2334823fb3fe18f36bc5d235d620d6e5f6fe3551ec3ea7038638899db8778c09f6d5c278f5ff95c3344b",
|
||||
"find_args": "CONFIG",
|
||||
"git_version": "3.3.0"
|
||||
},
|
||||
"sirit": {
|
||||
"repo": "eden-emulator/sirit",
|
||||
"git_version": "1.0.5",
|
||||
"tag": "v%VERSION%",
|
||||
"artifact": "sirit-source-%VERSION%.tar.zst",
|
||||
"hash_suffix": "sha512sum",
|
||||
"find_args": "CONFIG",
|
||||
"options": [
|
||||
"SIRIT_USE_SYSTEM_SPIRV_HEADERS ON"
|
||||
]
|
||||
},
|
||||
"sirit-ci": {
|
||||
"ci": true,
|
||||
"package": "sirit",
|
||||
"name": "sirit",
|
||||
"repo": "eden-emulator/sirit",
|
||||
"version": "1.0.5"
|
||||
},
|
||||
"httplib": {
|
||||
"repo": "yhirose/cpp-httplib",
|
||||
"tag": "v%VERSION%",
|
||||
"hash": "159ed94965018f2a371d45a3bfc1961e5fb1549e501ded70a6b4532d7fe99d0579c18b5195aff6e35f96f399b426cea2650ec9fb75ef80d4c9edeccb51f2e6c9",
|
||||
"git_version": "0.46.0",
|
||||
"find_args": "MODULE GLOBAL",
|
||||
"patches": [
|
||||
"0001-mingw.patch"
|
||||
],
|
||||
"options": [
|
||||
"HTTPLIB_REQUIRE_OPENSSL ON",
|
||||
"HTTPLIB_DISABLE_MACOSX_AUTOMATIC_ROOT_CERTIFICATES ON"
|
||||
]
|
||||
},
|
||||
"cpp-jwt": {
|
||||
"version": "1.4",
|
||||
"repo": "arun11299/cpp-jwt",
|
||||
"sha": "7f24eb4c32",
|
||||
"hash": "d11cbd5ddb3197b4c5ca15679bcd76a49963e7b530b7dd132db91e042925efa20dfb2c24ccfbe7ef82a7012af80deff0f72ee25851312ae80381a462df8534b8",
|
||||
"find_args": "CONFIG",
|
||||
"options": [
|
||||
"CPP_JWT_USE_VENDORED_NLOHMANN_JSON OFF"
|
||||
],
|
||||
"patches": [
|
||||
"0001-fix-missing-decl.patch"
|
||||
]
|
||||
},
|
||||
"xbyak": {
|
||||
"package": "xbyak",
|
||||
"repo": "herumi/xbyak",
|
||||
"tag": "v%VERSION%",
|
||||
"hash": "b6475276b2faaeb315734ea8f4f8bd87ededcee768961b39679bee547e7f3e98884d8b7851e176d861dab30a80a76e6ea302f8c111483607dde969b4797ea95a",
|
||||
"git_version": "7.35.2"
|
||||
},
|
||||
"oaknut": {
|
||||
"repo": "eden-emulator/oaknut",
|
||||
"version": "2.0.1",
|
||||
"git_version": "2.0.3",
|
||||
"tag": "v%VERSION%",
|
||||
"hash": "9697e80a7d5d9bcb3ce51051a9a24962fb90ca79d215f1f03ae6b58da8ba13a63b5dda1b4dde3d26ac6445029696b8ef2883f4e5a777b342bba01283ed293856"
|
||||
},
|
||||
"lagoon": {
|
||||
"repo": "loongson-community/lagoon",
|
||||
"tag": "%VERSION%",
|
||||
"version": "1.0.0",
|
||||
"hash": "b9380f99c6effaeccc6d8f81d4942e852c11ad28613df637e155451556ae5826f93765bee57a5c87a9740d2bd1db463ad0f55a947772fe9d57eeabae3efa373e"
|
||||
},
|
||||
"libadrenotools": {
|
||||
"repo": "eden-emulator/libadrenotools",
|
||||
"sha": "8ba23b42d7",
|
||||
"hash": "f6526620cb752876edc5ed4c0925d57b873a8218ee09ad10859ee476e9333259784f61c1dcc55a2bcba597352d18aff22cd2e4c1925ec2ae94074e09d7da2265",
|
||||
"patches": [
|
||||
"0001-linkerns-cpm.patch"
|
||||
]
|
||||
},
|
||||
"oboe": {
|
||||
"repo": "google/oboe",
|
||||
"tag": "%VERSION%",
|
||||
"hash": "ce4011afe7345370d4ead3b891cd69a5ef224b129535783586c0ca75051d303ed446e6c7f10bde8da31fff58d6e307f1732a3ffd03b249f9ef1fd48fd4132715",
|
||||
"git_version": "1.10.0",
|
||||
"bundled": true
|
||||
},
|
||||
"unordered-dense": {
|
||||
"package": "unordered_dense",
|
||||
"repo": "martinus/unordered_dense",
|
||||
"sha": "7b55cab841",
|
||||
"hash": "d2106f6640f6bfb81755e4b8bfb64982e46ec4a507cacdb38f940123212ccf35a20b43c70c6f01d7bfb8c246d1a16f7845d8052971949cea9def1475e3fa02c8",
|
||||
"find_args": "CONFIG",
|
||||
"bundled": true,
|
||||
"patches": [
|
||||
"0001-avoid-memset-when-clearing-an-empty-table.patch"
|
||||
]
|
||||
},
|
||||
"enet": {
|
||||
"repo": "lsalzman/enet",
|
||||
"tag": "v%VERSION%",
|
||||
"hash": "a0d2fa8c957704dd49e00a726284ac5ca034b50b00d2b20a94fa1bbfbb80841467834bfdc84aa0ed0d6aab894608fd6c86c3b94eee46343f0e6d9c22e391dbf9",
|
||||
"version": "1.3",
|
||||
"git_version": "1.3.18",
|
||||
"find_args": "MODULE"
|
||||
},
|
||||
"spirv-headers": {
|
||||
"package": "SPIRV-Headers",
|
||||
"repo": "KhronosGroup/SPIRV-Headers",
|
||||
"sha": "04f10f650d",
|
||||
"hash": "cae8cd179c9013068876908fecc1d158168310ad6ac250398a41f0f5206ceff6469e2aaeab9c820bce9d1b08950c725c89c46e94b89a692be9805432cf749396",
|
||||
"options": [
|
||||
"SPIRV_WERROR OFF"
|
||||
]
|
||||
},
|
||||
"cubeb": {
|
||||
"repo": "mozilla/cubeb",
|
||||
"sha": "fa02160712",
|
||||
"hash": "8a4bcb2f83ba590f52c66626e895304a73eb61928dbc57777e1822e55378e3568366f17f9da4b80036cc2ef4ea9723c32abf6e7d9bbe00fb03654f0991596ab0",
|
||||
"find_args": "CONFIG",
|
||||
"options": [
|
||||
"USE_SANITIZERS OFF",
|
||||
"BUILD_TESTS OFF",
|
||||
"BUILD_TOOLS OFF",
|
||||
"BUNDLE_SPEEX ON"
|
||||
]
|
||||
},
|
||||
"sdl3-ci": {
|
||||
"ci": true,
|
||||
"package": "SDL3",
|
||||
"name": "SDL3",
|
||||
"repo": "crueter-ci/SDL3",
|
||||
"version": "3.4.8-d57c3b685c",
|
||||
"min_version": "3.2.10"
|
||||
},
|
||||
"catch2": {
|
||||
"package": "Catch2",
|
||||
"repo": "catchorg/Catch2",
|
||||
"tag": "v%VERSION%",
|
||||
"hash": "7eea385d79d88a5690cde131fe7ccda97d5c54ea09d6f515000d7bf07c828809d61c1ac99912c1ee507cf933f61c1c47ecdcc45df7850ffa82714034b0fccf35",
|
||||
"version": "3.0.1",
|
||||
"git_version": "3.13.0",
|
||||
"patches": [
|
||||
"0001-solaris-isnan-fix.patch"
|
||||
]
|
||||
},
|
||||
"discord-rpc": {
|
||||
"package": "DiscordRPC",
|
||||
"repo": "eden-emulator/discord-rpc",
|
||||
"sha": "0d8b2d6a37",
|
||||
"hash": "8213c43dcb0f7d479f5861091d111ed12fbdec1e62e6d729d65a4bc181d82f48a35d5fd3cd5c291f2393ac7c9681eabc6b76609755f55376284c8a8d67e148f3",
|
||||
"find_args": "MODULE"
|
||||
},
|
||||
"simpleini": {
|
||||
"package": "SimpleIni",
|
||||
"repo": "brofield/simpleini",
|
||||
"tag": "v%VERSION%",
|
||||
"hash": "b937c18a7b6277d77ca7ebfb216af4984810f77af4c32d101b7685369a4bd5eb61406223f82698e167e6311a728d07415ab59639fdf19eff71ad6dc2abfda989",
|
||||
"find_args": "MODULE",
|
||||
"git_version": "4.25"
|
||||
},
|
||||
"sdl3": {
|
||||
"package": "SDL3",
|
||||
"repo": "libsdl-org/SDL",
|
||||
"tag": "release-%VERSION%",
|
||||
"hash": "df5a323af7ac366661a3c0e887969c72584d232f3cc211419d59b0487b620b6b2859d4549c9e8df002ee489290062e466fcfddf7edc0872a37b1f2845e81c0f3",
|
||||
"git_version": "3.4.8",
|
||||
"version": "3.2.10"
|
||||
},
|
||||
"moltenvk": {
|
||||
"repo": "V380-Ori/Ryujinx.MoltenVK",
|
||||
"tag": "v%VERSION%-ryujinx",
|
||||
"git_version": "1.4.1",
|
||||
"artifact": "MoltenVK-macOS.tar",
|
||||
"hash": "5695b36ca5775819a71791557fcb40a4a5ee4495be6b8442e0b666d0c436bec02aae68cc6210183f7a5c986bdbec0e117aecfad5396e496e9c2fd5c89133a347",
|
||||
"bundled": true
|
||||
},
|
||||
"gamemode": {
|
||||
"repo": "FeralInteractive/gamemode",
|
||||
"sha": "ce6fe122f3",
|
||||
"hash": "e87ec14ed3e826d578ebf095c41580069dda603792ba91efa84f45f4571a28f4d91889675055fd6f042d7dc25b0b9443daf70963ae463e38b11bcba95f4c65a9",
|
||||
"version": "1.7",
|
||||
"find_args": "MODULE"
|
||||
},
|
||||
"biscuit": {
|
||||
"repo": "lioncash/biscuit",
|
||||
"tag": "v%VERSION%",
|
||||
"hash": "1229f345b014f7ca544dedb4edb3311e41ba736f9aa9a67f88b5f26f3c983288c6bb6cdedcfb0b8a02c63088a37e6a0d7ba97d9c2a4d721b213916327cffe28a",
|
||||
"version": "0.9.1",
|
||||
"git_version": "0.19.0"
|
||||
},
|
||||
"libusb": {
|
||||
"repo": "libusb/libusb",
|
||||
"tag": "v%VERSION%",
|
||||
"hash": "98c5f7940ff06b25c9aa65aa98e23de4c79a4c1067595f4c73cc145af23a1c286639e1ba11185cd91bab702081f307b973f08a4c9746576dc8d01b3620a3aeb5",
|
||||
"find_args": "MODULE",
|
||||
"git_version": "1.0.29",
|
||||
"patches": [
|
||||
"0001-netbsd-gettime.patch"
|
||||
]
|
||||
},
|
||||
"ffmpeg": {
|
||||
"repo": "FFmpeg/FFmpeg",
|
||||
"sha": "c7b5f1537d",
|
||||
"hash": "ed177621176b3961bdcaa339187d3a7688c1c8b060b79c4bb0257cbc67ad7021ae5d5adca5303b45625abbbe3d9aafdd87ce777b8690ac295290d744c875489a",
|
||||
"bundled": true
|
||||
},
|
||||
"ffmpeg-ci": {
|
||||
"ci": true,
|
||||
"package": "FFmpeg",
|
||||
"name": "ffmpeg",
|
||||
"repo": "crueter-ci/FFmpeg",
|
||||
"version": "8.0.1-c7b5f1537d",
|
||||
"min_version": "4.1"
|
||||
},
|
||||
"tzdb": {
|
||||
"package": "nx_tzdb",
|
||||
"repo": "eden-emu/tzdb_to_nx",
|
||||
"git_host": "git.eden-emu.dev",
|
||||
"artifact": "%VERSION%.tar.gz",
|
||||
"tag": "%VERSION%",
|
||||
"hash": "cce65a12bf90f4ead43b24a0b95dfad77ac3d9bfbaaf66c55e6701346e7a1e44ca5d2f23f47ee35ee02271eb1082bf1762af207aad9fb236f1c8476812d008ed",
|
||||
"version": "121125",
|
||||
"git_version": "230326"
|
||||
},
|
||||
"vulkan-headers": {
|
||||
"repo": "KhronosGroup/Vulkan-Headers",
|
||||
"package": "VulkanHeaders",
|
||||
"version": "1.4.317",
|
||||
"hash": "d2846ea228415772645eea4b52a9efd33e6a563043dd3de059e798be6391a8f0ca089f455ae420ff22574939ed0f48ed7c6ff3d5a9987d5231dbf3b3f89b484b",
|
||||
"git_version": "1.4.345",
|
||||
"tag": "v%VERSION%"
|
||||
},
|
||||
"vulkan-utility-libraries": {
|
||||
"repo": "KhronosGroup/Vulkan-Utility-Libraries",
|
||||
"package": "VulkanUtilityLibraries",
|
||||
"hash": "114f6b237a6dcba923ccc576befb5dea3f1c9b3a30de7dc741f234a831d1c2d52d8a224afb37dd57dffca67ac0df461eaaab6a5ab5e503b393f91c166680c3e1",
|
||||
"git_version": "1.4.345",
|
||||
"tag": "v%VERSION%"
|
||||
},
|
||||
"frozen": {
|
||||
"package": "frozen",
|
||||
"repo": "serge-sans-paille/frozen",
|
||||
"sha": "61dce5ae18",
|
||||
"hash": "b8dfe741c82bc178dfc9749d4ab5a130cee718d9ee7b71d9b547cf5f7f23027ed0152ad250012a8546399fcc1e12187efc68d89d6731256c4d2df7d04eef8d5c"
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -83,9 +83,10 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
|
||||
ENABLE_OVERLAY("enable_overlay"),
|
||||
|
||||
// GPU Logging
|
||||
GPU_LOGGING_ENABLED("gpu_logging_enabled"),
|
||||
GPU_LOG_VULKAN_CALLS("gpu_log_vulkan_calls"),
|
||||
GPU_LOG_SHADER_DUMPS("gpu_log_shader_dumps"),
|
||||
DUMP_GUEST_SHADERS("dump_guest_shaders"),
|
||||
DUMP_MACROS("dump_macros"),
|
||||
GPU_LOG_MEMORY_TRACKING("gpu_log_memory_tracking"),
|
||||
GPU_LOG_DRIVER_DEBUG("gpu_log_driver_debug"),
|
||||
|
||||
|
||||
+14
-7
@@ -931,13 +931,6 @@ abstract class SettingsItem(
|
||||
)
|
||||
|
||||
// GPU Logging settings
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.GPU_LOGGING_ENABLED,
|
||||
titleId = R.string.gpu_logging_enabled,
|
||||
descriptionId = R.string.gpu_logging_enabled_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SingleChoiceSetting(
|
||||
ByteSetting.GPU_LOG_LEVEL,
|
||||
@@ -954,6 +947,13 @@ abstract class SettingsItem(
|
||||
descriptionId = R.string.gpu_log_vulkan_calls_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.DUMP_GUEST_SHADERS,
|
||||
titleId = R.string.dump_guest_shaders,
|
||||
descriptionId = R.string.dump_guest_shaders_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.GPU_LOG_SHADER_DUMPS,
|
||||
@@ -961,6 +961,13 @@ abstract class SettingsItem(
|
||||
descriptionId = R.string.gpu_log_shader_dumps_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.DUMP_MACROS,
|
||||
titleId = R.string.dump_macros,
|
||||
descriptionId = R.string.dump_macros_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.GPU_LOG_MEMORY_TRACKING,
|
||||
|
||||
+11
-8
@@ -1288,14 +1288,17 @@ class SettingsFragmentPresenter(
|
||||
add(ShortSetting.DEBUG_KNOBS.key)
|
||||
add(StringSetting.PROGRAM_ARGS.key)
|
||||
|
||||
add(HeaderSetting(R.string.gpu_logging_header))
|
||||
add(BooleanSetting.GPU_LOGGING_ENABLED.key)
|
||||
add(ByteSetting.GPU_LOG_LEVEL.key)
|
||||
add(BooleanSetting.GPU_LOG_VULKAN_CALLS.key)
|
||||
add(BooleanSetting.GPU_LOG_SHADER_DUMPS.key)
|
||||
add(BooleanSetting.GPU_LOG_MEMORY_TRACKING.key)
|
||||
add(BooleanSetting.GPU_LOG_DRIVER_DEBUG.key)
|
||||
add(IntSetting.GPU_LOG_RING_BUFFER_SIZE.key)
|
||||
if (!NativeConfig.isPerGameConfigLoaded()) {
|
||||
add(HeaderSetting(R.string.gpu_logging_header))
|
||||
add(ByteSetting.GPU_LOG_LEVEL.key)
|
||||
add(BooleanSetting.GPU_LOG_VULKAN_CALLS.key)
|
||||
add(BooleanSetting.DUMP_GUEST_SHADERS.key)
|
||||
add(BooleanSetting.GPU_LOG_SHADER_DUMPS.key)
|
||||
add(BooleanSetting.DUMP_MACROS.key)
|
||||
add(BooleanSetting.GPU_LOG_MEMORY_TRACKING.key)
|
||||
add(BooleanSetting.GPU_LOG_DRIVER_DEBUG.key)
|
||||
add(IntSetting.GPU_LOG_RING_BUFFER_SIZE.key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -569,8 +569,6 @@
|
||||
|
||||
<!-- GPU Logging strings -->
|
||||
<string name="gpu_logging_header">تسجيل وحدة معالجة الرسومات</string>
|
||||
<string name="gpu_logging_enabled">تمكين تسجيل وحدة معالجة الرسومات</string>
|
||||
<string name="gpu_logging_enabled_description">تسجيل عمليات وحدة معالجة الرسومات في ملف eden_gpu.log لتصحيح أخطاء برامج تشغيل Adreno</string>
|
||||
<string name="gpu_log_level">مستوى السجل</string>
|
||||
<string name="gpu_log_level_description">مستوى التفاصيل لسجلات وحدة معالجة الرسومات (كلما زاد المستوى، زادت التفاصيل وزادت التكاليف الإضافية)</string>
|
||||
<string name="gpu_log_vulkan_calls">تسجيل استدعاءات واجهة برمجة تطبيقات Vulkan</string>
|
||||
|
||||
@@ -563,8 +563,6 @@
|
||||
|
||||
<!-- GPU Logging strings -->
|
||||
<string name="gpu_logging_header">Registros de la GPU</string>
|
||||
<string name="gpu_logging_enabled">Activar los registros de la GPU</string>
|
||||
<string name="gpu_logging_enabled_description">Registra las operaciones de la GPU en eden_gpu.log para la depuración de los controladores de Adreno</string>
|
||||
<string name="gpu_log_level">Nivel de registros</string>
|
||||
<string name="gpu_log_level_description">Nivel de detalle de los registros de la GPU (más alto = más detalles, más sobrecarga)</string>
|
||||
<string name="gpu_log_vulkan_calls">Registros de llamadas del API de Vulkan</string>
|
||||
|
||||
@@ -525,7 +525,6 @@
|
||||
|
||||
<!-- GPU Logging strings -->
|
||||
<string name="gpu_logging_header">Journalisation GPU</string>
|
||||
<string name="gpu_logging_enabled">Activer la journalisation GPU</string>
|
||||
<string name="gpu_log_level">Niveau de journalisation</string>
|
||||
<string name="gpu_log_vulkan_calls">Journaliser les appels API Vulkan</string>
|
||||
<string name="gpu_log_shader_dumps">Extraire les shaders</string>
|
||||
|
||||
@@ -562,8 +562,6 @@
|
||||
|
||||
<!-- GPU Logging strings -->
|
||||
<string name="gpu_logging_header">Ведение журнала ГПУ</string>
|
||||
<string name="gpu_logging_enabled">Включить ведение журнала ГПУ</string>
|
||||
<string name="gpu_logging_enabled_description">Записывать операции ГПУ в файл eden_gpu.log для отладки драйверов Adreno</string>
|
||||
<string name="gpu_log_level">Уровень журналирования</string>
|
||||
<string name="gpu_log_level_description">Уровень детализации логов ГПУ (больше значение = больше деталей, выше нагрузка)</string>
|
||||
<string name="gpu_log_vulkan_calls">Записывать вызовы Vulkan API</string>
|
||||
|
||||
@@ -565,8 +565,6 @@
|
||||
|
||||
<!-- GPU Logging strings -->
|
||||
<string name="gpu_logging_header">Журналювання ГП</string>
|
||||
<string name="gpu_logging_enabled">Увімкнути журналювання ГП</string>
|
||||
<string name="gpu_logging_enabled_description">Журналювати операції ГП до eden_gpu.log для зневадження драйверів Adreno</string>
|
||||
<string name="gpu_log_level">Рівень журналювання</string>
|
||||
<string name="gpu_log_level_description">Рівень подробиць у журналі ГП (вищий = більше подробиць, більший вплив на швидкодію)</string>
|
||||
<string name="gpu_log_vulkan_calls">Записувати виклики API Vulkan</string>
|
||||
|
||||
@@ -97,7 +97,7 @@
|
||||
<string name="buffer_reorder_disable_description">勾选时,禁用映射内存上传的重排序功能,允许将上传与特定绘制关联。在某些情况下可能会降低性能。</string>
|
||||
|
||||
<string name="use_sync_core">同步核心速度</string>
|
||||
<string name="use_sync_core_description">将核心速度与最大速度百分比同步,在不改变游戏实际速度的情况下提高性能。</string>
|
||||
<string name="use_sync_core_description">将核心时钟周期速度与最大速度百分比同步,从而在不改变游戏实际速度的情况下提升性能。</string>
|
||||
<string name="cpuopt_unsafe_host_mmu">启用主机 MMU 模拟</string>
|
||||
<string name="cpuopt_unsafe_host_mmu_description">此优化可加速来宾程序的内存访问。启用后,来宾内存读取/写入将直接在内存中执行并利用主机的 MMU。禁用此功能将强制所有内存访问使用软件 MMU 模拟。</string>
|
||||
<string name="debug_knobs">调试开关</string>
|
||||
@@ -248,7 +248,7 @@
|
||||
<string name="install_prod_keys">安装 prod.keys 文件</string>
|
||||
<string name="install_prod_keys_description">需要密钥文件来解密游戏</string>
|
||||
<string name="install_prod_keys_warning">跳过添加密钥文件?</string>
|
||||
<string name="install_prod_keys_warning_description">对于商业游戏,需要有效的密钥文件才能运行。如果没有密钥文件,将只能运行自制软件。</string>
|
||||
<string name="install_prod_keys_warning_description">模拟零售版游戏需要有效的密钥。如果选择继续,仅有自制软件可以正常运行。</string>
|
||||
<string name="install_prod_keys_warning_help">https://yuzu-mirror.github.io/help/quickstart/#guide-introduction</string>
|
||||
<string name="install_firmware_warning">跳过添加固件?</string>
|
||||
<string name="emulator_data">设置模拟器数据</string>
|
||||
@@ -291,7 +291,7 @@
|
||||
<string name="gpu_driver_fetcher">GPU驱动获取器</string>
|
||||
<string name="gpu_driver_manager">GPU 驱动管理器</string>
|
||||
<string name="install_gpu_driver_description">安装替代的驱动程序以获得更好的性能和精度</string>
|
||||
<string name="advanced_settings">高级选项</string>
|
||||
<string name="advanced_settings">高级设置</string>
|
||||
<string name="settings_description">更改模拟器设置</string>
|
||||
<string name="search_recently_played">最近游玩</string>
|
||||
<string name="search_recently_added">最近添加</string>
|
||||
@@ -418,6 +418,9 @@
|
||||
<string name="cpu_accuracy">CPU 精度</string>
|
||||
<string name="value_with_units">%1$s%2$s</string>
|
||||
|
||||
<string name="program_args">自制软件参数</string>
|
||||
<string name="program_args_description">启动时传递给自制软件的命令行参数(例如 -noglsl)。</string>
|
||||
|
||||
<!-- System settings strings -->
|
||||
<string name="device_name">设备名称</string>
|
||||
<string name="use_docked_mode">主机模式</string>
|
||||
@@ -433,9 +436,9 @@
|
||||
<!-- CPU -->
|
||||
<string name="fast_cpu_time">CPU 超频</string>
|
||||
<string name="fast_cpu_time_description">强制模拟的 CPU 以更高的时钟频率运行,从而消除某些帧率限制。使用“升频”(1700MHz)以在 Switch 的最高原生时钟频率运行,或使用“快速”(2000MHz)以 2 倍时钟频率运行。</string>
|
||||
<string name="custom_cpu_ticks">自定义CPU时钟</string>
|
||||
<string name="custom_cpu_ticks_description">设置自定义的CPU时钟值。更高的值可能提高性能,但也可能导致游戏卡顿。建议范围为77-21000。</string>
|
||||
<string name="cpu_ticks">时钟</string>
|
||||
<string name="custom_cpu_ticks">自定义 CPU 时钟周期</string>
|
||||
<string name="custom_cpu_ticks_description">设定自定义的 CPU 时钟周期值。更高的值可以提升性能,但也可能导致游戏冻结卡死。建议的赋值范围为77-21000。</string>
|
||||
<string name="cpu_ticks">时钟周期</string>
|
||||
<string name="memory_layout">内存布局</string>
|
||||
<string name="memory_layout_description">(实验性) 更改模拟内存布局。此项设置并不会提升性能,但可能有助于游戏通过 mods 来利用高分辨率。请不要在内存不大于 8GB 的手机上使用。仅适用于 Dynamic(JIT)后端。</string>
|
||||
|
||||
@@ -466,7 +469,7 @@
|
||||
<string name="anisotropic_filtering">各向异性过滤</string>
|
||||
<string name="anisotropic_filtering_description">提高斜角的纹理质量</string>
|
||||
<string name="vram_usage_mode">显存使用模式</string>
|
||||
<string name="vram_usage_mode_description">控制显存分配策略</string>
|
||||
<string name="vram_usage_mode_description">控制显存分配与释放策略</string>
|
||||
<string name="accelerate_astc">ASTC解码方式</string>
|
||||
<string name="accelerate_astc_description">选择渲染时使用的 ASTC 压缩纹理解码方式:CPU(缓慢,安全),GPU(快速,推荐),或 CPU 异步(无卡顿,但可能导致问题)</string>
|
||||
|
||||
@@ -494,7 +497,7 @@
|
||||
<string name="skip_cpu_inner_invalidation">跳过CPU内部无效化</string>
|
||||
<string name="skip_cpu_inner_invalidation_description">在更新内存时跳过某些 CPU 端的缓存失效操作,从而降低 CPU 占用率并提升性能。可能会在某些游戏中引发故障点或崩溃。</string>
|
||||
<string name="antiflicker">防闪烁</string>
|
||||
<string name="antiflicker_description">强制 GPU 围栏回调等待已提交的 GPU 任务。配合“快速 GPU 模式”一起使用,以避免画面闪烁现象,仅会牺牲少量性能。</string>
|
||||
<string name="antiflicker_description">强制 GPU 围栏回调等待已提交的 GPU 任务。配合“快速 GPU 模式”一起使用,以牺牲少量性能为代价来避免画面闪烁现象。</string>
|
||||
<string name="fix_bloom_effects">修复 Bloom 效果</string>
|
||||
<string name="fix_bloom_effects_description">减少《智慧的再现》和《众神的三角力量2》(Adreno A6XX - A7XX/ Turnip)中的 bloom 模糊,并移除《Burnout》中的 bloom 效果。警告:可能会导致在其他游戏中出现图形异常。</string>
|
||||
<string name="emulate_bgr565">模拟 BGR565</string>
|
||||
@@ -539,8 +542,8 @@
|
||||
|
||||
<!-- Debug settings strings -->
|
||||
<string name="cpu">CPU</string>
|
||||
<string name="use_auto_stub">使用自动存根</string>
|
||||
<string name="use_auto_stub_description">自动补全缺失的服务和功能。可提高兼容性但可能导致崩溃和稳定性问题。</string>
|
||||
<string name="use_auto_stub">使用自动占位处理</string>
|
||||
<string name="use_auto_stub_description">自动为缺失的服务和功能生成占位代码。这可能会提高兼容性,但也可能导致崩溃和稳定性问题</string>
|
||||
|
||||
<string name="gpu">GPU</string>
|
||||
<string name="renderer_api">API</string>
|
||||
@@ -556,8 +559,6 @@
|
||||
|
||||
<!-- GPU Logging strings -->
|
||||
<string name="gpu_logging_header">GPU 日志</string>
|
||||
<string name="gpu_logging_enabled">启用 GPU 日志</string>
|
||||
<string name="gpu_logging_enabled_description">将 GPU 操作记录至 eden_gpu.log 以供调试 Adreno 驱动</string>
|
||||
<string name="gpu_log_level">日志等级</string>
|
||||
<string name="gpu_log_level_description">GPU 日志的详细级别(数值越高 = 细节越多,开销越大)</string>
|
||||
<string name="gpu_log_vulkan_calls">记录 Vulkan API 调用</string>
|
||||
@@ -661,9 +662,9 @@
|
||||
<string name="shutting_down">正在关闭…</string>
|
||||
<string name="reset_setting_confirmation">您要将此设定重置为默认值吗?</string>
|
||||
<string name="reset_to_default">恢复默认</string>
|
||||
<string name="reset_to_default_description">重置所有高级选项</string>
|
||||
<string name="reset_to_default_description">重置所有高级设置</string>
|
||||
<string name="reset_all_settings">重置所有设置项?</string>
|
||||
<string name="reset_all_settings_description">所有高级选项都将被重置,此动作无法还原。</string>
|
||||
<string name="reset_all_settings_description">所有高级设置都将重置为默认。此操作无法撤销。</string>
|
||||
<string name="settings_reset">重置设置项</string>
|
||||
<string name="close">关闭</string>
|
||||
<string name="learn_more">了解更多</string>
|
||||
@@ -790,7 +791,7 @@
|
||||
<string name="clear_shader_cache_warning_description">由于着色器缓存需要重新生成,您将遇到更多卡顿</string>
|
||||
<string name="cleared_shaders_successfully">着色器缓存清除成功</string>
|
||||
<string name="driver_shader_wipe_dialog_title">已清理着色器</string>
|
||||
<string name="driver_shader_wipe_dialog_message">Eden 已自动清除所有着色器缓存以维护 Vulkan 管线验证。这在切换 GPU 驱动程序时是必需的,以防止崩溃和图形损坏。在着色器重建期间您可能会遇到一些卡顿。</string>
|
||||
<string name="driver_shader_wipe_dialog_message">Eden 已自动清空所有着色器缓存,以维持 Vulkan 管线验证流程。在切换 GPU 驱动程序时,此操作是必要的,可防止崩溃和画面异常。在着色器重构期间,您可能会感受到些许卡顿。</string>
|
||||
<string name="addons_game">附加项: %1$s</string>
|
||||
<string name="save_data">保存数据</string>
|
||||
<string name="save_data_description">管理此游戏的保存数据</string>
|
||||
@@ -991,8 +992,8 @@
|
||||
<string name="dma_accuracy_unsafe">不安全</string>
|
||||
<string name="dma_accuracy_safe">安全</string>
|
||||
|
||||
<string name="vram_usage_conservative">保守模式</string>
|
||||
<string name="vram_usage_aggressive">激进模式</string>
|
||||
<string name="vram_usage_conservative">保守式</string>
|
||||
<string name="vram_usage_aggressive">主动式</string>
|
||||
|
||||
<!-- Renderer VSync -->
|
||||
<string name="renderer_vsync_immediate">即时 (关闭)</string>
|
||||
@@ -1014,9 +1015,9 @@
|
||||
<string name="ratio_stretch">拉伸窗口</string>
|
||||
|
||||
<!-- CPU Accuracy -->
|
||||
<string name="cpu_accuracy_accurate">高精度</string>
|
||||
<string name="cpu_accuracy_unsafe">低精度</string>
|
||||
<string name="cpu_accuracy_paranoid">偏执模式</string>
|
||||
<string name="cpu_accuracy_accurate">精准</string>
|
||||
<string name="cpu_accuracy_unsafe">不安全</string>
|
||||
<string name="cpu_accuracy_paranoid">极致</string>
|
||||
<string name="cpu_accuracy_debugging">调试</string>
|
||||
|
||||
<!-- Freedreno Settings -->
|
||||
|
||||
@@ -575,14 +575,16 @@
|
||||
|
||||
<!-- GPU Logging strings -->
|
||||
<string name="gpu_logging_header">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_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_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_shader_dumps">Dump Shaders</string>
|
||||
<string name="gpu_log_shader_dumps_description">Save compiled shader SPIR-V to files</string>
|
||||
<string name="gpu_log_shader_dumps">Dump SPIR-V Shaders</string>
|
||||
<string name="gpu_log_shader_dumps_description">Save recompiled SPIR-V binaries (.spv) to dump folder. Inspect with spirv-dis/spirv-cross/spirv-val.</string>
|
||||
<string name="dump_guest_shaders">Dump Guest (Maxwell) Shaders</string>
|
||||
<string name="dump_guest_shaders_description">Save Maxwell guest shader bytecode files (*.ash) to dump folder. Inspect with nvdisasm.</string>
|
||||
<string name="dump_macros">Dump Maxwell Macros</string>
|
||||
<string name="dump_macros_description">Save Maxwell macro program files (*.macro) to dump folder. Inspect with envydis.</string>
|
||||
<string name="gpu_log_memory_tracking">Track GPU Memory</string>
|
||||
<string name="gpu_log_memory_tracking_description">Monitor GPU memory allocations and deallocations</string>
|
||||
<string name="gpu_log_driver_debug">Driver Debug Info</string>
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -8,10 +11,11 @@
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
AudioCore::AudioCore(Core::System& system) : audio_manager{std::make_unique<AudioManager>()} {
|
||||
AudioCore::AudioCore(Core::System& system) {
|
||||
audio_manager.emplace();
|
||||
CreateSinks();
|
||||
// Must be created after the sinks
|
||||
adsp = std::make_unique<ADSP::ADSP>(system, *output_sink);
|
||||
adsp.emplace(system, *output_sink);
|
||||
}
|
||||
|
||||
AudioCore ::~AudioCore() {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -15,10 +18,7 @@ class System;
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
class AudioManager;
|
||||
/**
|
||||
* Main audio class, stored inside the core, and holding the audio manager, all sinks, and the ADSP.
|
||||
*/
|
||||
/// @brief Main audio class, stored inside the core, and holding the audio manager, all sinks, and the ADSP.
|
||||
class AudioCore {
|
||||
public:
|
||||
explicit AudioCore(Core::System& system);
|
||||
@@ -50,27 +50,22 @@ public:
|
||||
*/
|
||||
Sink::Sink& GetInputSink();
|
||||
|
||||
/**
|
||||
* Get the ADSP.
|
||||
*
|
||||
* @return Ref to the ADSP.
|
||||
*/
|
||||
/// @brief Get the ADSP.
|
||||
/// @return Ref to the ADSP.
|
||||
ADSP::ADSP& ADSP();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Create the sinks on startup.
|
||||
*/
|
||||
/// @brief Create the sinks on startup.
|
||||
void CreateSinks();
|
||||
|
||||
/// Main audio manager for audio in/out
|
||||
std::unique_ptr<AudioManager> audio_manager;
|
||||
std::optional<AudioManager> audio_manager;
|
||||
/// Sink used for audio renderer and audio out
|
||||
std::unique_ptr<Sink::Sink> output_sink;
|
||||
/// Sink used for audio input
|
||||
std::unique_ptr<Sink::Sink> input_sink;
|
||||
/// The ADSP in the sysmodule
|
||||
std::unique_ptr<ADSP::ADSP> adsp;
|
||||
std::optional<ADSP::ADSP> adsp;
|
||||
};
|
||||
|
||||
} // namespace AudioCore
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -41,8 +44,7 @@ void Manager::ReleaseSessionId(const size_t session_id) {
|
||||
Result Manager::LinkToManager() {
|
||||
std::scoped_lock l{mutex};
|
||||
if (!linked_to_manager) {
|
||||
AudioManager& manager{system.AudioCore().GetAudioManager()};
|
||||
manager.SetInManager(std::bind(&Manager::BufferReleaseAndRegister, this));
|
||||
system.AudioCore().GetAudioManager().SetInManager(std::bind(&Manager::BufferReleaseAndRegister, this));
|
||||
linked_to_manager = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,81 +1,77 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "audio_core/audio_manager.h"
|
||||
#include "common/thread.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/audio/errors.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
AudioManager::AudioManager() {
|
||||
thread = std::jthread([this]() { ThreadFunc(); });
|
||||
thread = std::jthread([this](std::stop_token stop_token) {
|
||||
Common::SetCurrentThreadName("AudioManager");
|
||||
std::unique_lock l{events.GetAudioEventLock()};
|
||||
events.ClearEvents();
|
||||
while (!stop_token.stop_requested()) {
|
||||
const auto timed_out{events.Wait(l, std::chrono::seconds(2))};
|
||||
if (events.CheckAudioEventSet(Event::Type::Max)) {
|
||||
break;
|
||||
}
|
||||
for (size_t i = 0; i < buffer_events.size(); i++) {
|
||||
const auto event_type = Event::Type(i);
|
||||
if (events.CheckAudioEventSet(event_type) || timed_out) {
|
||||
if (buffer_events[i]) {
|
||||
buffer_events[i]();
|
||||
}
|
||||
}
|
||||
events.SetAudioEvent(event_type, false);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void AudioManager::Shutdown() {
|
||||
running = false;
|
||||
events.SetAudioEvent(Event::Type::Max, true);
|
||||
thread.join();
|
||||
if (thread.joinable()) {
|
||||
thread.request_stop();
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
Result AudioManager::SetOutManager(BufferEventFunc buffer_func) {
|
||||
if (!running) {
|
||||
return Service::Audio::ResultOperationFailed;
|
||||
if (thread.joinable()) {
|
||||
std::scoped_lock l{lock};
|
||||
const auto index{events.GetManagerIndex(Event::Type::AudioOutManager)};
|
||||
if (buffer_events[index] == nullptr) {
|
||||
buffer_events[index] = std::move(buffer_func);
|
||||
needs_update = true;
|
||||
events.SetAudioEvent(Event::Type::AudioOutManager, true);
|
||||
}
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
std::scoped_lock l{lock};
|
||||
|
||||
const auto index{events.GetManagerIndex(Event::Type::AudioOutManager)};
|
||||
if (buffer_events[index] == nullptr) {
|
||||
buffer_events[index] = std::move(buffer_func);
|
||||
needs_update = true;
|
||||
events.SetAudioEvent(Event::Type::AudioOutManager, true);
|
||||
}
|
||||
return ResultSuccess;
|
||||
return Service::Audio::ResultOperationFailed;
|
||||
}
|
||||
|
||||
Result AudioManager::SetInManager(BufferEventFunc buffer_func) {
|
||||
if (!running) {
|
||||
return Service::Audio::ResultOperationFailed;
|
||||
if (thread.joinable()) {
|
||||
std::scoped_lock l{lock};
|
||||
const auto index{events.GetManagerIndex(Event::Type::AudioInManager)};
|
||||
if (buffer_events[index] == nullptr) {
|
||||
buffer_events[index] = std::move(buffer_func);
|
||||
needs_update = true;
|
||||
events.SetAudioEvent(Event::Type::AudioInManager, true);
|
||||
}
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
std::scoped_lock l{lock};
|
||||
|
||||
const auto index{events.GetManagerIndex(Event::Type::AudioInManager)};
|
||||
if (buffer_events[index] == nullptr) {
|
||||
buffer_events[index] = std::move(buffer_func);
|
||||
needs_update = true;
|
||||
events.SetAudioEvent(Event::Type::AudioInManager, true);
|
||||
}
|
||||
return ResultSuccess;
|
||||
return Service::Audio::ResultOperationFailed;
|
||||
}
|
||||
|
||||
void AudioManager::SetEvent(const Event::Type type, const bool signalled) {
|
||||
events.SetAudioEvent(type, signalled);
|
||||
}
|
||||
|
||||
void AudioManager::ThreadFunc() {
|
||||
std::unique_lock l{events.GetAudioEventLock()};
|
||||
events.ClearEvents();
|
||||
running = true;
|
||||
|
||||
while (running) {
|
||||
const auto timed_out{events.Wait(l, std::chrono::seconds(2))};
|
||||
|
||||
if (events.CheckAudioEventSet(Event::Type::Max)) {
|
||||
break;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < buffer_events.size(); i++) {
|
||||
const auto event_type = static_cast<Event::Type>(i);
|
||||
|
||||
if (events.CheckAudioEventSet(event_type) || timed_out) {
|
||||
if (buffer_events[i]) {
|
||||
buffer_events[i]();
|
||||
}
|
||||
}
|
||||
events.SetAudioEvent(event_type, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace AudioCore
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -66,13 +69,6 @@ public:
|
||||
void SetEvent(Event::Type type, bool signalled);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Main thread, waiting on a manager signal and calling the registered function.
|
||||
*/
|
||||
void ThreadFunc();
|
||||
|
||||
/// Is the main thread running?
|
||||
std::atomic<bool> running{};
|
||||
/// Unused
|
||||
bool needs_update{};
|
||||
/// Events to be set and signalled
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -40,8 +43,7 @@ void Manager::ReleaseSessionId(const size_t session_id) {
|
||||
Result Manager::LinkToManager() {
|
||||
std::scoped_lock l{mutex};
|
||||
if (!linked_to_manager) {
|
||||
AudioManager& manager{system.AudioCore().GetAudioManager()};
|
||||
manager.SetOutManager(std::bind(&Manager::BufferReleaseAndRegister, this));
|
||||
system.AudioCore().GetAudioManager().SetOutManager(std::bind(&Manager::BufferReleaseAndRegister, this));
|
||||
linked_to_manager = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -39,7 +42,7 @@ Result DeviceSession::Initialize(std::string_view name_, SampleFormat sample_for
|
||||
channel_count = channel_count_;
|
||||
session_id = session_id_;
|
||||
handle = handle_;
|
||||
handle->Open();
|
||||
handle->Open(system.Kernel());
|
||||
applet_resource_user_id = applet_resource_user_id_;
|
||||
|
||||
if (type == Sink::StreamType::In) {
|
||||
@@ -60,7 +63,7 @@ void DeviceSession::Finalize() {
|
||||
}
|
||||
|
||||
if (handle) {
|
||||
handle->Close();
|
||||
handle->Close(system.Kernel());
|
||||
handle = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ Result System::Stop() {
|
||||
session->SetVolume(0.0f);
|
||||
session->ClearBuffers();
|
||||
if (buffers.ReleaseBuffers(system.CoreTiming(), *session, true)) {
|
||||
buffer_event->Signal();
|
||||
buffer_event->Signal(system.Kernel());
|
||||
}
|
||||
state = State::Stopped;
|
||||
}
|
||||
@@ -164,7 +164,7 @@ void System::ReleaseBuffers() {
|
||||
|
||||
if (signal) {
|
||||
// Signal if any buffer was released, or if none are registered, we need more.
|
||||
buffer_event->Signal();
|
||||
buffer_event->Signal(system.Kernel());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,7 +181,7 @@ bool System::FlushAudioInBuffers() {
|
||||
buffers.FlushBuffers(buffers_released);
|
||||
|
||||
if (buffers_released > 0) {
|
||||
buffer_event->Signal();
|
||||
buffer_event->Signal(system.Kernel());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ Result System::Stop() {
|
||||
session->SetVolume(0.0f);
|
||||
session->ClearBuffers();
|
||||
if (buffers.ReleaseBuffers(system.CoreTiming(), *session, true)) {
|
||||
buffer_event->Signal();
|
||||
buffer_event->Signal(system.Kernel());
|
||||
}
|
||||
state = State::Stopped;
|
||||
}
|
||||
@@ -162,7 +162,7 @@ void System::ReleaseBuffers() {
|
||||
bool signal{buffers.ReleaseBuffers(system.CoreTiming(), *session, false)};
|
||||
if (signal) {
|
||||
// Signal if any buffer was released, or if none are registered, we need more.
|
||||
buffer_event->Signal();
|
||||
buffer_event->Signal(system.Kernel());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,7 +179,7 @@ bool System::FlushAudioOutBuffers() {
|
||||
buffers.FlushBuffers(buffers_released);
|
||||
|
||||
if (buffers_released > 0) {
|
||||
buffer_event->Signal();
|
||||
buffer_event->Signal(system.Kernel());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
@@ -540,7 +540,7 @@ Result System::Update(std::span<const u8> input, std::span<u8> performance, std:
|
||||
return result;
|
||||
}
|
||||
|
||||
adsp_rendered_event->Clear();
|
||||
adsp_rendered_event->Clear(core.Kernel());
|
||||
num_times_updated++;
|
||||
|
||||
const auto end_time{core.CoreTiming().GetGlobalTimeNs().count()};
|
||||
@@ -624,7 +624,7 @@ void System::SendCommandToDsp() {
|
||||
reset_command_buffers = false;
|
||||
command_buffer_size = command_size;
|
||||
if (remaining_command_count == 0) {
|
||||
adsp_rendered_event->Signal();
|
||||
adsp_rendered_event->Signal(core.Kernel());
|
||||
}
|
||||
} else {
|
||||
audio_renderer.ClearRemainCommandCount(session_id);
|
||||
|
||||
@@ -50,7 +50,6 @@ add_library(
|
||||
elf.h
|
||||
error.cpp
|
||||
error.h
|
||||
expected.h
|
||||
fiber.cpp
|
||||
fiber.h
|
||||
fixed_point.h
|
||||
|
||||
@@ -1,986 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
// This is based on the proposed implementation of std::expected (P0323)
|
||||
// https://github.com/TartanLlama/expected/blob/master/include/tl/expected.hpp
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace Common {
|
||||
|
||||
template <typename T, typename E>
|
||||
class Expected;
|
||||
|
||||
template <typename E>
|
||||
class Unexpected {
|
||||
public:
|
||||
Unexpected() = delete;
|
||||
|
||||
constexpr explicit Unexpected(const E& e) : m_val{e} {}
|
||||
|
||||
constexpr explicit Unexpected(E&& e) : m_val{std::move(e)} {}
|
||||
|
||||
constexpr E& value() & {
|
||||
return m_val;
|
||||
}
|
||||
|
||||
constexpr const E& value() const& {
|
||||
return m_val;
|
||||
}
|
||||
|
||||
constexpr E&& value() && {
|
||||
return std::move(m_val);
|
||||
}
|
||||
|
||||
constexpr const E&& value() const&& {
|
||||
return std::move(m_val);
|
||||
}
|
||||
|
||||
private:
|
||||
E m_val;
|
||||
};
|
||||
|
||||
template <typename E>
|
||||
constexpr auto operator<=>(const Unexpected<E>& lhs, const Unexpected<E>& rhs) {
|
||||
return lhs.value() <=> rhs.value();
|
||||
}
|
||||
|
||||
struct unexpect_t {
|
||||
constexpr explicit unexpect_t() = default;
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct no_init_t {
|
||||
constexpr explicit no_init_t() = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* This specialization is for when T is not trivially destructible,
|
||||
* so the destructor must be called on destruction of `expected'
|
||||
* Additionally, this requires E to be trivially destructible
|
||||
*/
|
||||
template <typename T, typename E, bool = std::is_trivially_destructible_v<T>>
|
||||
requires std::is_trivially_destructible_v<E>
|
||||
struct expected_storage_base {
|
||||
constexpr expected_storage_base() : m_val{T{}}, m_has_val{true} {}
|
||||
|
||||
constexpr expected_storage_base(no_init_t) : m_has_val{false} {}
|
||||
|
||||
template <typename... Args, std::enable_if_t<std::is_constructible_v<T, Args&&...>>* = nullptr>
|
||||
constexpr expected_storage_base(std::in_place_t, Args&&... args)
|
||||
: m_val{std::forward<Args>(args)...}, m_has_val{true} {}
|
||||
|
||||
template <typename U, typename... Args,
|
||||
std::enable_if_t<std::is_constructible_v<T, std::initializer_list<U>&, Args&&...>>* =
|
||||
nullptr>
|
||||
constexpr expected_storage_base(std::in_place_t, std::initializer_list<U> il, Args&&... args)
|
||||
: m_val{il, std::forward<Args>(args)...}, m_has_val{true} {}
|
||||
|
||||
template <typename... Args, std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
|
||||
constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
|
||||
: m_unexpect{std::forward<Args>(args)...}, m_has_val{false} {}
|
||||
|
||||
template <typename U, typename... Args,
|
||||
std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&, Args&&...>>* =
|
||||
nullptr>
|
||||
constexpr explicit expected_storage_base(unexpect_t, std::initializer_list<U> il,
|
||||
Args&&... args)
|
||||
: m_unexpect{il, std::forward<Args>(args)...}, m_has_val{false} {}
|
||||
|
||||
~expected_storage_base() {
|
||||
if (m_has_val) {
|
||||
m_val.~T();
|
||||
}
|
||||
}
|
||||
|
||||
union {
|
||||
T m_val;
|
||||
Unexpected<E> m_unexpect;
|
||||
};
|
||||
|
||||
bool m_has_val;
|
||||
};
|
||||
|
||||
/**
|
||||
* This specialization is for when T is trivially destructible,
|
||||
* so the destructor of `expected` can be trivial
|
||||
* Additionally, this requires E to be trivially destructible
|
||||
*/
|
||||
template <typename T, typename E>
|
||||
requires std::is_trivially_destructible_v<E>
|
||||
struct expected_storage_base<T, E, true> {
|
||||
constexpr expected_storage_base() : m_val{T{}}, m_has_val{true} {}
|
||||
|
||||
constexpr expected_storage_base(no_init_t) : m_has_val{false} {}
|
||||
|
||||
template <typename... Args, std::enable_if_t<std::is_constructible_v<T, Args&&...>>* = nullptr>
|
||||
constexpr expected_storage_base(std::in_place_t, Args&&... args)
|
||||
: m_val{std::forward<Args>(args)...}, m_has_val{true} {}
|
||||
|
||||
template <typename U, typename... Args,
|
||||
std::enable_if_t<std::is_constructible_v<T, std::initializer_list<U>&, Args&&...>>* =
|
||||
nullptr>
|
||||
constexpr expected_storage_base(std::in_place_t, std::initializer_list<U> il, Args&&... args)
|
||||
: m_val{il, std::forward<Args>(args)...}, m_has_val{true} {}
|
||||
|
||||
template <typename... Args, std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
|
||||
constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
|
||||
: m_unexpect{std::forward<Args>(args)...}, m_has_val{false} {}
|
||||
|
||||
template <typename U, typename... Args,
|
||||
std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&, Args&&...>>* =
|
||||
nullptr>
|
||||
constexpr explicit expected_storage_base(unexpect_t, std::initializer_list<U> il,
|
||||
Args&&... args)
|
||||
: m_unexpect{il, std::forward<Args>(args)...}, m_has_val{false} {}
|
||||
|
||||
~expected_storage_base() = default;
|
||||
|
||||
union {
|
||||
T m_val;
|
||||
Unexpected<E> m_unexpect;
|
||||
};
|
||||
|
||||
bool m_has_val;
|
||||
};
|
||||
|
||||
template <typename T, typename E>
|
||||
struct expected_operations_base : expected_storage_base<T, E> {
|
||||
using expected_storage_base<T, E>::expected_storage_base;
|
||||
|
||||
template <typename... Args>
|
||||
void construct(Args&&... args) noexcept {
|
||||
new (std::addressof(this->m_val)) T{std::forward<Args>(args)...};
|
||||
this->m_has_val = true;
|
||||
}
|
||||
|
||||
template <typename Rhs>
|
||||
void construct_with(Rhs&& rhs) noexcept {
|
||||
new (std::addressof(this->m_val)) T{std::forward<Rhs>(rhs).get()};
|
||||
this->m_has_val = true;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void construct_error(Args&&... args) noexcept {
|
||||
new (std::addressof(this->m_unexpect)) Unexpected<E>{std::forward<Args>(args)...};
|
||||
this->m_has_val = false;
|
||||
}
|
||||
|
||||
void assign(const expected_operations_base& rhs) noexcept {
|
||||
if (!this->m_has_val && rhs.m_has_val) {
|
||||
geterr().~Unexpected<E>();
|
||||
construct(rhs.get());
|
||||
} else {
|
||||
assign_common(rhs);
|
||||
}
|
||||
}
|
||||
|
||||
void assign(expected_operations_base&& rhs) noexcept {
|
||||
if (!this->m_has_val && rhs.m_has_val) {
|
||||
geterr().~Unexpected<E>();
|
||||
construct(std::move(rhs).get());
|
||||
} else {
|
||||
assign_common(rhs);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Rhs>
|
||||
void assign_common(Rhs&& rhs) {
|
||||
if (this->m_has_val) {
|
||||
if (rhs.m_has_val) {
|
||||
get() = std::forward<Rhs>(rhs).get();
|
||||
} else {
|
||||
destroy_val();
|
||||
construct_error(std::forward<Rhs>(rhs).geterr());
|
||||
}
|
||||
} else {
|
||||
if (!rhs.m_has_val) {
|
||||
geterr() = std::forward<Rhs>(rhs).geterr();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool has_value() const {
|
||||
return this->m_has_val;
|
||||
}
|
||||
|
||||
constexpr T& get() & {
|
||||
return this->m_val;
|
||||
}
|
||||
|
||||
constexpr const T& get() const& {
|
||||
return this->m_val;
|
||||
}
|
||||
|
||||
constexpr T&& get() && {
|
||||
return std::move(this->m_val);
|
||||
}
|
||||
|
||||
constexpr const T&& get() const&& {
|
||||
return std::move(this->m_val);
|
||||
}
|
||||
|
||||
constexpr Unexpected<E>& geterr() & {
|
||||
return this->m_unexpect;
|
||||
}
|
||||
|
||||
constexpr const Unexpected<E>& geterr() const& {
|
||||
return this->m_unexpect;
|
||||
}
|
||||
|
||||
constexpr Unexpected<E>&& geterr() && {
|
||||
return std::move(this->m_unexpect);
|
||||
}
|
||||
|
||||
constexpr const Unexpected<E>&& geterr() const&& {
|
||||
return std::move(this->m_unexpect);
|
||||
}
|
||||
|
||||
constexpr void destroy_val() {
|
||||
get().~T();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This manages conditionally having a trivial copy constructor
|
||||
* This specialization is for when T is trivially copy constructible
|
||||
* Additionally, this requires E to be trivially copy constructible
|
||||
*/
|
||||
template <typename T, typename E, bool = std::is_trivially_copy_constructible_v<T>>
|
||||
requires std::is_trivially_copy_constructible_v<E>
|
||||
struct expected_copy_base : expected_operations_base<T, E> {
|
||||
using expected_operations_base<T, E>::expected_operations_base;
|
||||
};
|
||||
|
||||
/**
|
||||
* This specialization is for when T is not trivially copy constructible
|
||||
* Additionally, this requires E to be trivially copy constructible
|
||||
*/
|
||||
template <typename T, typename E>
|
||||
requires std::is_trivially_copy_constructible_v<E>
|
||||
struct expected_copy_base<T, E, false> : expected_operations_base<T, E> {
|
||||
using expected_operations_base<T, E>::expected_operations_base;
|
||||
|
||||
expected_copy_base() = default;
|
||||
|
||||
expected_copy_base(const expected_copy_base& rhs)
|
||||
: expected_operations_base<T, E>{no_init_t{}} {
|
||||
if (rhs.has_value()) {
|
||||
this->construct_with(rhs);
|
||||
} else {
|
||||
this->construct_error(rhs.geterr());
|
||||
}
|
||||
}
|
||||
|
||||
expected_copy_base(expected_copy_base&&) = default;
|
||||
|
||||
expected_copy_base& operator=(const expected_copy_base&) = default;
|
||||
|
||||
expected_copy_base& operator=(expected_copy_base&&) = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* This manages conditionally having a trivial move constructor
|
||||
* This specialization is for when T is trivially move constructible
|
||||
* Additionally, this requires E to be trivially move constructible
|
||||
*/
|
||||
template <typename T, typename E, bool = std::is_trivially_move_constructible_v<T>>
|
||||
requires std::is_trivially_move_constructible_v<E>
|
||||
struct expected_move_base : expected_copy_base<T, E> {
|
||||
using expected_copy_base<T, E>::expected_copy_base;
|
||||
};
|
||||
|
||||
/**
|
||||
* This specialization is for when T is not trivially move constructible
|
||||
* Additionally, this requires E to be trivially move constructible
|
||||
*/
|
||||
template <typename T, typename E>
|
||||
requires std::is_trivially_move_constructible_v<E>
|
||||
struct expected_move_base<T, E, false> : expected_copy_base<T, E> {
|
||||
using expected_copy_base<T, E>::expected_copy_base;
|
||||
|
||||
expected_move_base() = default;
|
||||
|
||||
expected_move_base(const expected_move_base&) = default;
|
||||
|
||||
expected_move_base(expected_move_base&& rhs) noexcept(std::is_nothrow_move_constructible_v<T>)
|
||||
: expected_copy_base<T, E>{no_init_t{}} {
|
||||
if (rhs.has_value()) {
|
||||
this->construct_with(std::move(rhs));
|
||||
} else {
|
||||
this->construct_error(std::move(rhs.geterr()));
|
||||
}
|
||||
}
|
||||
|
||||
expected_move_base& operator=(const expected_move_base&) = default;
|
||||
|
||||
expected_move_base& operator=(expected_move_base&&) = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* This manages conditionally having a trivial copy assignment operator
|
||||
* This specialization is for when T is trivially copy assignable
|
||||
* Additionally, this requires E to be trivially copy assignable
|
||||
*/
|
||||
template <typename T, typename E,
|
||||
bool = std::conjunction_v<std::is_trivially_copy_assignable<T>,
|
||||
std::is_trivially_copy_constructible<T>,
|
||||
std::is_trivially_destructible<T>>>
|
||||
requires std::conjunction_v<std::is_trivially_copy_assignable<E>,
|
||||
std::is_trivially_copy_constructible<E>,
|
||||
std::is_trivially_destructible<E>>
|
||||
struct expected_copy_assign_base : expected_move_base<T, E> {
|
||||
using expected_move_base<T, E>::expected_move_base;
|
||||
};
|
||||
|
||||
/**
|
||||
* This specialization is for when T is not trivially copy assignable
|
||||
* Additionally, this requires E to be trivially copy assignable
|
||||
*/
|
||||
template <typename T, typename E>
|
||||
requires std::conjunction_v<std::is_trivially_copy_assignable<E>,
|
||||
std::is_trivially_copy_constructible<E>,
|
||||
std::is_trivially_destructible<E>>
|
||||
struct expected_copy_assign_base<T, E, false> : expected_move_base<T, E> {
|
||||
using expected_move_base<T, E>::expected_move_base;
|
||||
|
||||
expected_copy_assign_base() = default;
|
||||
|
||||
expected_copy_assign_base(const expected_copy_assign_base&) = default;
|
||||
|
||||
expected_copy_assign_base(expected_copy_assign_base&&) = default;
|
||||
|
||||
expected_copy_assign_base& operator=(const expected_copy_assign_base& rhs) {
|
||||
this->assign(rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
expected_copy_assign_base& operator=(expected_copy_assign_base&&) = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* This manages conditionally having a trivial move assignment operator
|
||||
* This specialization is for when T is trivially move assignable
|
||||
* Additionally, this requires E to be trivially move assignable
|
||||
*/
|
||||
template <typename T, typename E,
|
||||
bool = std::conjunction_v<std::is_trivially_move_assignable<T>,
|
||||
std::is_trivially_move_constructible<T>,
|
||||
std::is_trivially_destructible<T>>>
|
||||
requires std::conjunction_v<std::is_trivially_move_assignable<E>,
|
||||
std::is_trivially_move_constructible<E>,
|
||||
std::is_trivially_destructible<E>>
|
||||
struct expected_move_assign_base : expected_copy_assign_base<T, E> {
|
||||
using expected_copy_assign_base<T, E>::expected_copy_assign_base;
|
||||
};
|
||||
|
||||
/**
|
||||
* This specialization is for when T is not trivially move assignable
|
||||
* Additionally, this requires E to be trivially move assignable
|
||||
*/
|
||||
template <typename T, typename E>
|
||||
requires std::conjunction_v<std::is_trivially_move_assignable<E>,
|
||||
std::is_trivially_move_constructible<E>,
|
||||
std::is_trivially_destructible<E>>
|
||||
struct expected_move_assign_base<T, E, false> : expected_copy_assign_base<T, E> {
|
||||
using expected_copy_assign_base<T, E>::expected_copy_assign_base;
|
||||
|
||||
expected_move_assign_base() = default;
|
||||
|
||||
expected_move_assign_base(const expected_move_assign_base&) = default;
|
||||
|
||||
expected_move_assign_base(expected_move_assign_base&&) = default;
|
||||
|
||||
expected_move_assign_base& operator=(const expected_move_assign_base&) = default;
|
||||
|
||||
expected_move_assign_base& operator=(expected_move_assign_base&& rhs) noexcept(
|
||||
std::conjunction_v<std::is_nothrow_move_constructible<T>,
|
||||
std::is_nothrow_move_assignable<T>>) {
|
||||
this->assign(std::move(rhs));
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* expected_delete_ctor_base will conditionally delete copy and move constructors
|
||||
* depending on whether T is copy/move constructible
|
||||
* Additionally, this requires E to be copy/move constructible
|
||||
*/
|
||||
template <typename T, typename E, bool EnableCopy = std::is_copy_constructible_v<T>,
|
||||
bool EnableMove = std::is_move_constructible_v<T>>
|
||||
requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>>
|
||||
struct expected_delete_ctor_base {
|
||||
expected_delete_ctor_base() = default;
|
||||
expected_delete_ctor_base(const expected_delete_ctor_base&) = default;
|
||||
expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = default;
|
||||
expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) = default;
|
||||
expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept = default;
|
||||
};
|
||||
|
||||
template <typename T, typename E>
|
||||
requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>>
|
||||
struct expected_delete_ctor_base<T, E, true, false> {
|
||||
expected_delete_ctor_base() = default;
|
||||
expected_delete_ctor_base(const expected_delete_ctor_base&) = default;
|
||||
expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = delete;
|
||||
expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) = default;
|
||||
expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept = default;
|
||||
};
|
||||
|
||||
template <typename T, typename E>
|
||||
requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>>
|
||||
struct expected_delete_ctor_base<T, E, false, true> {
|
||||
expected_delete_ctor_base() = default;
|
||||
expected_delete_ctor_base(const expected_delete_ctor_base&) = delete;
|
||||
expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = default;
|
||||
expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) = default;
|
||||
expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept = default;
|
||||
};
|
||||
|
||||
template <typename T, typename E>
|
||||
requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>>
|
||||
struct expected_delete_ctor_base<T, E, false, false> {
|
||||
expected_delete_ctor_base() = default;
|
||||
expected_delete_ctor_base(const expected_delete_ctor_base&) = delete;
|
||||
expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = delete;
|
||||
expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) = default;
|
||||
expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* expected_delete_assign_base will conditionally delete copy and move assignment operators
|
||||
* depending on whether T is copy/move constructible + assignable
|
||||
* Additionally, this requires E to be copy/move constructible + assignable
|
||||
*/
|
||||
template <
|
||||
typename T, typename E,
|
||||
bool EnableCopy = std::conjunction_v<std::is_copy_constructible<T>, std::is_copy_assignable<T>>,
|
||||
bool EnableMove = std::conjunction_v<std::is_move_constructible<T>, std::is_move_assignable<T>>>
|
||||
requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>,
|
||||
std::is_copy_assignable<E>, std::is_move_assignable<E>>
|
||||
struct expected_delete_assign_base {
|
||||
expected_delete_assign_base() = default;
|
||||
expected_delete_assign_base(const expected_delete_assign_base&) = default;
|
||||
expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default;
|
||||
expected_delete_assign_base& operator=(const expected_delete_assign_base&) = default;
|
||||
expected_delete_assign_base& operator=(expected_delete_assign_base&&) noexcept = default;
|
||||
};
|
||||
|
||||
template <typename T, typename E>
|
||||
requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>,
|
||||
std::is_copy_assignable<E>, std::is_move_assignable<E>>
|
||||
struct expected_delete_assign_base<T, E, true, false> {
|
||||
expected_delete_assign_base() = default;
|
||||
expected_delete_assign_base(const expected_delete_assign_base&) = default;
|
||||
expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default;
|
||||
expected_delete_assign_base& operator=(const expected_delete_assign_base&) = default;
|
||||
expected_delete_assign_base& operator=(expected_delete_assign_base&&) noexcept = delete;
|
||||
};
|
||||
|
||||
template <typename T, typename E>
|
||||
requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>,
|
||||
std::is_copy_assignable<E>, std::is_move_assignable<E>>
|
||||
struct expected_delete_assign_base<T, E, false, true> {
|
||||
expected_delete_assign_base() = default;
|
||||
expected_delete_assign_base(const expected_delete_assign_base&) = default;
|
||||
expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default;
|
||||
expected_delete_assign_base& operator=(const expected_delete_assign_base&) = delete;
|
||||
expected_delete_assign_base& operator=(expected_delete_assign_base&&) noexcept = default;
|
||||
};
|
||||
|
||||
template <typename T, typename E>
|
||||
requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>,
|
||||
std::is_copy_assignable<E>, std::is_move_assignable<E>>
|
||||
struct expected_delete_assign_base<T, E, false, false> {
|
||||
expected_delete_assign_base() = default;
|
||||
expected_delete_assign_base(const expected_delete_assign_base&) = default;
|
||||
expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default;
|
||||
expected_delete_assign_base& operator=(const expected_delete_assign_base&) = delete;
|
||||
expected_delete_assign_base& operator=(expected_delete_assign_base&&) noexcept = delete;
|
||||
};
|
||||
|
||||
/**
|
||||
* This is needed to be able to construct the expected_default_ctor_base which follows,
|
||||
* while still conditionally deleting the default constructor.
|
||||
*/
|
||||
struct default_constructor_tag {
|
||||
constexpr explicit default_constructor_tag() = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* expected_default_ctor_base will ensure that expected
|
||||
* has a deleted default constructor if T is not default constructible
|
||||
* This specialization is for when T is default constructible
|
||||
*/
|
||||
template <typename T, typename E, bool Enable = std::is_default_constructible_v<T>>
|
||||
struct expected_default_ctor_base {
|
||||
constexpr expected_default_ctor_base() noexcept = default;
|
||||
constexpr expected_default_ctor_base(expected_default_ctor_base const&) noexcept = default;
|
||||
constexpr expected_default_ctor_base(expected_default_ctor_base&&) noexcept = default;
|
||||
expected_default_ctor_base& operator=(expected_default_ctor_base const&) noexcept = default;
|
||||
expected_default_ctor_base& operator=(expected_default_ctor_base&&) noexcept = default;
|
||||
|
||||
constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
|
||||
};
|
||||
|
||||
template <typename T, typename E>
|
||||
struct expected_default_ctor_base<T, E, false> {
|
||||
constexpr expected_default_ctor_base() noexcept = delete;
|
||||
constexpr expected_default_ctor_base(expected_default_ctor_base const&) noexcept = default;
|
||||
constexpr expected_default_ctor_base(expected_default_ctor_base&&) noexcept = default;
|
||||
expected_default_ctor_base& operator=(expected_default_ctor_base const&) noexcept = default;
|
||||
expected_default_ctor_base& operator=(expected_default_ctor_base&&) noexcept = default;
|
||||
|
||||
constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
|
||||
};
|
||||
|
||||
template <typename T, typename E, typename U>
|
||||
using expected_enable_forward_value =
|
||||
std::enable_if_t<std::is_constructible_v<T, U&&> &&
|
||||
!std::is_same_v<std::remove_cvref_t<U>, std::in_place_t> &&
|
||||
!std::is_same_v<Expected<T, E>, std::remove_cvref_t<U>> &&
|
||||
!std::is_same_v<Unexpected<E>, std::remove_cvref_t<U>>>;
|
||||
|
||||
template <typename T, typename E, typename U, typename G, typename UR, typename GR>
|
||||
using expected_enable_from_other = std::enable_if_t<
|
||||
std::is_constructible_v<T, UR> && std::is_constructible_v<E, GR> &&
|
||||
!std::is_constructible_v<T, Expected<U, G>&> && !std::is_constructible_v<T, Expected<U, G>&&> &&
|
||||
!std::is_constructible_v<T, const Expected<U, G>&> &&
|
||||
!std::is_constructible_v<T, const Expected<U, G>&&> &&
|
||||
!std::is_convertible_v<Expected<U, G>&, T> && !std::is_convertible_v<Expected<U, G>&&, T> &&
|
||||
!std::is_convertible_v<const Expected<U, G>&, T> &&
|
||||
!std::is_convertible_v<const Expected<U, G>&&, T>>;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename T, typename E>
|
||||
class Expected : private detail::expected_move_assign_base<T, E>,
|
||||
private detail::expected_delete_ctor_base<T, E>,
|
||||
private detail::expected_delete_assign_base<T, E>,
|
||||
private detail::expected_default_ctor_base<T, E> {
|
||||
public:
|
||||
using value_type = T;
|
||||
using error_type = E;
|
||||
using unexpected_type = Unexpected<E>;
|
||||
|
||||
constexpr Expected() = default;
|
||||
constexpr Expected(const Expected&) = default;
|
||||
constexpr Expected(Expected&&) = default;
|
||||
Expected& operator=(const Expected&) = default;
|
||||
Expected& operator=(Expected&&) = default;
|
||||
|
||||
template <typename... Args, std::enable_if_t<std::is_constructible_v<T, Args&&...>>* = nullptr>
|
||||
constexpr Expected(std::in_place_t, Args&&... args)
|
||||
: impl_base{std::in_place, std::forward<Args>(args)...},
|
||||
ctor_base{detail::default_constructor_tag{}} {}
|
||||
|
||||
template <typename U, typename... Args,
|
||||
std::enable_if_t<std::is_constructible_v<T, std::initializer_list<U>&, Args&&...>>* =
|
||||
nullptr>
|
||||
constexpr Expected(std::in_place_t, std::initializer_list<U> il, Args&&... args)
|
||||
: impl_base{std::in_place, il, std::forward<Args>(args)...},
|
||||
ctor_base{detail::default_constructor_tag{}} {}
|
||||
|
||||
template <typename G = E, std::enable_if_t<std::is_constructible_v<E, const G&>>* = nullptr,
|
||||
std::enable_if_t<!std::is_convertible_v<const G&, E>>* = nullptr>
|
||||
constexpr explicit Expected(const Unexpected<G>& e)
|
||||
: impl_base{unexpect_t{}, e.value()}, ctor_base{detail::default_constructor_tag{}} {}
|
||||
|
||||
template <typename G = E, std::enable_if_t<std::is_constructible_v<E, const G&>>* = nullptr,
|
||||
std::enable_if_t<std::is_convertible_v<const G&, E>>* = nullptr>
|
||||
constexpr Expected(Unexpected<G> const& e)
|
||||
: impl_base{unexpect_t{}, e.value()}, ctor_base{detail::default_constructor_tag{}} {}
|
||||
|
||||
template <typename G = E, std::enable_if_t<std::is_constructible_v<E, G&&>>* = nullptr,
|
||||
std::enable_if_t<!std::is_convertible_v<G&&, E>>* = nullptr>
|
||||
constexpr explicit Expected(Unexpected<G>&& e) noexcept(std::is_nothrow_constructible_v<E, G&&>)
|
||||
: impl_base{unexpect_t{}, std::move(e.value())}, ctor_base{
|
||||
detail::default_constructor_tag{}} {}
|
||||
|
||||
template <typename G = E, std::enable_if_t<std::is_constructible_v<E, G&&>>* = nullptr,
|
||||
std::enable_if_t<std::is_convertible_v<G&&, E>>* = nullptr>
|
||||
constexpr Expected(Unexpected<G>&& e) noexcept(std::is_nothrow_constructible_v<E, G&&>)
|
||||
: impl_base{unexpect_t{}, std::move(e.value())}, ctor_base{
|
||||
detail::default_constructor_tag{}} {}
|
||||
|
||||
template <typename... Args, std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
|
||||
constexpr explicit Expected(unexpect_t, Args&&... args)
|
||||
: impl_base{unexpect_t{}, std::forward<Args>(args)...},
|
||||
ctor_base{detail::default_constructor_tag{}} {}
|
||||
|
||||
template <typename U, typename... Args,
|
||||
std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&, Args&&...>>* =
|
||||
nullptr>
|
||||
constexpr explicit Expected(unexpect_t, std::initializer_list<U> il, Args&&... args)
|
||||
: impl_base{unexpect_t{}, il, std::forward<Args>(args)...},
|
||||
ctor_base{detail::default_constructor_tag{}} {}
|
||||
|
||||
template <typename U, typename G,
|
||||
std::enable_if_t<!(std::is_convertible_v<U const&, T> &&
|
||||
std::is_convertible_v<G const&, E>)>* = nullptr,
|
||||
detail::expected_enable_from_other<T, E, U, G, const U&, const G&>* = nullptr>
|
||||
constexpr explicit Expected(const Expected<U, G>& rhs)
|
||||
: ctor_base{detail::default_constructor_tag{}} {
|
||||
if (rhs.has_value()) {
|
||||
this->construct(*rhs);
|
||||
} else {
|
||||
this->construct_error(rhs.error());
|
||||
}
|
||||
}
|
||||
|
||||
template <typename U, typename G,
|
||||
std::enable_if_t<(std::is_convertible_v<U const&, T> &&
|
||||
std::is_convertible_v<G const&, E>)>* = nullptr,
|
||||
detail::expected_enable_from_other<T, E, U, G, const U&, const G&>* = nullptr>
|
||||
constexpr Expected(const Expected<U, G>& rhs) : ctor_base{detail::default_constructor_tag{}} {
|
||||
if (rhs.has_value()) {
|
||||
this->construct(*rhs);
|
||||
} else {
|
||||
this->construct_error(rhs.error());
|
||||
}
|
||||
}
|
||||
|
||||
template <typename U, typename G,
|
||||
std::enable_if_t<!(std::is_convertible_v<U&&, T> && std::is_convertible_v<G&&, E>)>* =
|
||||
nullptr,
|
||||
detail::expected_enable_from_other<T, E, U, G, U&&, G&&>* = nullptr>
|
||||
constexpr explicit Expected(Expected<U, G>&& rhs)
|
||||
: ctor_base{detail::default_constructor_tag{}} {
|
||||
if (rhs.has_value()) {
|
||||
this->construct(std::move(*rhs));
|
||||
} else {
|
||||
this->construct_error(std::move(rhs.error()));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename U, typename G,
|
||||
std::enable_if_t<(std::is_convertible_v<U&&, T> && std::is_convertible_v<G&&, E>)>* =
|
||||
nullptr,
|
||||
detail::expected_enable_from_other<T, E, U, G, U&&, G&&>* = nullptr>
|
||||
constexpr Expected(Expected<U, G>&& rhs) : ctor_base{detail::default_constructor_tag{}} {
|
||||
if (rhs.has_value()) {
|
||||
this->construct(std::move(*rhs));
|
||||
} else {
|
||||
this->construct_error(std::move(rhs.error()));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename U = T, std::enable_if_t<!std::is_convertible_v<U&&, T>>* = nullptr,
|
||||
detail::expected_enable_forward_value<T, E, U>* = nullptr>
|
||||
constexpr explicit Expected(U&& v) : Expected{std::in_place, std::forward<U>(v)} {}
|
||||
|
||||
template <typename U = T, std::enable_if_t<std::is_convertible_v<U&&, T>>* = nullptr,
|
||||
detail::expected_enable_forward_value<T, E, U>* = nullptr>
|
||||
constexpr Expected(U&& v) : Expected{std::in_place, std::forward<U>(v)} {}
|
||||
|
||||
template <typename U = T, typename G = T,
|
||||
std::enable_if_t<std::is_nothrow_constructible_v<T, U&&>>* = nullptr,
|
||||
std::enable_if_t<(
|
||||
!std::is_same_v<Expected<T, E>, std::remove_cvref_t<U>> &&
|
||||
!std::conjunction_v<std::is_scalar<T>, std::is_same<T, std::remove_cvref_t<U>>> &&
|
||||
std::is_constructible_v<T, U> && std::is_assignable_v<G&, U> &&
|
||||
std::is_nothrow_move_constructible_v<E>)>* = nullptr>
|
||||
Expected& operator=(U&& v) {
|
||||
if (has_value()) {
|
||||
val() = std::forward<U>(v);
|
||||
} else {
|
||||
err().~Unexpected<E>();
|
||||
new (valptr()) T{std::forward<U>(v)};
|
||||
this->m_has_val = true;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename U = T, typename G = T,
|
||||
std::enable_if_t<!std::is_nothrow_constructible_v<T, U&&>>* = nullptr,
|
||||
std::enable_if_t<(
|
||||
!std::is_same_v<Expected<T, E>, std::remove_cvref_t<U>> &&
|
||||
!std::conjunction_v<std::is_scalar<T>, std::is_same<T, std::remove_cvref_t<U>>> &&
|
||||
std::is_constructible_v<T, U> && std::is_assignable_v<G&, U> &&
|
||||
std::is_nothrow_move_constructible_v<E>)>* = nullptr>
|
||||
Expected& operator=(U&& v) {
|
||||
if (has_value()) {
|
||||
val() = std::forward<U>(v);
|
||||
} else {
|
||||
auto tmp = std::move(err());
|
||||
err().~Unexpected<E>();
|
||||
new (valptr()) T{std::forward<U>(v)};
|
||||
this->m_has_val = true;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename G = E, std::enable_if_t<std::is_nothrow_copy_constructible_v<G> &&
|
||||
std::is_assignable_v<G&, G>>* = nullptr>
|
||||
Expected& operator=(const Unexpected<G>& rhs) {
|
||||
if (!has_value()) {
|
||||
err() = rhs;
|
||||
} else {
|
||||
this->destroy_val();
|
||||
new (errptr()) Unexpected<E>{rhs};
|
||||
this->m_has_val = false;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename G = E, std::enable_if_t<std::is_nothrow_move_constructible_v<G> &&
|
||||
std::is_move_assignable_v<G>>* = nullptr>
|
||||
Expected& operator=(Unexpected<G>&& rhs) noexcept {
|
||||
if (!has_value()) {
|
||||
err() = std::move(rhs);
|
||||
} else {
|
||||
this->destroy_val();
|
||||
new (errptr()) Unexpected<E>{std::move(rhs)};
|
||||
this->m_has_val = false;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename... Args,
|
||||
std::enable_if_t<std::is_nothrow_constructible_v<T, Args&&...>>* = nullptr>
|
||||
void emplace(Args&&... args) {
|
||||
if (has_value()) {
|
||||
val() = T{std::forward<Args>(args)...};
|
||||
} else {
|
||||
err().~Unexpected<E>();
|
||||
new (valptr()) T{std::forward<Args>(args)...};
|
||||
this->m_has_val = true;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... Args,
|
||||
std::enable_if_t<!std::is_nothrow_constructible_v<T, Args&&...>>* = nullptr>
|
||||
void emplace(Args&&... args) {
|
||||
if (has_value()) {
|
||||
val() = T{std::forward<Args>(args)...};
|
||||
} else {
|
||||
auto tmp = std::move(err());
|
||||
err().~Unexpected<E>();
|
||||
new (valptr()) T{std::forward<Args>(args)...};
|
||||
this->m_has_val = true;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename U, typename... Args,
|
||||
std::enable_if_t<std::is_nothrow_constructible_v<T, std::initializer_list<U>&,
|
||||
Args&&...>>* = nullptr>
|
||||
void emplace(std::initializer_list<U> il, Args&&... args) {
|
||||
if (has_value()) {
|
||||
T t{il, std::forward<Args>(args)...};
|
||||
val() = std::move(t);
|
||||
} else {
|
||||
err().~Unexpected<E>();
|
||||
new (valptr()) T{il, std::forward<Args>(args)...};
|
||||
this->m_has_val = true;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename U, typename... Args,
|
||||
std::enable_if_t<!std::is_nothrow_constructible_v<T, std::initializer_list<U>&,
|
||||
Args&&...>>* = nullptr>
|
||||
void emplace(std::initializer_list<U> il, Args&&... args) {
|
||||
if (has_value()) {
|
||||
T t{il, std::forward<Args>(args)...};
|
||||
val() = std::move(t);
|
||||
} else {
|
||||
auto tmp = std::move(err());
|
||||
err().~Unexpected<E>();
|
||||
new (valptr()) T{il, std::forward<Args>(args)...};
|
||||
this->m_has_val = true;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr T* operator->() {
|
||||
return valptr();
|
||||
}
|
||||
|
||||
constexpr const T* operator->() const {
|
||||
return valptr();
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
constexpr U& operator*() & {
|
||||
return val();
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
constexpr const U& operator*() const& {
|
||||
return val();
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
constexpr U&& operator*() && {
|
||||
return std::move(val());
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
constexpr const U&& operator*() const&& {
|
||||
return std::move(val());
|
||||
}
|
||||
|
||||
constexpr bool has_value() const noexcept {
|
||||
return this->m_has_val;
|
||||
}
|
||||
|
||||
constexpr explicit operator bool() const noexcept {
|
||||
return this->m_has_val;
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
constexpr U& value() & {
|
||||
return val();
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
constexpr const U& value() const& {
|
||||
return val();
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
constexpr U&& value() && {
|
||||
return std::move(val());
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
constexpr const U&& value() const&& {
|
||||
return std::move(val());
|
||||
}
|
||||
|
||||
constexpr E& error() & {
|
||||
return err().value();
|
||||
}
|
||||
|
||||
constexpr const E& error() const& {
|
||||
return err().value();
|
||||
}
|
||||
|
||||
constexpr E&& error() && {
|
||||
return std::move(err().value());
|
||||
}
|
||||
|
||||
constexpr const E&& error() const&& {
|
||||
return std::move(err().value());
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
constexpr T value_or(U&& v) const& {
|
||||
static_assert(std::is_copy_constructible_v<T> && std::is_convertible_v<U&&, T>,
|
||||
"T must be copy-constructible and convertible from U&&");
|
||||
return bool(*this) ? **this : static_cast<T>(std::forward<U>(v));
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
constexpr T value_or(U&& v) && {
|
||||
static_assert(std::is_move_constructible_v<T> && std::is_convertible_v<U&&, T>,
|
||||
"T must be move-constructible and convertible from U&&");
|
||||
return bool(*this) ? std::move(**this) : static_cast<T>(std::forward<U>(v));
|
||||
}
|
||||
|
||||
private:
|
||||
static_assert(!std::is_reference_v<T>, "T must not be a reference");
|
||||
static_assert(!std::is_same_v<T, std::remove_cv_t<std::in_place_t>>,
|
||||
"T must not be std::in_place_t");
|
||||
static_assert(!std::is_same_v<T, std::remove_cv_t<unexpect_t>>, "T must not be unexpect_t");
|
||||
static_assert(!std::is_same_v<T, std::remove_cv_t<Unexpected<E>>>,
|
||||
"T must not be Unexpected<E>");
|
||||
static_assert(!std::is_reference_v<E>, "E must not be a reference");
|
||||
|
||||
T* valptr() {
|
||||
return std::addressof(this->m_val);
|
||||
}
|
||||
|
||||
const T* valptr() const {
|
||||
return std::addressof(this->m_val);
|
||||
}
|
||||
|
||||
Unexpected<E>* errptr() {
|
||||
return std::addressof(this->m_unexpect);
|
||||
}
|
||||
|
||||
const Unexpected<E>* errptr() const {
|
||||
return std::addressof(this->m_unexpect);
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
constexpr U& val() {
|
||||
return this->m_val;
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
constexpr const U& val() const {
|
||||
return this->m_val;
|
||||
}
|
||||
|
||||
constexpr Unexpected<E>& err() {
|
||||
return this->m_unexpect;
|
||||
}
|
||||
|
||||
constexpr const Unexpected<E>& err() const {
|
||||
return this->m_unexpect;
|
||||
}
|
||||
|
||||
using impl_base = detail::expected_move_assign_base<T, E>;
|
||||
using ctor_base = detail::expected_default_ctor_base<T, E>;
|
||||
};
|
||||
|
||||
template <typename T, typename E, typename U, typename F>
|
||||
constexpr bool operator==(const Expected<T, E>& lhs, const Expected<U, F>& rhs) {
|
||||
return (lhs.has_value() != rhs.has_value())
|
||||
? false
|
||||
: (!lhs.has_value() ? lhs.error() == rhs.error() : *lhs == *rhs);
|
||||
}
|
||||
|
||||
template <typename T, typename E, typename U, typename F>
|
||||
constexpr bool operator!=(const Expected<T, E>& lhs, const Expected<U, F>& rhs) {
|
||||
return !operator==(lhs, rhs);
|
||||
}
|
||||
|
||||
template <typename T, typename E, typename U>
|
||||
constexpr bool operator==(const Expected<T, E>& x, const U& v) {
|
||||
return x.has_value() ? *x == v : false;
|
||||
}
|
||||
|
||||
template <typename T, typename E, typename U>
|
||||
constexpr bool operator==(const U& v, const Expected<T, E>& x) {
|
||||
return x.has_value() ? *x == v : false;
|
||||
}
|
||||
|
||||
template <typename T, typename E, typename U>
|
||||
constexpr bool operator!=(const Expected<T, E>& x, const U& v) {
|
||||
return !operator==(x, v);
|
||||
}
|
||||
|
||||
template <typename T, typename E, typename U>
|
||||
constexpr bool operator!=(const U& v, const Expected<T, E>& x) {
|
||||
return !operator==(v, x);
|
||||
}
|
||||
|
||||
template <typename T, typename E>
|
||||
constexpr bool operator==(const Expected<T, E>& x, const Unexpected<E>& e) {
|
||||
return x.has_value() ? false : x.error() == e.value();
|
||||
}
|
||||
|
||||
template <typename T, typename E>
|
||||
constexpr bool operator==(const Unexpected<E>& e, const Expected<T, E>& x) {
|
||||
return x.has_value() ? false : x.error() == e.value();
|
||||
}
|
||||
|
||||
template <typename T, typename E>
|
||||
constexpr bool operator!=(const Expected<T, E>& x, const Unexpected<E>& e) {
|
||||
return !operator==(x, e);
|
||||
}
|
||||
|
||||
template <typename T, typename E>
|
||||
constexpr bool operator!=(const Unexpected<E>& e, const Expected<T, E>& x) {
|
||||
return !operator==(e, x);
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
+10
-1
@@ -54,7 +54,6 @@ SWITCHABLE(CpuBackend, true);
|
||||
SWITCHABLE(CpuAccuracy, true);
|
||||
SWITCHABLE(FullscreenMode, true);
|
||||
SWITCHABLE(GpuAccuracy, true);
|
||||
SWITCHABLE(GpuLogLevel, true);
|
||||
SWITCHABLE(Language, true);
|
||||
SWITCHABLE(MemoryLayout, true);
|
||||
SWITCHABLE(NvdecEmulation, false);
|
||||
@@ -213,6 +212,16 @@ bool IsNceEnabled() {
|
||||
return is_nce_enabled;
|
||||
}
|
||||
|
||||
static u64 current_program_id = 0;
|
||||
|
||||
void SetCurrentProgramID(u64 program_id) {
|
||||
current_program_id = program_id;
|
||||
}
|
||||
|
||||
u64 GetCurrentProgramID() {
|
||||
return current_program_id;
|
||||
}
|
||||
|
||||
bool IsDockedMode() {
|
||||
return values.use_docked_mode.GetValue() == Settings::ConsoleMode::Docked;
|
||||
}
|
||||
|
||||
+14
-5
@@ -573,6 +573,13 @@ struct Values {
|
||||
false,
|
||||
#endif
|
||||
"rescale_hack", Category::RendererHacks};
|
||||
SwitchableSetting<bool> enable_gpu_buffer_readback{linkage,
|
||||
false,
|
||||
"enable_gpu_buffer_readback",
|
||||
Category::RendererAdvanced,
|
||||
Specialization::Default,
|
||||
true,
|
||||
true};
|
||||
|
||||
SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders",
|
||||
Category::RendererHacks};
|
||||
@@ -788,8 +795,8 @@ struct Values {
|
||||
false}; // runtime_modifiable_ — startup-only
|
||||
Setting<bool> dump_exefs{linkage, false, "dump_exefs", Category::Debugging};
|
||||
Setting<bool> dump_nso{linkage, false, "dump_nso", Category::Debugging};
|
||||
Setting<bool> dump_shaders{
|
||||
linkage, false, "dump_shaders", Category::DebuggingGraphics, Specialization::Default,
|
||||
Setting<bool> dump_guest_shaders{
|
||||
linkage, false, "dump_guest_shaders", Category::DebuggingGraphics, Specialization::Default,
|
||||
false};
|
||||
Setting<bool> dump_macros{
|
||||
linkage, false, "dump_macros", Category::DebuggingGraphics, Specialization::Default, false};
|
||||
@@ -813,9 +820,8 @@ struct Values {
|
||||
Setting<bool> disable_web_applet{linkage, true, "disable_web_applet", Category::Debugging};
|
||||
|
||||
// GPU Logging
|
||||
Setting<bool> gpu_logging_enabled{linkage, false, "gpu_logging_enabled", Category::Debugging};
|
||||
SwitchableSetting<GpuLogLevel> gpu_log_level{linkage, GpuLogLevel::Standard, "gpu_log_level",
|
||||
Category::Debugging};
|
||||
Setting<GpuLogLevel> gpu_log_level{linkage, GpuLogLevel::Off, "gpu_log_level",
|
||||
Category::Debugging};
|
||||
Setting<bool> gpu_log_vulkan_calls{linkage, true, "gpu_log_vulkan_calls", Category::Debugging};
|
||||
Setting<bool> gpu_log_shader_dumps{linkage, false, "gpu_log_shader_dumps", Category::Debugging};
|
||||
Setting<bool> gpu_log_memory_tracking{linkage, true, "gpu_log_memory_tracking",
|
||||
@@ -876,6 +882,9 @@ bool IsFastmemEnabled();
|
||||
void SetNceEnabled(bool is_64bit);
|
||||
bool IsNceEnabled();
|
||||
|
||||
void SetCurrentProgramID(u64 program_id);
|
||||
u64 GetCurrentProgramID();
|
||||
|
||||
bool IsOpenGL();
|
||||
|
||||
bool IsDockedMode();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
@@ -17,7 +17,11 @@ namespace Common {
|
||||
|
||||
void* AllocateMemoryPages(std::size_t size) noexcept {
|
||||
#ifdef _WIN32
|
||||
void* base = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE);
|
||||
void* base = VirtualAlloc(nullptr, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||
if (base == nullptr) {
|
||||
// Probably failing to reserve is less likely than failing to commit
|
||||
base = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE);
|
||||
}
|
||||
#else
|
||||
void* base = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
if (base == MAP_FAILED)
|
||||
|
||||
+9
-8
@@ -108,7 +108,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
|
||||
|
||||
struct System::Impl {
|
||||
explicit Impl(System& system)
|
||||
: kernel{system}, fs_controller{system}, hid_core{}, cpu_manager{system},
|
||||
: kernel{system}, fs_controller{system}, hid_core{system.Kernel()}, cpu_manager{system},
|
||||
reporter{system}, applet_manager{system}, frontend_applets{system}, profile_manager{} {}
|
||||
|
||||
u64 program_id;
|
||||
@@ -271,7 +271,7 @@ struct System::Impl {
|
||||
|
||||
SystemResultStatus SetupForApplicationProcess(System& system, Frontend::EmuWindow& emu_window) {
|
||||
host1x_core.emplace(system);
|
||||
gpu_core = VideoCore::CreateGPU(emu_window, system);
|
||||
VideoCore::CreateGPU(gpu_core, emu_window, system);
|
||||
if (!gpu_core)
|
||||
return SystemResultStatus::ErrorVideoCore;
|
||||
|
||||
@@ -326,6 +326,9 @@ struct System::Impl {
|
||||
|
||||
LOG_INFO(Core, "Loading {} ({:016X}) ...", name, params.program_id);
|
||||
|
||||
// Expose program id to dump sites and other global readers.
|
||||
Settings::SetCurrentProgramID(params.program_id);
|
||||
|
||||
// Track launch time for frontend launches
|
||||
LaunchTimestampCache::SaveLaunchTimestamp(params.program_id);
|
||||
|
||||
@@ -391,10 +394,8 @@ struct System::Impl {
|
||||
is_powered_on = false;
|
||||
exit_locked = false;
|
||||
exit_requested = false;
|
||||
|
||||
if (gpu_core != nullptr) {
|
||||
if (gpu_core)
|
||||
gpu_core->NotifyShutdown();
|
||||
}
|
||||
|
||||
stop_event.request_stop();
|
||||
core_timing.SyncPause(false);
|
||||
@@ -478,6 +479,7 @@ struct System::Impl {
|
||||
std::optional<Memory::CheatEngine> cheat_engine;
|
||||
std::optional<Tools::Freezer> memory_freezer;
|
||||
std::optional<Tools::RenderdocAPI> renderdoc_api;
|
||||
std::optional<Tegra::GPU> gpu_core;
|
||||
|
||||
std::array<Core::GPUDirtyMemoryManager, Core::Hardware::NUM_CPU_CORES> gpu_dirty_memory_managers;
|
||||
std::vector<std::vector<u8>> user_channel;
|
||||
@@ -492,7 +494,6 @@ struct System::Impl {
|
||||
std::unique_ptr<FileSys::ContentProviderUnion> content_provider;
|
||||
/// AppLoader used to load the current executing application
|
||||
std::unique_ptr<Loader::AppLoader> app_loader;
|
||||
std::unique_ptr<Tegra::GPU> gpu_core;
|
||||
std::stop_source stop_event;
|
||||
|
||||
mutable std::mutex suspend_guard;
|
||||
@@ -925,7 +926,7 @@ void System::PushGeneralChannelData(std::vector<u8>&& data) {
|
||||
const bool was_empty = impl->general_channel.empty();
|
||||
impl->general_channel.push_back(std::move(data));
|
||||
if (was_empty) {
|
||||
impl->general_channel_event->Signal();
|
||||
impl->general_channel_event->Signal(impl->kernel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -937,7 +938,7 @@ bool System::TryPopGeneralChannel(std::vector<u8>& out_data) {
|
||||
out_data = std::move(impl->general_channel.back());
|
||||
impl->general_channel.pop_back();
|
||||
if (impl->general_channel.empty()) {
|
||||
impl->general_channel_event->Clear();
|
||||
impl->general_channel_event->Clear(impl->kernel);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -438,7 +438,6 @@ public:
|
||||
/// Applies any changes to settings to this core instance.
|
||||
void ApplySettings();
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
};
|
||||
|
||||
+35
-50
@@ -25,9 +25,11 @@ CpuManager::~CpuManager() = default;
|
||||
|
||||
void CpuManager::Initialize() {
|
||||
num_cores = is_multicore ? Core::Hardware::NUM_CPU_CORES : 1;
|
||||
gpu_barrier = std::make_unique<Common::Barrier>(num_cores + 1);
|
||||
gpu_barrier.emplace(num_cores + 1);
|
||||
for (std::size_t core = 0; core < num_cores; core++)
|
||||
core_data[core].host_thread = std::jthread([this, core](std::stop_token token) { RunThread(token, core); });
|
||||
core_data[core].host_thread = std::jthread([this, core](std::stop_token token) {
|
||||
RunThread(token, core);
|
||||
});
|
||||
}
|
||||
|
||||
void CpuManager::Shutdown() {
|
||||
@@ -39,69 +41,61 @@ void CpuManager::Shutdown() {
|
||||
}
|
||||
}
|
||||
|
||||
void CpuManager::GuestThreadFunction() {
|
||||
void CpuManager::GuestThreadFunction(Kernel::KernelCore& kernel) {
|
||||
if (is_multicore) {
|
||||
MultiCoreRunGuestThread();
|
||||
MultiCoreRunGuestThread(kernel);
|
||||
} else {
|
||||
SingleCoreRunGuestThread();
|
||||
SingleCoreRunGuestThread(kernel);
|
||||
}
|
||||
}
|
||||
|
||||
void CpuManager::IdleThreadFunction() {
|
||||
void CpuManager::IdleThreadFunction(Kernel::KernelCore& kernel) {
|
||||
if (is_multicore) {
|
||||
MultiCoreRunIdleThread();
|
||||
MultiCoreRunIdleThread(kernel);
|
||||
} else {
|
||||
SingleCoreRunIdleThread();
|
||||
SingleCoreRunIdleThread(kernel);
|
||||
}
|
||||
}
|
||||
|
||||
void CpuManager::ShutdownThreadFunction() {
|
||||
ShutdownThread();
|
||||
void CpuManager::ShutdownThreadFunction(Kernel::KernelCore& kernel) {
|
||||
ShutdownThread(kernel);
|
||||
}
|
||||
|
||||
void CpuManager::HandleInterrupt() {
|
||||
auto& kernel = system.Kernel();
|
||||
void CpuManager::HandleInterrupt(Kernel::KernelCore& kernel) {
|
||||
auto core_index = kernel.CurrentPhysicalCoreIndex();
|
||||
|
||||
Kernel::KInterruptManager::HandleInterrupt(kernel, static_cast<s32>(core_index));
|
||||
Kernel::KInterruptManager::HandleInterrupt(kernel, s32(core_index));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// MultiCore ///
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void CpuManager::MultiCoreRunGuestThread() {
|
||||
void CpuManager::MultiCoreRunGuestThread(Kernel::KernelCore& kernel) {
|
||||
// Similar to UserModeThreadStarter in HOS
|
||||
auto& kernel = system.Kernel();
|
||||
auto* thread = Kernel::GetCurrentThreadPointer(kernel);
|
||||
kernel.CurrentScheduler()->OnThreadStart();
|
||||
kernel.CurrentScheduler()->OnThreadStart(kernel);
|
||||
|
||||
while (true) {
|
||||
auto* physical_core = &kernel.CurrentPhysicalCore();
|
||||
while (!physical_core->IsInterrupted()) {
|
||||
physical_core->RunThread(thread);
|
||||
physical_core->RunThread(kernel, thread);
|
||||
physical_core = &kernel.CurrentPhysicalCore();
|
||||
}
|
||||
|
||||
HandleInterrupt();
|
||||
HandleInterrupt(kernel);
|
||||
}
|
||||
}
|
||||
|
||||
void CpuManager::MultiCoreRunIdleThread() {
|
||||
void CpuManager::MultiCoreRunIdleThread(Kernel::KernelCore& kernel) {
|
||||
// Not accurate to HOS. Remove this entire method when singlecore is removed.
|
||||
// See notes in KScheduler::ScheduleImpl for more information about why this
|
||||
// is inaccurate.
|
||||
|
||||
auto& kernel = system.Kernel();
|
||||
kernel.CurrentScheduler()->OnThreadStart();
|
||||
|
||||
kernel.CurrentScheduler()->OnThreadStart(kernel);
|
||||
while (true) {
|
||||
auto& physical_core = kernel.CurrentPhysicalCore();
|
||||
if (!physical_core.IsInterrupted()) {
|
||||
physical_core.Idle();
|
||||
}
|
||||
|
||||
HandleInterrupt();
|
||||
HandleInterrupt(kernel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,15 +103,14 @@ void CpuManager::MultiCoreRunIdleThread() {
|
||||
/// SingleCore ///
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void CpuManager::SingleCoreRunGuestThread() {
|
||||
auto& kernel = system.Kernel();
|
||||
void CpuManager::SingleCoreRunGuestThread(Kernel::KernelCore& kernel) {
|
||||
auto* thread = Kernel::GetCurrentThreadPointer(kernel);
|
||||
kernel.CurrentScheduler()->OnThreadStart();
|
||||
kernel.CurrentScheduler()->OnThreadStart(kernel);
|
||||
|
||||
while (true) {
|
||||
auto* physical_core = &kernel.CurrentPhysicalCore();
|
||||
if (!physical_core->IsInterrupted()) {
|
||||
physical_core->RunThread(thread);
|
||||
physical_core->RunThread(kernel, thread);
|
||||
physical_core = &kernel.CurrentPhysicalCore();
|
||||
}
|
||||
|
||||
@@ -125,26 +118,22 @@ void CpuManager::SingleCoreRunGuestThread() {
|
||||
system.CoreTiming().Advance();
|
||||
kernel.SetIsPhantomModeForSingleCore(false);
|
||||
|
||||
PreemptSingleCore();
|
||||
HandleInterrupt();
|
||||
PreemptSingleCore(kernel);
|
||||
HandleInterrupt(kernel);
|
||||
}
|
||||
}
|
||||
|
||||
void CpuManager::SingleCoreRunIdleThread() {
|
||||
auto& kernel = system.Kernel();
|
||||
kernel.CurrentScheduler()->OnThreadStart();
|
||||
|
||||
void CpuManager::SingleCoreRunIdleThread(Kernel::KernelCore& kernel) {
|
||||
kernel.CurrentScheduler()->OnThreadStart(kernel);
|
||||
while (true) {
|
||||
PreemptSingleCore(false);
|
||||
PreemptSingleCore(kernel, false);
|
||||
system.CoreTiming().AddTicks(1000U);
|
||||
idle_count++;
|
||||
HandleInterrupt();
|
||||
HandleInterrupt(kernel);
|
||||
}
|
||||
}
|
||||
|
||||
void CpuManager::PreemptSingleCore(bool from_running_environment) {
|
||||
auto& kernel = system.Kernel();
|
||||
|
||||
void CpuManager::PreemptSingleCore(Kernel::KernelCore& kernel, bool from_running_environment) {
|
||||
if (idle_count >= 4 || from_running_environment) {
|
||||
if (!from_running_environment) {
|
||||
system.CoreTiming().Idle();
|
||||
@@ -156,7 +145,7 @@ void CpuManager::PreemptSingleCore(bool from_running_environment) {
|
||||
}
|
||||
current_core.store((current_core + 1) % Core::Hardware::NUM_CPU_CORES);
|
||||
system.CoreTiming().ResetTicks();
|
||||
kernel.Scheduler(current_core).PreemptSingleCore();
|
||||
kernel.Scheduler(current_core).PreemptSingleCore(kernel);
|
||||
|
||||
// We've now been scheduled again, and we may have exchanged schedulers.
|
||||
// Reload the scheduler in case it's different.
|
||||
@@ -165,20 +154,16 @@ void CpuManager::PreemptSingleCore(bool from_running_environment) {
|
||||
}
|
||||
}
|
||||
|
||||
void CpuManager::GuestActivate() {
|
||||
void CpuManager::GuestActivate(Kernel::KernelCore& kernel) {
|
||||
// Similar to the HorizonKernelMain callback in HOS
|
||||
auto& kernel = system.Kernel();
|
||||
auto* scheduler = kernel.CurrentScheduler();
|
||||
|
||||
scheduler->Activate();
|
||||
scheduler->Activate(kernel);
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void CpuManager::ShutdownThread() {
|
||||
auto& kernel = system.Kernel();
|
||||
void CpuManager::ShutdownThread(Kernel::KernelCore& kernel) {
|
||||
auto* thread = kernel.GetCurrentEmuThread();
|
||||
auto core = is_multicore ? kernel.CurrentPhysicalCoreIndex() : 0;
|
||||
|
||||
Common::Fiber::YieldTo(thread->GetHostContext(), *core_data[core].host_context);
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
+32
-27
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -14,6 +17,10 @@
|
||||
#include "common/thread.h"
|
||||
#include "core/hardware_properties.h"
|
||||
|
||||
namespace Kernel {
|
||||
class KernelCore;
|
||||
}
|
||||
|
||||
namespace Common {
|
||||
class Event;
|
||||
class Fiber;
|
||||
@@ -51,57 +58,55 @@ public:
|
||||
void Initialize();
|
||||
void Shutdown();
|
||||
|
||||
std::function<void()> GetGuestActivateFunc() {
|
||||
return [this] { GuestActivate(); };
|
||||
std::function<void()> GetGuestActivateFunc(Kernel::KernelCore& kernel) {
|
||||
return [this, &kernel] { GuestActivate(kernel); };
|
||||
}
|
||||
std::function<void()> GetGuestThreadFunc() {
|
||||
return [this] { GuestThreadFunction(); };
|
||||
std::function<void()> GetGuestThreadFunc(Kernel::KernelCore& kernel) {
|
||||
return [this, &kernel] { GuestThreadFunction(kernel); };
|
||||
}
|
||||
std::function<void()> GetIdleThreadStartFunc() {
|
||||
return [this] { IdleThreadFunction(); };
|
||||
std::function<void()> GetIdleThreadStartFunc(Kernel::KernelCore& kernel) {
|
||||
return [this, &kernel] { IdleThreadFunction(kernel); };
|
||||
}
|
||||
std::function<void()> GetShutdownThreadStartFunc() {
|
||||
return [this] { ShutdownThreadFunction(); };
|
||||
std::function<void()> GetShutdownThreadStartFunc(Kernel::KernelCore& kernel) {
|
||||
return [this, &kernel] { ShutdownThreadFunction(kernel); };
|
||||
}
|
||||
|
||||
void PreemptSingleCore(bool from_running_environment = true);
|
||||
void PreemptSingleCore(Kernel::KernelCore& kernel, bool from_running_environment = true);
|
||||
|
||||
std::size_t CurrentCore() const {
|
||||
return current_core.load();
|
||||
}
|
||||
|
||||
private:
|
||||
void GuestThreadFunction();
|
||||
void IdleThreadFunction();
|
||||
void ShutdownThreadFunction();
|
||||
void GuestThreadFunction(Kernel::KernelCore& kernel);
|
||||
void IdleThreadFunction(Kernel::KernelCore& kernel);
|
||||
void ShutdownThreadFunction(Kernel::KernelCore& kernel);
|
||||
|
||||
void MultiCoreRunGuestThread();
|
||||
void MultiCoreRunIdleThread();
|
||||
void MultiCoreRunGuestThread(Kernel::KernelCore& kernel);
|
||||
void MultiCoreRunIdleThread(Kernel::KernelCore& kernel);
|
||||
|
||||
void SingleCoreRunGuestThread();
|
||||
void SingleCoreRunIdleThread();
|
||||
void SingleCoreRunGuestThread(Kernel::KernelCore& kernel);
|
||||
void SingleCoreRunIdleThread(Kernel::KernelCore& kernel);
|
||||
|
||||
void GuestActivate();
|
||||
void HandleInterrupt();
|
||||
void ShutdownThread();
|
||||
void GuestActivate(Kernel::KernelCore& kernel);
|
||||
void HandleInterrupt(Kernel::KernelCore& kernel);
|
||||
void ShutdownThread(Kernel::KernelCore& kernel);
|
||||
void RunThread(std::stop_token stop_token, std::size_t core);
|
||||
|
||||
static constexpr std::size_t max_cycle_runs = 5;
|
||||
|
||||
std::optional<Common::Barrier> gpu_barrier{};
|
||||
struct CoreData {
|
||||
std::shared_ptr<Common::Fiber> host_context;
|
||||
std::jthread host_thread;
|
||||
};
|
||||
|
||||
std::unique_ptr<Common::Barrier> gpu_barrier{};
|
||||
std::array<CoreData, Core::Hardware::NUM_CPU_CORES> core_data{};
|
||||
|
||||
bool is_async_gpu{};
|
||||
bool is_multicore{};
|
||||
Core::System& system;
|
||||
std::atomic<std::size_t> current_core{};
|
||||
std::size_t idle_count{};
|
||||
std::size_t num_cores{};
|
||||
static constexpr std::size_t max_cycle_runs = 5;
|
||||
|
||||
System& system;
|
||||
bool is_async_gpu{};
|
||||
bool is_multicore{};
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -81,7 +81,10 @@ namespace Core {
|
||||
|
||||
class DebuggerImpl : public DebuggerBackend {
|
||||
public:
|
||||
explicit DebuggerImpl(Core::System& system_, u16 port) : system{system_} {
|
||||
explicit DebuggerImpl(Core::System& system_, u16 port)
|
||||
: system{system_}
|
||||
, debug_process{system_.Kernel()}
|
||||
{
|
||||
InitializeServer(port);
|
||||
}
|
||||
|
||||
@@ -121,7 +124,7 @@ public:
|
||||
}
|
||||
|
||||
void SetActiveThread(Kernel::KThread* thread) override {
|
||||
state->active_thread = thread;
|
||||
state->active_thread = {system.Kernel(), thread};
|
||||
}
|
||||
|
||||
Kernel::KThread* GetActiveThread() override {
|
||||
@@ -168,14 +171,7 @@ private:
|
||||
frontend = std::make_unique<GDBStub>(*this, system, debug_process.GetPointerUnsafe());
|
||||
|
||||
// Set the new state. This will tear down any existing state.
|
||||
state = ConnectionState{
|
||||
.client_socket{std::move(peer)},
|
||||
.signal_pipe{io_context},
|
||||
.info{},
|
||||
.active_thread{},
|
||||
.client_data{},
|
||||
.pipe_data{},
|
||||
};
|
||||
state.emplace(std::move(peer), io_context, system.Kernel());
|
||||
|
||||
// Set up the client signals for new data.
|
||||
AsyncReceiveInto(state->signal_pipe, state->pipe_data, [&](auto d) { PipeData(d); });
|
||||
@@ -204,7 +200,7 @@ private:
|
||||
PauseEmulation();
|
||||
|
||||
// Notify the client.
|
||||
state->active_thread = state->info.thread;
|
||||
state->active_thread = {system.Kernel(), state->info.thread};
|
||||
UpdateActiveThread();
|
||||
|
||||
if (state->info.type == SignalType::Watchpoint) {
|
||||
@@ -258,7 +254,7 @@ private:
|
||||
auto* gdb = static_cast<GDBStub*>(frontend.get());
|
||||
MarkResumed([this, threads = std::move(gdb->resume_threads)] {
|
||||
state->active_thread->SetStepState(Kernel::StepState::StepPending);
|
||||
state->active_thread->Resume(Kernel::SuspendType::Debug);
|
||||
state->active_thread->Resume(system.Kernel(), Kernel::SuspendType::Debug);
|
||||
ResumeThreads(threads, state->active_thread.GetPointerUnsafe());
|
||||
});
|
||||
break;
|
||||
@@ -281,7 +277,7 @@ private:
|
||||
|
||||
// Put all threads to sleep on next scheduler round.
|
||||
for (auto& thread : ThreadList()) {
|
||||
thread.RequestSuspend(Kernel::SuspendType::Debug);
|
||||
thread.RequestSuspend(system.Kernel(), Kernel::SuspendType::Debug);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,7 +292,7 @@ private:
|
||||
}
|
||||
|
||||
thread.SetStepState(Kernel::StepState::NotStepping);
|
||||
thread.Resume(Kernel::SuspendType::Debug);
|
||||
thread.Resume(system.Kernel(), Kernel::SuspendType::Debug);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,7 +308,7 @@ private:
|
||||
}
|
||||
|
||||
thread->SetStepState(Kernel::StepState::NotStepping);
|
||||
thread->Resume(Kernel::SuspendType::Debug);
|
||||
thread->Resume(system.Kernel(), Kernel::SuspendType::Debug);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -332,7 +328,7 @@ private:
|
||||
return;
|
||||
}
|
||||
}
|
||||
state->active_thread = std::addressof(threads.front());
|
||||
state->active_thread = {system.Kernel(), std::addressof(threads.front())};
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -354,13 +350,20 @@ private:
|
||||
std::mutex connection_lock;
|
||||
|
||||
struct ConnectionState {
|
||||
boost::asio::ip::tcp::socket client_socket;
|
||||
#ifdef USE_BOOST_v1
|
||||
boost::process::v1::async_pipe signal_pipe;
|
||||
using async_pipe = boost::process::v1::async_pipe;
|
||||
#else
|
||||
boost::process::async_pipe signal_pipe;
|
||||
using async_pipe = boost::process::async_pipe;
|
||||
#endif
|
||||
|
||||
ConnectionState(boost::asio::ip::tcp::socket&& client_socket_, async_pipe signal_pipe_, Kernel::KernelCore& kernel)
|
||||
: client_socket{std::move(client_socket_)}
|
||||
, signal_pipe{signal_pipe_}
|
||||
, active_thread{kernel, nullptr}
|
||||
{}
|
||||
|
||||
boost::asio::ip::tcp::socket client_socket;
|
||||
async_pipe signal_pipe;
|
||||
SignalInfo info;
|
||||
Kernel::KScopedAutoObject<Kernel::KThread> active_thread;
|
||||
std::array<u8, 4096> client_data;
|
||||
|
||||
@@ -323,13 +323,13 @@ void GDBStub::HandleBreakpointInsert(std::string_view command) {
|
||||
success = true;
|
||||
break;
|
||||
case BreakpointType::WriteWatch:
|
||||
success = debug_process->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::Write);
|
||||
success = debug_process->InsertWatchpoint(system.Kernel(), addr, size, Kernel::DebugWatchpointType::Write);
|
||||
break;
|
||||
case BreakpointType::ReadWatch:
|
||||
success = debug_process->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::Read);
|
||||
success = debug_process->InsertWatchpoint(system.Kernel(), addr, size, Kernel::DebugWatchpointType::Read);
|
||||
break;
|
||||
case BreakpointType::AccessWatch:
|
||||
success = debug_process->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::ReadOrWrite);
|
||||
success = debug_process->InsertWatchpoint(system.Kernel(), addr, size, Kernel::DebugWatchpointType::ReadOrWrite);
|
||||
break;
|
||||
case BreakpointType::Hardware:
|
||||
default:
|
||||
@@ -368,13 +368,13 @@ void GDBStub::HandleBreakpointRemove(std::string_view sv) {
|
||||
break;
|
||||
}
|
||||
case BreakpointType::WriteWatch:
|
||||
success = debug_process->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::Write);
|
||||
success = debug_process->RemoveWatchpoint(system.Kernel(), addr, size, Kernel::DebugWatchpointType::Write);
|
||||
break;
|
||||
case BreakpointType::ReadWatch:
|
||||
success = debug_process->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::Read);
|
||||
success = debug_process->RemoveWatchpoint(system.Kernel(), addr, size, Kernel::DebugWatchpointType::Read);
|
||||
break;
|
||||
case BreakpointType::AccessWatch:
|
||||
success = debug_process->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::ReadOrWrite);
|
||||
success = debug_process->RemoveWatchpoint(system.Kernel(), addr, size, Kernel::DebugWatchpointType::ReadOrWrite);
|
||||
break;
|
||||
case BreakpointType::Hardware:
|
||||
default:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
@@ -17,7 +17,8 @@
|
||||
namespace Kernel {
|
||||
|
||||
GlobalSchedulerContext::GlobalSchedulerContext(KernelCore& kernel)
|
||||
: m_kernel{kernel}, m_scheduler_lock{kernel} {}
|
||||
: m_scheduler_lock{kernel}
|
||||
{}
|
||||
|
||||
GlobalSchedulerContext::~GlobalSchedulerContext() = default;
|
||||
|
||||
@@ -37,7 +38,7 @@ void GlobalSchedulerContext::RemoveThread(KThread* thread) noexcept {
|
||||
/// and then does some core rebalancing. Preemption priorities can be found
|
||||
/// in the array 'preemption_priorities'.
|
||||
/// @note This operation happens every 10ms.
|
||||
void GlobalSchedulerContext::PreemptThreads() noexcept {
|
||||
void GlobalSchedulerContext::PreemptThreads(KernelCore& kernel) noexcept {
|
||||
// The priority levels at which the global scheduler preempts threads every 10 ms. They are
|
||||
// ordered from Core 0 to Core 3.
|
||||
static constexpr std::array<u32, Core::Hardware::NUM_CPU_CORES> per_core{
|
||||
@@ -46,9 +47,9 @@ void GlobalSchedulerContext::PreemptThreads() noexcept {
|
||||
59,
|
||||
63,
|
||||
};
|
||||
ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel));
|
||||
ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel));
|
||||
for (u32 core_id = 0; core_id < per_core.size(); core_id++)
|
||||
KScheduler::RotateScheduledQueue(m_kernel, core_id, per_core[core_id]);
|
||||
KScheduler::RotateScheduledQueue(kernel, core_id, per_core[core_id]);
|
||||
}
|
||||
|
||||
/// @brief Returns true if the global scheduler lock is acquired
|
||||
@@ -69,11 +70,11 @@ void GlobalSchedulerContext::UnregisterDummyThreadForWakeup(KThread* thread) noe
|
||||
}
|
||||
}
|
||||
|
||||
void GlobalSchedulerContext::WakeupWaitingDummyThreads() noexcept {
|
||||
void GlobalSchedulerContext::WakeupWaitingDummyThreads(KernelCore& kernel) noexcept {
|
||||
ASSERT(this->IsLocked());
|
||||
if (m_woken_dummy_threads.size() > 0) {
|
||||
for (auto* thread : m_woken_dummy_threads)
|
||||
thread->DummyThreadEndWait();
|
||||
thread->DummyThreadEndWait(kernel);
|
||||
m_woken_dummy_threads.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
@@ -50,17 +50,16 @@ public:
|
||||
}
|
||||
void AddThread(KThread* thread) noexcept;
|
||||
void RemoveThread(KThread* thread) noexcept;
|
||||
void PreemptThreads() noexcept;
|
||||
void PreemptThreads(KernelCore& kernel) noexcept;
|
||||
bool IsLocked() const noexcept;
|
||||
void UnregisterDummyThreadForWakeup(KThread* thread) noexcept;
|
||||
void RegisterDummyThreadForWakeup(KThread* thread) noexcept;
|
||||
void WakeupWaitingDummyThreads() noexcept;
|
||||
void WakeupWaitingDummyThreads(KernelCore& kernel) noexcept;
|
||||
|
||||
private:
|
||||
friend class KScopedSchedulerLock;
|
||||
friend class KScopedSchedulerLockAndSleep;
|
||||
|
||||
KernelCore& m_kernel;
|
||||
std::atomic_bool m_scheduler_update_needed{};
|
||||
KSchedulerPriorityQueue m_priority_queue;
|
||||
LockType m_scheduler_lock;
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -281,7 +284,7 @@ void KPageBufferSlabHeap::Initialize(Core::System& system) {
|
||||
|
||||
// Reserve memory from the system resource limit.
|
||||
ASSERT(
|
||||
kernel.GetSystemResourceLimit()->Reserve(LimitableResource::PhysicalMemoryMax, slab_size));
|
||||
kernel.GetSystemResourceLimit()->Reserve(kernel, LimitableResource::PhysicalMemoryMax, slab_size));
|
||||
|
||||
// Allocate memory for the slab.
|
||||
constexpr auto AllocateOption = KMemoryManager::EncodeOption(
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -16,8 +19,9 @@
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
KAddressArbiter::KAddressArbiter(Core::System& system)
|
||||
: m_system{system}, m_kernel{system.Kernel()} {}
|
||||
KAddressArbiter::KAddressArbiter(Core::System& system_)
|
||||
: system{system_}
|
||||
{}
|
||||
KAddressArbiter::~KAddressArbiter() = default;
|
||||
|
||||
namespace {
|
||||
@@ -112,7 +116,7 @@ public:
|
||||
explicit ThreadQueueImplForKAddressArbiter(KernelCore& kernel, KAddressArbiter::ThreadTree* t)
|
||||
: KThreadQueue(kernel), m_tree(t) {}
|
||||
|
||||
void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override {
|
||||
void CancelWait(KernelCore& kernel, KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override {
|
||||
// If the thread is waiting on an address arbiter, remove it from the tree.
|
||||
if (waiting_thread->IsWaitingForAddressArbiter()) {
|
||||
m_tree->erase(m_tree->iterator_to(*waiting_thread));
|
||||
@@ -120,7 +124,7 @@ public:
|
||||
}
|
||||
|
||||
// Invoke the base cancel wait handler.
|
||||
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
|
||||
KThreadQueue::CancelWait(kernel, waiting_thread, wait_result, cancel_timer_task);
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -133,14 +137,14 @@ Result KAddressArbiter::Signal(uint64_t addr, s32 count) {
|
||||
// Perform signaling.
|
||||
s32 num_waiters{};
|
||||
{
|
||||
KScopedSchedulerLock sl(m_kernel);
|
||||
KScopedSchedulerLock sl(system.Kernel());
|
||||
|
||||
auto it = m_tree.nfind_key({addr, -1});
|
||||
while ((it != m_tree.end()) && (count <= 0 || num_waiters < count) &&
|
||||
(it->GetAddressArbiterKey() == addr)) {
|
||||
// End the thread's wait.
|
||||
KThread* target_thread = std::addressof(*it);
|
||||
target_thread->EndWait(ResultSuccess);
|
||||
target_thread->EndWait(system.Kernel(), ResultSuccess);
|
||||
|
||||
ASSERT(target_thread->IsWaitingForAddressArbiter());
|
||||
target_thread->ClearAddressArbiter();
|
||||
@@ -156,11 +160,11 @@ Result KAddressArbiter::SignalAndIncrementIfEqual(uint64_t addr, s32 value, s32
|
||||
// Perform signaling.
|
||||
s32 num_waiters{};
|
||||
{
|
||||
KScopedSchedulerLock sl(m_kernel);
|
||||
KScopedSchedulerLock sl(system.Kernel());
|
||||
|
||||
// Check the userspace value.
|
||||
s32 user_value{};
|
||||
R_UNLESS(UpdateIfEqual(m_kernel, std::addressof(user_value), addr, value, value + 1),
|
||||
R_UNLESS(UpdateIfEqual(system.Kernel(), std::addressof(user_value), addr, value, value + 1),
|
||||
ResultInvalidCurrentMemory);
|
||||
R_UNLESS(user_value == value, ResultInvalidState);
|
||||
|
||||
@@ -169,7 +173,7 @@ Result KAddressArbiter::SignalAndIncrementIfEqual(uint64_t addr, s32 value, s32
|
||||
(it->GetAddressArbiterKey() == addr)) {
|
||||
// End the thread's wait.
|
||||
KThread* target_thread = std::addressof(*it);
|
||||
target_thread->EndWait(ResultSuccess);
|
||||
target_thread->EndWait(system.Kernel(), ResultSuccess);
|
||||
|
||||
ASSERT(target_thread->IsWaitingForAddressArbiter());
|
||||
target_thread->ClearAddressArbiter();
|
||||
@@ -185,7 +189,7 @@ Result KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(uint64_t addr, s32
|
||||
// Perform signaling.
|
||||
s32 num_waiters{};
|
||||
{
|
||||
KScopedSchedulerLock sl(m_kernel);
|
||||
KScopedSchedulerLock sl(system.Kernel());
|
||||
|
||||
auto it = m_tree.nfind_key({addr, -1});
|
||||
// Determine the updated value.
|
||||
@@ -220,9 +224,9 @@ Result KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(uint64_t addr, s32
|
||||
s32 user_value{};
|
||||
bool succeeded{};
|
||||
if (value != new_value) {
|
||||
succeeded = UpdateIfEqual(m_kernel, std::addressof(user_value), addr, value, new_value);
|
||||
succeeded = UpdateIfEqual(system.Kernel(), std::addressof(user_value), addr, value, new_value);
|
||||
} else {
|
||||
succeeded = ReadFromUser(m_kernel, std::addressof(user_value), addr);
|
||||
succeeded = ReadFromUser(system.Kernel(), std::addressof(user_value), addr);
|
||||
}
|
||||
|
||||
R_UNLESS(succeeded, ResultInvalidCurrentMemory);
|
||||
@@ -232,7 +236,7 @@ Result KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(uint64_t addr, s32
|
||||
(it->GetAddressArbiterKey() == addr)) {
|
||||
// End the thread's wait.
|
||||
KThread* target_thread = std::addressof(*it);
|
||||
target_thread->EndWait(ResultSuccess);
|
||||
target_thread->EndWait(system.Kernel(), ResultSuccess);
|
||||
|
||||
ASSERT(target_thread->IsWaitingForAddressArbiter());
|
||||
target_thread->ClearAddressArbiter();
|
||||
@@ -246,12 +250,12 @@ Result KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(uint64_t addr, s32
|
||||
|
||||
Result KAddressArbiter::WaitIfLessThan(uint64_t addr, s32 value, bool decrement, s64 timeout) {
|
||||
// Prepare to wait.
|
||||
KThread* cur_thread = GetCurrentThreadPointer(m_kernel);
|
||||
KThread* cur_thread = GetCurrentThreadPointer(system.Kernel());
|
||||
KHardwareTimer* timer{};
|
||||
ThreadQueueImplForKAddressArbiter wait_queue(m_kernel, std::addressof(m_tree));
|
||||
ThreadQueueImplForKAddressArbiter wait_queue(system.Kernel(), std::addressof(m_tree));
|
||||
|
||||
{
|
||||
KScopedSchedulerLockAndSleep slp{m_kernel, std::addressof(timer), cur_thread, timeout};
|
||||
KScopedSchedulerLockAndSleep slp{system.Kernel(), std::addressof(timer), cur_thread, timeout};
|
||||
|
||||
// Check that the thread isn't terminating.
|
||||
if (cur_thread->IsTerminationRequested()) {
|
||||
@@ -263,9 +267,9 @@ Result KAddressArbiter::WaitIfLessThan(uint64_t addr, s32 value, bool decrement,
|
||||
s32 user_value{};
|
||||
bool succeeded{};
|
||||
if (decrement) {
|
||||
succeeded = DecrementIfLessThan(m_kernel, std::addressof(user_value), addr, value);
|
||||
succeeded = DecrementIfLessThan(system.Kernel(), std::addressof(user_value), addr, value);
|
||||
} else {
|
||||
succeeded = ReadFromUser(m_kernel, std::addressof(user_value), addr);
|
||||
succeeded = ReadFromUser(system.Kernel(), std::addressof(user_value), addr);
|
||||
}
|
||||
|
||||
if (!succeeded) {
|
||||
@@ -291,7 +295,7 @@ Result KAddressArbiter::WaitIfLessThan(uint64_t addr, s32 value, bool decrement,
|
||||
|
||||
// Wait for the thread to finish.
|
||||
wait_queue.SetHardwareTimer(timer);
|
||||
cur_thread->BeginWait(std::addressof(wait_queue));
|
||||
cur_thread->BeginWait(system.Kernel(), std::addressof(wait_queue));
|
||||
cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration);
|
||||
}
|
||||
|
||||
@@ -301,12 +305,12 @@ Result KAddressArbiter::WaitIfLessThan(uint64_t addr, s32 value, bool decrement,
|
||||
|
||||
Result KAddressArbiter::WaitIfEqual(uint64_t addr, s32 value, s64 timeout) {
|
||||
// Prepare to wait.
|
||||
KThread* cur_thread = GetCurrentThreadPointer(m_kernel);
|
||||
KThread* cur_thread = GetCurrentThreadPointer(system.Kernel());
|
||||
KHardwareTimer* timer{};
|
||||
ThreadQueueImplForKAddressArbiter wait_queue(m_kernel, std::addressof(m_tree));
|
||||
ThreadQueueImplForKAddressArbiter wait_queue(system.Kernel(), std::addressof(m_tree));
|
||||
|
||||
{
|
||||
KScopedSchedulerLockAndSleep slp{m_kernel, std::addressof(timer), cur_thread, timeout};
|
||||
KScopedSchedulerLockAndSleep slp{system.Kernel(), std::addressof(timer), cur_thread, timeout};
|
||||
|
||||
// Check that the thread isn't terminating.
|
||||
if (cur_thread->IsTerminationRequested()) {
|
||||
@@ -316,7 +320,7 @@ Result KAddressArbiter::WaitIfEqual(uint64_t addr, s32 value, s64 timeout) {
|
||||
|
||||
// Read the value from userspace.
|
||||
s32 user_value{};
|
||||
if (!ReadFromUser(m_kernel, std::addressof(user_value), addr)) {
|
||||
if (!ReadFromUser(system.Kernel(), std::addressof(user_value), addr)) {
|
||||
slp.CancelSleep();
|
||||
R_THROW(ResultInvalidCurrentMemory);
|
||||
}
|
||||
@@ -339,7 +343,7 @@ Result KAddressArbiter::WaitIfEqual(uint64_t addr, s32 value, s64 timeout) {
|
||||
|
||||
// Wait for the thread to finish.
|
||||
wait_queue.SetHardwareTimer(timer);
|
||||
cur_thread->BeginWait(std::addressof(wait_queue));
|
||||
cur_thread->BeginWait(system.Kernel(), std::addressof(wait_queue));
|
||||
cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -60,8 +63,7 @@ private:
|
||||
|
||||
private:
|
||||
ThreadTree m_tree;
|
||||
Core::System& m_system;
|
||||
KernelCore& m_kernel;
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
@@ -15,8 +15,8 @@ KAutoObject* KAutoObject::Create(KAutoObject* obj) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
void KAutoObject::RegisterWithKernel() {
|
||||
m_kernel.RegisterKernelObject(this);
|
||||
void KAutoObject::RegisterWithKernel(KernelCore& kernel) {
|
||||
kernel.RegisterKernelObject(this);
|
||||
}
|
||||
|
||||
void KAutoObject::UnregisterWithKernel(KernelCore& kernel, KAutoObject* self) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
@@ -87,21 +87,21 @@ private:
|
||||
KERNEL_AUTOOBJECT_TRAITS_IMPL(KAutoObject, KAutoObject, const);
|
||||
|
||||
public:
|
||||
explicit KAutoObject(KernelCore& kernel) : m_kernel(kernel) {
|
||||
explicit KAutoObject(KernelCore& kernel) {
|
||||
m_class_token = GetStaticTypeObj().GetClassToken();
|
||||
RegisterWithKernel();
|
||||
RegisterWithKernel(kernel);
|
||||
}
|
||||
virtual ~KAutoObject() = default;
|
||||
|
||||
static KAutoObject* Create(KAutoObject* ptr);
|
||||
|
||||
// Destroy is responsible for destroying the auto object's resources when ref_count hits zero.
|
||||
virtual void Destroy() {
|
||||
virtual void Destroy(KernelCore& kernel) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
// Finalize is responsible for cleaning up resource, but does not destroy the object.
|
||||
virtual void Finalize() {}
|
||||
virtual void Finalize(KernelCore& kernel) {}
|
||||
|
||||
virtual KProcess* GetOwner() const {
|
||||
return nullptr;
|
||||
@@ -123,67 +123,50 @@ public:
|
||||
Derived DynamicCast() {
|
||||
static_assert(std::is_pointer_v<Derived>);
|
||||
using DerivedType = std::remove_pointer_t<Derived>;
|
||||
|
||||
if (this->IsDerivedFrom(DerivedType::GetStaticTypeObj())) {
|
||||
return static_cast<Derived>(this);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
if (this->IsDerivedFrom(DerivedType::GetStaticTypeObj()))
|
||||
return Derived(this);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename Derived>
|
||||
const Derived DynamicCast() const {
|
||||
static_assert(std::is_pointer_v<Derived>);
|
||||
using DerivedType = std::remove_pointer_t<Derived>;
|
||||
|
||||
if (this->IsDerivedFrom(DerivedType::GetStaticTypeObj())) {
|
||||
return static_cast<Derived>(this);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
if (this->IsDerivedFrom(DerivedType::GetStaticTypeObj()))
|
||||
return Derived(this);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Open() {
|
||||
bool Open(KernelCore& kernel) {
|
||||
// Atomically increment the reference count, only if it's positive.
|
||||
u32 cur_ref_count = m_ref_count.load(std::memory_order_acquire);
|
||||
do {
|
||||
if (cur_ref_count == 0) {
|
||||
if (cur_ref_count == 0)
|
||||
return false;
|
||||
}
|
||||
ASSERT(cur_ref_count < cur_ref_count + 1);
|
||||
} while (!m_ref_count.compare_exchange_weak(cur_ref_count, cur_ref_count + 1,
|
||||
std::memory_order_relaxed));
|
||||
|
||||
} while (!m_ref_count.compare_exchange_weak(cur_ref_count, cur_ref_count + 1, std::memory_order_relaxed));
|
||||
return true;
|
||||
}
|
||||
|
||||
void Close() {
|
||||
void Close(KernelCore& kernel) {
|
||||
// Atomically decrement the reference count, not allowing it to become negative.
|
||||
u32 cur_ref_count = m_ref_count.load(std::memory_order_acquire);
|
||||
do {
|
||||
if (cur_ref_count == 0) {
|
||||
if (cur_ref_count == 0)
|
||||
return;
|
||||
}
|
||||
ASSERT(cur_ref_count > 0);
|
||||
} while (!m_ref_count.compare_exchange_weak(cur_ref_count, cur_ref_count - 1,
|
||||
std::memory_order_acq_rel));
|
||||
|
||||
} while (!m_ref_count.compare_exchange_weak(cur_ref_count, cur_ref_count - 1, std::memory_order_acq_rel));
|
||||
// If ref count hits 1, destroy the object.
|
||||
if (cur_ref_count == 1) {
|
||||
KernelCore& kernel = m_kernel;
|
||||
this->Destroy();
|
||||
this->Destroy(kernel);
|
||||
KAutoObject::UnregisterWithKernel(kernel, this);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void RegisterWithKernel();
|
||||
void RegisterWithKernel(KernelCore& kernel);
|
||||
static void UnregisterWithKernel(KernelCore& kernel, KAutoObject* self);
|
||||
|
||||
protected:
|
||||
KernelCore& m_kernel;
|
||||
|
||||
private:
|
||||
std::atomic<u32> m_ref_count{};
|
||||
ClassTokenType m_class_token{};
|
||||
};
|
||||
@@ -225,17 +208,22 @@ class KScopedAutoObject {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(KScopedAutoObject);
|
||||
|
||||
constexpr KScopedAutoObject() = default;
|
||||
constexpr KScopedAutoObject(KernelCore& kernel_)
|
||||
: kernel{kernel_}
|
||||
{}
|
||||
|
||||
constexpr KScopedAutoObject(T* o) : m_obj(o) {
|
||||
constexpr KScopedAutoObject(KernelCore& kernel_, T* o)
|
||||
: kernel{kernel_}
|
||||
, m_obj(o)
|
||||
{
|
||||
if (m_obj != nullptr) {
|
||||
m_obj->Open();
|
||||
m_obj->Open(kernel);
|
||||
}
|
||||
}
|
||||
|
||||
~KScopedAutoObject() {
|
||||
if (m_obj != nullptr) {
|
||||
m_obj->Close();
|
||||
m_obj->Close(kernel);
|
||||
}
|
||||
m_obj = nullptr;
|
||||
}
|
||||
@@ -253,7 +241,7 @@ public:
|
||||
if (rhs.m_obj != nullptr) {
|
||||
derived = rhs.m_obj->template DynamicCast<T*>();
|
||||
if (derived == nullptr) {
|
||||
rhs.m_obj->Close();
|
||||
rhs.m_obj->Close(rhs.kernel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,8 +262,16 @@ public:
|
||||
return *m_obj;
|
||||
}
|
||||
|
||||
constexpr void SetObject(T* o) {
|
||||
if (m_obj)
|
||||
m_obj->Close(kernel);
|
||||
m_obj = o;
|
||||
if (m_obj)
|
||||
m_obj->Open(kernel);
|
||||
}
|
||||
|
||||
constexpr void Reset(T* o) {
|
||||
KScopedAutoObject(o).Swap(*this);
|
||||
KScopedAutoObject(kernel, o).Swap(*this);
|
||||
}
|
||||
|
||||
constexpr T* GetPointerUnsafe() {
|
||||
@@ -304,6 +300,7 @@ private:
|
||||
friend class KScopedAutoObject;
|
||||
|
||||
private:
|
||||
KernelCore& kernel;
|
||||
T* m_obj{};
|
||||
|
||||
private:
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2021 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -15,7 +18,7 @@ namespace Kernel {
|
||||
KClientPort::KClientPort(KernelCore& kernel) : KSynchronizationObject{kernel} {}
|
||||
KClientPort::~KClientPort() = default;
|
||||
|
||||
void KClientPort::Initialize(KPort* parent, s32 max_sessions) {
|
||||
void KClientPort::Initialize(KernelCore& kernel, KPort* parent, s32 max_sessions) {
|
||||
// Set member variables.
|
||||
m_num_sessions = 0;
|
||||
m_peak_sessions = 0;
|
||||
@@ -23,48 +26,48 @@ void KClientPort::Initialize(KPort* parent, s32 max_sessions) {
|
||||
m_max_sessions = max_sessions;
|
||||
}
|
||||
|
||||
void KClientPort::OnSessionFinalized() {
|
||||
KScopedSchedulerLock sl{m_kernel};
|
||||
void KClientPort::OnSessionFinalized(KernelCore& kernel) {
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
|
||||
if (const auto prev = m_num_sessions--; prev == m_max_sessions) {
|
||||
this->NotifyAvailable();
|
||||
this->NotifyAvailable(kernel);
|
||||
}
|
||||
}
|
||||
|
||||
void KClientPort::OnServerClosed() {}
|
||||
void KClientPort::OnServerClosed(KernelCore& kernel) {}
|
||||
|
||||
bool KClientPort::IsLight() const {
|
||||
bool KClientPort::IsLight(KernelCore& kernel) const {
|
||||
return this->GetParent()->IsLight();
|
||||
}
|
||||
|
||||
bool KClientPort::IsServerClosed() const {
|
||||
return this->GetParent()->IsServerClosed();
|
||||
bool KClientPort::IsServerClosed(KernelCore& kernel) const {
|
||||
return this->GetParent()->IsServerClosed(kernel);
|
||||
}
|
||||
|
||||
void KClientPort::Destroy() {
|
||||
void KClientPort::Destroy(KernelCore& kernel) {
|
||||
// Note with our parent that we're closed.
|
||||
m_parent->OnClientClosed();
|
||||
m_parent->OnClientClosed(kernel);
|
||||
|
||||
// Close our reference to our parent.
|
||||
m_parent->Close();
|
||||
m_parent->Close(kernel);
|
||||
}
|
||||
|
||||
bool KClientPort::IsSignaled() const {
|
||||
bool KClientPort::IsSignaled(KernelCore& kernel) const {
|
||||
return m_num_sessions.load() < m_max_sessions;
|
||||
}
|
||||
|
||||
Result KClientPort::CreateSession(KClientSession** out) {
|
||||
Result KClientPort::CreateSession(KernelCore& kernel, KClientSession** out) {
|
||||
// Declare the session we're going to allocate.
|
||||
KSession* session{};
|
||||
|
||||
// Reserve a new session from the resource limit.
|
||||
KScopedResourceReservation session_reservation(GetCurrentProcessPointer(m_kernel),
|
||||
KScopedResourceReservation session_reservation(kernel, GetCurrentProcessPointer(kernel),
|
||||
LimitableResource::SessionCountMax);
|
||||
R_UNLESS(session_reservation.Succeeded(), ResultLimitReached);
|
||||
|
||||
// Allocate a session normally.
|
||||
// TODO: Dynamic resource limits
|
||||
session = KSession::Create(m_kernel);
|
||||
session = KSession::Create(kernel);
|
||||
|
||||
// Check that we successfully created a session.
|
||||
R_UNLESS(session != nullptr, ResultOutOfResource);
|
||||
@@ -72,7 +75,7 @@ Result KClientPort::CreateSession(KClientSession** out) {
|
||||
// Update the session counts.
|
||||
{
|
||||
ON_RESULT_FAILURE {
|
||||
session->Close();
|
||||
session->Close(kernel);
|
||||
};
|
||||
|
||||
// Atomically increment the number of sessions.
|
||||
@@ -100,38 +103,37 @@ Result KClientPort::CreateSession(KClientSession** out) {
|
||||
}
|
||||
|
||||
// Initialize the session.
|
||||
session->Initialize(this, m_parent->GetName());
|
||||
session->Initialize(kernel, this, m_parent->GetName());
|
||||
|
||||
// Commit the session reservation.
|
||||
session_reservation.Commit();
|
||||
|
||||
// Register the session.
|
||||
KSession::Register(m_kernel, session);
|
||||
KSession::Register(kernel, session);
|
||||
ON_RESULT_FAILURE {
|
||||
session->GetClientSession().Close();
|
||||
session->GetServerSession().Close();
|
||||
session->GetClientSession().Close(kernel);
|
||||
session->GetServerSession().Close(kernel);
|
||||
};
|
||||
|
||||
// Enqueue the session with our parent.
|
||||
R_TRY(m_parent->EnqueueSession(std::addressof(session->GetServerSession())));
|
||||
R_TRY(m_parent->EnqueueSession(kernel, std::addressof(session->GetServerSession())));
|
||||
|
||||
// We succeeded, so set the output.
|
||||
*out = std::addressof(session->GetClientSession());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result KClientPort::CreateLightSession(KLightClientSession** out) {
|
||||
Result KClientPort::CreateLightSession(KernelCore& kernel, KLightClientSession** out) {
|
||||
// Declare the session we're going to allocate.
|
||||
KLightSession* session{};
|
||||
|
||||
// Reserve a new session from the resource limit.
|
||||
KScopedResourceReservation session_reservation(GetCurrentProcessPointer(m_kernel),
|
||||
Svc::LimitableResource::SessionCountMax);
|
||||
KScopedResourceReservation session_reservation(kernel, GetCurrentProcessPointer(kernel), Svc::LimitableResource::SessionCountMax);
|
||||
R_UNLESS(session_reservation.Succeeded(), ResultLimitReached);
|
||||
|
||||
// Allocate a session normally.
|
||||
// TODO: Dynamic resource limits
|
||||
session = KLightSession::Create(m_kernel);
|
||||
session = KLightSession::Create(kernel);
|
||||
|
||||
// Check that we successfully created a session.
|
||||
R_UNLESS(session != nullptr, ResultOutOfResource);
|
||||
@@ -139,7 +141,7 @@ Result KClientPort::CreateLightSession(KLightClientSession** out) {
|
||||
// Update the session counts.
|
||||
{
|
||||
ON_RESULT_FAILURE {
|
||||
session->Close();
|
||||
session->Close(kernel);
|
||||
};
|
||||
|
||||
// Atomically increment the number of sessions.
|
||||
@@ -167,20 +169,20 @@ Result KClientPort::CreateLightSession(KLightClientSession** out) {
|
||||
}
|
||||
|
||||
// Initialize the session.
|
||||
session->Initialize(this, m_parent->GetName());
|
||||
session->Initialize(kernel, this, m_parent->GetName());
|
||||
|
||||
// Commit the session reservation.
|
||||
session_reservation.Commit();
|
||||
|
||||
// Register the session.
|
||||
KLightSession::Register(m_kernel, session);
|
||||
KLightSession::Register(kernel, session);
|
||||
ON_RESULT_FAILURE {
|
||||
session->GetClientSession().Close();
|
||||
session->GetServerSession().Close();
|
||||
session->GetClientSession().Close(kernel);
|
||||
session->GetServerSession().Close(kernel);
|
||||
};
|
||||
|
||||
// Enqueue the session with our parent.
|
||||
R_TRY(m_parent->EnqueueSession(std::addressof(session->GetServerSession())));
|
||||
R_TRY(m_parent->EnqueueSession(kernel, std::addressof(session->GetServerSession())));
|
||||
|
||||
// We succeeded, so set the output.
|
||||
*out = std::addressof(session->GetClientSession());
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -23,9 +26,9 @@ public:
|
||||
explicit KClientPort(KernelCore& kernel);
|
||||
~KClientPort() override;
|
||||
|
||||
void Initialize(KPort* parent, s32 max_sessions);
|
||||
void OnSessionFinalized();
|
||||
void OnServerClosed();
|
||||
void Initialize(KernelCore& kernel, KPort* parent, s32 max_sessions);
|
||||
void OnSessionFinalized(KernelCore& kernel);
|
||||
void OnServerClosed(KernelCore& kernel);
|
||||
|
||||
const KPort* GetParent() const {
|
||||
return m_parent;
|
||||
@@ -44,15 +47,15 @@ public:
|
||||
return m_max_sessions;
|
||||
}
|
||||
|
||||
bool IsLight() const;
|
||||
bool IsServerClosed() const;
|
||||
bool IsLight(KernelCore& kernel) const;
|
||||
bool IsServerClosed(KernelCore& kernel) const;
|
||||
|
||||
// Overridden virtual functions.
|
||||
void Destroy() override;
|
||||
bool IsSignaled() const override;
|
||||
void Destroy(KernelCore& kernel) override;
|
||||
bool IsSignaled(KernelCore& kernel) const override;
|
||||
|
||||
Result CreateSession(KClientSession** out);
|
||||
Result CreateLightSession(KLightClientSession** out);
|
||||
Result CreateSession(KernelCore& kernel, KClientSession** out);
|
||||
Result CreateLightSession(KernelCore& kernel, KLightClientSession** out);
|
||||
|
||||
private:
|
||||
std::atomic<s32> m_num_sessions{};
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -13,41 +16,41 @@ namespace Kernel {
|
||||
KClientSession::KClientSession(KernelCore& kernel) : KAutoObject{kernel} {}
|
||||
KClientSession::~KClientSession() = default;
|
||||
|
||||
void KClientSession::Destroy() {
|
||||
m_parent->OnClientClosed();
|
||||
m_parent->Close();
|
||||
void KClientSession::Destroy(KernelCore& kernel) {
|
||||
m_parent->OnClientClosed(kernel);
|
||||
m_parent->Close(kernel);
|
||||
}
|
||||
|
||||
void KClientSession::OnServerClosed() {}
|
||||
|
||||
Result KClientSession::SendSyncRequest(uintptr_t address, size_t size) {
|
||||
Result KClientSession::SendSyncRequest(KernelCore& kernel, uintptr_t address, size_t size) {
|
||||
// Create a session request.
|
||||
KSessionRequest* request = KSessionRequest::Create(m_kernel);
|
||||
KSessionRequest* request = KSessionRequest::Create(kernel);
|
||||
R_UNLESS(request != nullptr, ResultOutOfResource);
|
||||
SCOPE_EXIT {
|
||||
request->Close();
|
||||
request->Close(kernel);
|
||||
};
|
||||
|
||||
// Initialize the request.
|
||||
request->Initialize(nullptr, address, size);
|
||||
request->Initialize(kernel, nullptr, address, size);
|
||||
|
||||
// Send the request.
|
||||
R_RETURN(m_parent->OnRequest(request));
|
||||
R_RETURN(m_parent->OnRequest(kernel, request));
|
||||
}
|
||||
|
||||
Result KClientSession::SendAsyncRequest(KEvent* event, uintptr_t address, size_t size) {
|
||||
Result KClientSession::SendAsyncRequest(KernelCore& kernel, KEvent* event, uintptr_t address, size_t size) {
|
||||
// Create a session request.
|
||||
KSessionRequest* request = KSessionRequest::Create(m_kernel);
|
||||
KSessionRequest* request = KSessionRequest::Create(kernel);
|
||||
R_UNLESS(request != nullptr, ResultOutOfResource);
|
||||
SCOPE_EXIT {
|
||||
request->Close();
|
||||
request->Close(kernel);
|
||||
};
|
||||
|
||||
// Initialize the request.
|
||||
request->Initialize(event, address, size);
|
||||
request->Initialize(kernel, event, address, size);
|
||||
|
||||
// Send the request.
|
||||
R_RETURN(m_parent->OnRequest(request));
|
||||
R_RETURN(m_parent->OnRequest(kernel, request));
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -26,14 +29,14 @@ public:
|
||||
m_parent = parent;
|
||||
}
|
||||
|
||||
void Destroy() override;
|
||||
void Destroy(KernelCore& kernel) override;
|
||||
|
||||
KSession* GetParent() const {
|
||||
return m_parent;
|
||||
}
|
||||
|
||||
Result SendSyncRequest(uintptr_t address, size_t size);
|
||||
Result SendAsyncRequest(KEvent* event, uintptr_t address, size_t size);
|
||||
Result SendSyncRequest(KernelCore& kernel, uintptr_t address, size_t size);
|
||||
Result SendAsyncRequest(KernelCore& kernel, KEvent* event, uintptr_t address, size_t size);
|
||||
|
||||
void OnServerClosed();
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -19,16 +22,15 @@ namespace Kernel {
|
||||
KCodeMemory::KCodeMemory(KernelCore& kernel)
|
||||
: KAutoObjectWithSlabHeapAndContainer{kernel}, m_lock(kernel) {}
|
||||
|
||||
Result KCodeMemory::Initialize(Core::DeviceMemory& device_memory, KProcessAddress addr,
|
||||
size_t size) {
|
||||
Result KCodeMemory::Initialize(KernelCore& kernel, Core::DeviceMemory& device_memory, KProcessAddress addr, size_t size) {
|
||||
// Set members.
|
||||
m_owner = GetCurrentProcessPointer(m_kernel);
|
||||
m_owner = GetCurrentProcessPointer(kernel);
|
||||
|
||||
// Get the owner page table.
|
||||
auto& page_table = m_owner->GetPageTable();
|
||||
|
||||
// Construct the page group.
|
||||
m_page_group.emplace(m_kernel, page_table.GetBlockInfoManager());
|
||||
m_page_group.emplace(kernel, page_table.GetBlockInfoManager());
|
||||
|
||||
// Lock the memory.
|
||||
R_TRY(page_table.LockForCodeMemory(std::addressof(*m_page_group), addr, size))
|
||||
@@ -39,7 +41,7 @@ Result KCodeMemory::Initialize(Core::DeviceMemory& device_memory, KProcessAddres
|
||||
}
|
||||
|
||||
// Set remaining tracking members.
|
||||
m_owner->Open();
|
||||
m_owner->Open(kernel);
|
||||
m_address = addr;
|
||||
m_is_initialized = true;
|
||||
m_is_owner_mapped = false;
|
||||
@@ -49,7 +51,7 @@ Result KCodeMemory::Initialize(Core::DeviceMemory& device_memory, KProcessAddres
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void KCodeMemory::Finalize() {
|
||||
void KCodeMemory::Finalize(KernelCore& kernel) {
|
||||
// Unlock.
|
||||
if (!m_is_mapped && !m_is_owner_mapped) {
|
||||
const size_t size = m_page_group->GetNumPages() * PageSize;
|
||||
@@ -57,14 +59,14 @@ void KCodeMemory::Finalize() {
|
||||
}
|
||||
|
||||
// Close the page group.
|
||||
m_page_group->Close();
|
||||
m_page_group->Close(kernel);
|
||||
m_page_group->Finalize();
|
||||
|
||||
// Close our reference to our owner.
|
||||
m_owner->Close();
|
||||
m_owner->Close(kernel);
|
||||
}
|
||||
|
||||
Result KCodeMemory::Map(KProcessAddress address, size_t size) {
|
||||
Result KCodeMemory::Map(KernelCore& kernel, KProcessAddress address, size_t size) {
|
||||
// Validate the size.
|
||||
R_UNLESS(m_page_group->GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
|
||||
|
||||
@@ -75,7 +77,7 @@ Result KCodeMemory::Map(KProcessAddress address, size_t size) {
|
||||
R_UNLESS(!m_is_mapped, ResultInvalidState);
|
||||
|
||||
// Map the memory.
|
||||
R_TRY(GetCurrentProcess(m_kernel).GetPageTable().MapPageGroup(
|
||||
R_TRY(GetCurrentProcess(kernel).GetPageTable().MapPageGroup(
|
||||
address, *m_page_group, KMemoryState::CodeOut, KMemoryPermission::UserReadWrite));
|
||||
|
||||
// Mark ourselves as mapped.
|
||||
@@ -84,7 +86,7 @@ Result KCodeMemory::Map(KProcessAddress address, size_t size) {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result KCodeMemory::Unmap(KProcessAddress address, size_t size) {
|
||||
Result KCodeMemory::Unmap(KernelCore& kernel, KProcessAddress address, size_t size) {
|
||||
// Validate the size.
|
||||
R_UNLESS(m_page_group->GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
|
||||
|
||||
@@ -92,7 +94,7 @@ Result KCodeMemory::Unmap(KProcessAddress address, size_t size) {
|
||||
KScopedLightLock lk(m_lock);
|
||||
|
||||
// Unmap the memory.
|
||||
R_TRY(GetCurrentProcess(m_kernel).GetPageTable().UnmapPageGroup(address, *m_page_group,
|
||||
R_TRY(GetCurrentProcess(kernel).GetPageTable().UnmapPageGroup(address, *m_page_group,
|
||||
KMemoryState::CodeOut));
|
||||
|
||||
// Mark ourselves as unmapped.
|
||||
@@ -101,7 +103,7 @@ Result KCodeMemory::Unmap(KProcessAddress address, size_t size) {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result KCodeMemory::MapToOwner(KProcessAddress address, size_t size, Svc::MemoryPermission perm) {
|
||||
Result KCodeMemory::MapToOwner(KernelCore& kernel, KProcessAddress address, size_t size, Svc::MemoryPermission perm) {
|
||||
// Validate the size.
|
||||
R_UNLESS(m_page_group->GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
|
||||
|
||||
@@ -135,7 +137,7 @@ Result KCodeMemory::MapToOwner(KProcessAddress address, size_t size, Svc::Memory
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result KCodeMemory::UnmapFromOwner(KProcessAddress address, size_t size) {
|
||||
Result KCodeMemory::UnmapFromOwner(KernelCore& kernel, KProcessAddress address, size_t size) {
|
||||
// Validate the size.
|
||||
R_UNLESS(m_page_group->GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -31,18 +34,18 @@ class KCodeMemory final
|
||||
public:
|
||||
explicit KCodeMemory(KernelCore& kernel);
|
||||
|
||||
Result Initialize(Core::DeviceMemory& device_memory, KProcessAddress address, size_t size);
|
||||
void Finalize() override;
|
||||
Result Initialize(KernelCore& kernel, Core::DeviceMemory& device_memory, KProcessAddress address, size_t size);
|
||||
void Finalize(KernelCore& kernel) override;
|
||||
|
||||
Result Map(KProcessAddress address, size_t size);
|
||||
Result Unmap(KProcessAddress address, size_t size);
|
||||
Result MapToOwner(KProcessAddress address, size_t size, Svc::MemoryPermission perm);
|
||||
Result UnmapFromOwner(KProcessAddress address, size_t size);
|
||||
Result Map(KernelCore& kernel, KProcessAddress address, size_t size);
|
||||
Result Unmap(KernelCore& kernel, KProcessAddress address, size_t size);
|
||||
Result MapToOwner(KernelCore& kernel, KProcessAddress address, size_t size, Svc::MemoryPermission perm);
|
||||
Result UnmapFromOwner(KernelCore& kernel, KProcessAddress address, size_t size);
|
||||
|
||||
bool IsInitialized() const override {
|
||||
return m_is_initialized;
|
||||
}
|
||||
static void PostDestroy(uintptr_t arg) {}
|
||||
static void PostDestroy(KernelCore& kernel, uintptr_t arg) {}
|
||||
|
||||
KProcess* GetOwner() const override {
|
||||
return m_owner;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
@@ -66,14 +66,15 @@ bool UpdateLockAtomic(KernelCore& kernel, u32* out, KProcessAddress address, u32
|
||||
class ThreadQueueImplForKConditionVariableWaitForAddress final : public KThreadQueue {
|
||||
public:
|
||||
explicit ThreadQueueImplForKConditionVariableWaitForAddress(KernelCore& kernel)
|
||||
: KThreadQueue(kernel) {}
|
||||
: KThreadQueue(kernel)
|
||||
{}
|
||||
|
||||
void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override {
|
||||
virtual void CancelWait(KernelCore& kernel, KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override {
|
||||
// Remove the thread as a waiter from its owner.
|
||||
waiting_thread->GetLockOwner()->RemoveWaiter(waiting_thread);
|
||||
waiting_thread->GetLockOwner(kernel)->RemoveWaiter(kernel, waiting_thread);
|
||||
|
||||
// Invoke the base cancel wait handler.
|
||||
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
|
||||
KThreadQueue::CancelWait(kernel, waiting_thread, wait_result, cancel_timer_task);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -82,14 +83,15 @@ private:
|
||||
KConditionVariable::ThreadTree* m_tree;
|
||||
|
||||
public:
|
||||
explicit ThreadQueueImplForKConditionVariableWaitConditionVariable(
|
||||
KernelCore& kernel, KConditionVariable::ThreadTree* t)
|
||||
: KThreadQueue(kernel), m_tree(t) {}
|
||||
explicit ThreadQueueImplForKConditionVariableWaitConditionVariable(KernelCore& kernel, KConditionVariable::ThreadTree* t)
|
||||
: KThreadQueue(kernel)
|
||||
, m_tree(t)
|
||||
{}
|
||||
|
||||
void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override {
|
||||
void CancelWait(KernelCore& kernel, KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override {
|
||||
// Remove the thread as a waiter from its owner.
|
||||
if (KThread* owner = waiting_thread->GetLockOwner(); owner != nullptr) {
|
||||
owner->RemoveWaiter(waiting_thread);
|
||||
if (KThread* owner = waiting_thread->GetLockOwner(kernel); owner != nullptr) {
|
||||
owner->RemoveWaiter(kernel, waiting_thread);
|
||||
}
|
||||
|
||||
// If the thread is waiting on a condvar, remove it from the tree.
|
||||
@@ -99,14 +101,15 @@ public:
|
||||
}
|
||||
|
||||
// Invoke the base cancel wait handler.
|
||||
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
|
||||
KThreadQueue::CancelWait(kernel, waiting_thread, wait_result, cancel_timer_task);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
KConditionVariable::KConditionVariable(Core::System& system)
|
||||
: m_system{system}, m_kernel{system.Kernel()} {}
|
||||
: m_system{system}
|
||||
{}
|
||||
|
||||
KConditionVariable::~KConditionVariable() = default;
|
||||
|
||||
@@ -119,8 +122,7 @@ Result KConditionVariable::SignalToAddress(KernelCore& kernel, KProcessAddress a
|
||||
|
||||
// Remove waiter thread.
|
||||
bool has_waiters{};
|
||||
KThread* const next_owner_thread =
|
||||
owner_thread->RemoveUserWaiterByKey(std::addressof(has_waiters), addr);
|
||||
KThread* const next_owner_thread = owner_thread->RemoveUserWaiterByKey(kernel, std::addressof(has_waiters), addr);
|
||||
|
||||
// Determine the next tag.
|
||||
u32 next_value{};
|
||||
@@ -144,15 +146,14 @@ Result KConditionVariable::SignalToAddress(KernelCore& kernel, KProcessAddress a
|
||||
|
||||
// If necessary, signal the next owner thread.
|
||||
if (next_owner_thread != nullptr) {
|
||||
next_owner_thread->EndWait(result);
|
||||
next_owner_thread->EndWait(kernel, result);
|
||||
}
|
||||
|
||||
R_RETURN(result);
|
||||
}
|
||||
}
|
||||
|
||||
Result KConditionVariable::WaitForAddress(KernelCore& kernel, Handle handle, KProcessAddress addr,
|
||||
u32 value) {
|
||||
Result KConditionVariable::WaitForAddress(KernelCore& kernel, Handle handle, KProcessAddress addr, u32 value) {
|
||||
KThread* cur_thread = GetCurrentThreadPointer(kernel);
|
||||
ThreadQueueImplForKConditionVariableWaitForAddress wait_queue(kernel);
|
||||
|
||||
@@ -173,30 +174,30 @@ Result KConditionVariable::WaitForAddress(KernelCore& kernel, Handle handle, KPr
|
||||
|
||||
// Get the lock owner thread.
|
||||
owner_thread = GetCurrentProcess(kernel)
|
||||
.GetHandleTable()
|
||||
.GetObjectWithoutPseudoHandle<KThread>(handle)
|
||||
.ReleasePointerUnsafe();
|
||||
.GetHandleTable()
|
||||
.GetObjectWithoutPseudoHandle<KThread>(kernel, handle)
|
||||
.ReleasePointerUnsafe();
|
||||
R_UNLESS(owner_thread != nullptr, ResultInvalidHandle);
|
||||
|
||||
// Update the lock.
|
||||
cur_thread->SetUserAddressKey(addr, value);
|
||||
owner_thread->AddWaiter(cur_thread);
|
||||
owner_thread->AddWaiter(kernel, cur_thread);
|
||||
|
||||
// Begin waiting.
|
||||
cur_thread->BeginWait(std::addressof(wait_queue));
|
||||
cur_thread->BeginWait(kernel, std::addressof(wait_queue));
|
||||
cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar);
|
||||
}
|
||||
|
||||
// Close our reference to the owner thread, now that the wait is over.
|
||||
owner_thread->Close();
|
||||
owner_thread->Close(kernel);
|
||||
|
||||
// Get the wait result.
|
||||
R_RETURN(cur_thread->GetWaitResult());
|
||||
}
|
||||
|
||||
void KConditionVariable::SignalImpl(KThread* thread) {
|
||||
void KConditionVariable::SignalImpl(KernelCore& kernel, KThread* thread) {
|
||||
// Check pre-conditions.
|
||||
ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel));
|
||||
ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel));
|
||||
|
||||
// Update the tag.
|
||||
KProcessAddress address = thread->GetAddressKey();
|
||||
@@ -211,35 +212,33 @@ void KConditionVariable::SignalImpl(KThread* thread) {
|
||||
// TODO(bunnei): We should call CanAccessAtomic(..) here.
|
||||
can_access = true;
|
||||
if (can_access) {
|
||||
UpdateLockAtomic(m_kernel, std::addressof(prev_tag), address, own_tag,
|
||||
Svc::HandleWaitMask);
|
||||
UpdateLockAtomic(kernel, std::addressof(prev_tag), address, own_tag, Svc::HandleWaitMask);
|
||||
}
|
||||
}
|
||||
|
||||
if (can_access) {
|
||||
if (prev_tag == Svc::InvalidHandle) {
|
||||
// If nobody held the lock previously, we're all good.
|
||||
thread->EndWait(ResultSuccess);
|
||||
thread->EndWait(kernel, ResultSuccess);
|
||||
} else {
|
||||
// Get the previous owner.
|
||||
KThread* owner_thread = GetCurrentProcess(m_kernel)
|
||||
.GetHandleTable()
|
||||
.GetObjectWithoutPseudoHandle<KThread>(
|
||||
static_cast<Handle>(prev_tag & ~Svc::HandleWaitMask))
|
||||
.ReleasePointerUnsafe();
|
||||
KThread* owner_thread = GetCurrentProcess(kernel)
|
||||
.GetHandleTable()
|
||||
.GetObjectWithoutPseudoHandle<KThread>(kernel, Handle(prev_tag & ~Svc::HandleWaitMask))
|
||||
.ReleasePointerUnsafe();
|
||||
|
||||
if (owner_thread) {
|
||||
// Add the thread as a waiter on the owner.
|
||||
owner_thread->AddWaiter(thread);
|
||||
owner_thread->Close();
|
||||
owner_thread->AddWaiter(kernel, thread);
|
||||
owner_thread->Close(kernel);
|
||||
} else {
|
||||
// The lock was tagged with a thread that doesn't exist.
|
||||
thread->EndWait(ResultInvalidState);
|
||||
thread->EndWait(kernel, ResultInvalidState);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If the address wasn't accessible, note so.
|
||||
thread->EndWait(ResultInvalidCurrentMemory);
|
||||
thread->EndWait(kernel, ResultInvalidCurrentMemory);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,17 +246,16 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) {
|
||||
// Perform signaling.
|
||||
s32 num_waiters{};
|
||||
{
|
||||
KScopedSchedulerLock sl(m_kernel);
|
||||
KScopedSchedulerLock sl(m_system.Kernel());
|
||||
|
||||
auto it = m_tree.nfind_key({cv_key, -1});
|
||||
while ((it != m_tree.end()) && (count <= 0 || num_waiters < count) &&
|
||||
(it->GetConditionVariableKey() == cv_key)) {
|
||||
while ((it != m_tree.end()) && (count <= 0 || num_waiters < count) && (it->GetConditionVariableKey() == cv_key)) {
|
||||
KThread* target_thread = std::addressof(*it);
|
||||
|
||||
it = m_tree.erase(it);
|
||||
target_thread->ClearConditionVariable();
|
||||
|
||||
this->SignalImpl(target_thread);
|
||||
this->SignalImpl(m_system.Kernel(), target_thread);
|
||||
|
||||
++num_waiters;
|
||||
}
|
||||
@@ -265,20 +263,20 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) {
|
||||
// If we have no waiters, clear the has waiter flag.
|
||||
if (it == m_tree.end() || it->GetConditionVariableKey() != cv_key) {
|
||||
constexpr u32 HasNoWaiterFlag = 0;
|
||||
WriteToUser(m_kernel, cv_key, HasNoWaiterFlag);
|
||||
WriteToUser(m_system.Kernel(), cv_key, HasNoWaiterFlag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Result KConditionVariable::Wait(KProcessAddress addr, u64 key, u32 value, s64 timeout) {
|
||||
// Prepare to wait.
|
||||
KThread* cur_thread = GetCurrentThreadPointer(m_kernel);
|
||||
KThread* cur_thread = GetCurrentThreadPointer(m_system.Kernel());
|
||||
KHardwareTimer* timer{};
|
||||
ThreadQueueImplForKConditionVariableWaitConditionVariable wait_queue(m_kernel,
|
||||
ThreadQueueImplForKConditionVariableWaitConditionVariable wait_queue(m_system.Kernel(),
|
||||
std::addressof(m_tree));
|
||||
|
||||
{
|
||||
KScopedSchedulerLockAndSleep slp(m_kernel, std::addressof(timer), cur_thread, timeout);
|
||||
KScopedSchedulerLockAndSleep slp(m_system.Kernel(), std::addressof(timer), cur_thread, timeout);
|
||||
|
||||
// Check that the thread isn't terminating.
|
||||
if (cur_thread->IsTerminationRequested()) {
|
||||
@@ -291,7 +289,7 @@ Result KConditionVariable::Wait(KProcessAddress addr, u64 key, u32 value, s64 ti
|
||||
// Remove waiter thread.
|
||||
bool has_waiters{};
|
||||
KThread* next_owner_thread =
|
||||
cur_thread->RemoveUserWaiterByKey(std::addressof(has_waiters), addr);
|
||||
cur_thread->RemoveUserWaiterByKey(m_system.Kernel(), std::addressof(has_waiters), addr);
|
||||
|
||||
// Update for the next owner thread.
|
||||
u32 next_value{};
|
||||
@@ -303,18 +301,18 @@ Result KConditionVariable::Wait(KProcessAddress addr, u64 key, u32 value, s64 ti
|
||||
}
|
||||
|
||||
// Wake up the next owner.
|
||||
next_owner_thread->EndWait(ResultSuccess);
|
||||
next_owner_thread->EndWait(m_system.Kernel(), ResultSuccess);
|
||||
}
|
||||
|
||||
// Write to the cv key.
|
||||
{
|
||||
constexpr u32 HasWaiterFlag = 1;
|
||||
WriteToUser(m_kernel, key, HasWaiterFlag);
|
||||
WriteToUser(m_system.Kernel(), key, HasWaiterFlag);
|
||||
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||
}
|
||||
|
||||
// Write the value to userspace.
|
||||
if (!WriteToUser(m_kernel, addr, next_value)) {
|
||||
if (!WriteToUser(m_system.Kernel(), addr, next_value)) {
|
||||
slp.CancelSleep();
|
||||
R_THROW(ResultInvalidCurrentMemory);
|
||||
}
|
||||
@@ -329,7 +327,7 @@ Result KConditionVariable::Wait(KProcessAddress addr, u64 key, u32 value, s64 ti
|
||||
|
||||
// Begin waiting.
|
||||
wait_queue.SetHardwareTimer(timer);
|
||||
cur_thread->BeginWait(std::addressof(wait_queue));
|
||||
cur_thread->BeginWait(m_system.Kernel(), std::addressof(wait_queue));
|
||||
cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -26,19 +29,17 @@ public:
|
||||
|
||||
// Arbitration.
|
||||
static Result SignalToAddress(KernelCore& kernel, KProcessAddress addr);
|
||||
static Result WaitForAddress(KernelCore& kernel, Handle handle, KProcessAddress addr,
|
||||
u32 value);
|
||||
static Result WaitForAddress(KernelCore& kernel, Handle handle, KProcessAddress addr, u32 value);
|
||||
|
||||
// Condition variable.
|
||||
void Signal(u64 cv_key, s32 count);
|
||||
Result Wait(KProcessAddress addr, u64 key, u32 value, s64 timeout);
|
||||
|
||||
private:
|
||||
void SignalImpl(KThread* thread);
|
||||
void SignalImpl(KernelCore& kernel, KThread* thread);
|
||||
|
||||
private:
|
||||
Core::System& m_system;
|
||||
KernelCore& m_kernel;
|
||||
ThreadTree m_tree{};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -14,7 +17,7 @@ class KDebug final : public KAutoObjectWithSlabHeapAndContainer<KDebug, KAutoObj
|
||||
public:
|
||||
explicit KDebug(KernelCore& kernel) : KAutoObjectWithSlabHeapAndContainer{kernel} {}
|
||||
|
||||
static void PostDestroy(uintptr_t arg) {}
|
||||
static void PostDestroy(KernelCore& kernel, uintptr_t arg) {}
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -13,13 +16,13 @@ KDeviceAddressSpace::KDeviceAddressSpace(KernelCore& kernel)
|
||||
: KAutoObjectWithSlabHeapAndContainer(kernel), m_lock(kernel), m_is_initialized(false) {}
|
||||
KDeviceAddressSpace::~KDeviceAddressSpace() = default;
|
||||
|
||||
void KDeviceAddressSpace::Initialize() {
|
||||
void KDeviceAddressSpace::Initialize(KernelCore& kernel) {
|
||||
// This just forwards to the device page table manager.
|
||||
// KDevicePageTable::Initialize();
|
||||
}
|
||||
|
||||
// Member functions.
|
||||
Result KDeviceAddressSpace::Initialize(u64 address, u64 size) {
|
||||
Result KDeviceAddressSpace::Initialize(KernelCore& kernel, u64 address, u64 size) {
|
||||
// Initialize the device page table.
|
||||
// R_TRY(m_table.Initialize(address, size));
|
||||
|
||||
@@ -31,7 +34,7 @@ Result KDeviceAddressSpace::Initialize(u64 address, u64 size) {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void KDeviceAddressSpace::Finalize() {
|
||||
void KDeviceAddressSpace::Finalize(KernelCore& kernel) {
|
||||
// Finalize the table.
|
||||
// m_table.Finalize();
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -20,13 +23,13 @@ public:
|
||||
explicit KDeviceAddressSpace(KernelCore& kernel);
|
||||
~KDeviceAddressSpace();
|
||||
|
||||
Result Initialize(u64 address, u64 size);
|
||||
void Finalize() override;
|
||||
Result Initialize(KernelCore& kernel, u64 address, u64 size);
|
||||
void Finalize(KernelCore& kernel) override;
|
||||
|
||||
bool IsInitialized() const override {
|
||||
return m_is_initialized;
|
||||
}
|
||||
static void PostDestroy(uintptr_t arg) {}
|
||||
static void PostDestroy(KernelCore& kernel, uintptr_t arg) {}
|
||||
|
||||
Result Attach(Svc::DeviceName device_name);
|
||||
Result Detach(Svc::DeviceName device_name);
|
||||
@@ -44,7 +47,7 @@ public:
|
||||
Result Unmap(KProcessPageTable* page_table, KProcessAddress process_address, size_t size,
|
||||
u64 device_address);
|
||||
|
||||
static void Initialize();
|
||||
static void Initialize(KernelCore& kernel);
|
||||
|
||||
private:
|
||||
Result Map(KProcessPageTable* page_table, KProcessAddress process_address, size_t size,
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -8,56 +11,58 @@
|
||||
namespace Kernel {
|
||||
|
||||
KEvent::KEvent(KernelCore& kernel)
|
||||
: KAutoObjectWithSlabHeapAndContainer{kernel}, m_readable_event{kernel} {}
|
||||
: KAutoObjectWithSlabHeapAndContainer{kernel}
|
||||
, m_readable_event{kernel}
|
||||
{}
|
||||
|
||||
KEvent::~KEvent() = default;
|
||||
|
||||
void KEvent::Initialize(KProcess* owner) {
|
||||
void KEvent::Initialize(KernelCore& kernel, KProcess* owner) {
|
||||
// Create our readable event.
|
||||
KAutoObject::Create(std::addressof(m_readable_event));
|
||||
|
||||
// Initialize our readable event.
|
||||
m_readable_event.Initialize(this);
|
||||
m_readable_event.Initialize(kernel, this);
|
||||
|
||||
// Set our owner process.
|
||||
// HACK: this should never be nullptr, but service threads don't have a
|
||||
// proper parent process yet.
|
||||
if (owner != nullptr) {
|
||||
m_owner = owner;
|
||||
m_owner->Open();
|
||||
m_owner->Open(kernel);
|
||||
}
|
||||
|
||||
// Mark initialized.
|
||||
m_initialized = true;
|
||||
}
|
||||
|
||||
void KEvent::Finalize() {
|
||||
KAutoObjectWithSlabHeapAndContainer<KEvent, KAutoObjectWithList>::Finalize();
|
||||
void KEvent::Finalize(KernelCore& kernel) {
|
||||
KAutoObjectWithSlabHeapAndContainer<KEvent, KAutoObjectWithList>::Finalize(kernel);
|
||||
}
|
||||
|
||||
Result KEvent::Signal() {
|
||||
KScopedSchedulerLock sl{m_kernel};
|
||||
Result KEvent::Signal(KernelCore& kernel) {
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
|
||||
R_SUCCEED_IF(m_readable_event_destroyed);
|
||||
|
||||
return m_readable_event.Signal();
|
||||
return m_readable_event.Signal(kernel);
|
||||
}
|
||||
|
||||
Result KEvent::Clear() {
|
||||
KScopedSchedulerLock sl{m_kernel};
|
||||
Result KEvent::Clear(KernelCore& kernel) {
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
|
||||
R_SUCCEED_IF(m_readable_event_destroyed);
|
||||
|
||||
return m_readable_event.Clear();
|
||||
return m_readable_event.Clear(kernel);
|
||||
}
|
||||
|
||||
void KEvent::PostDestroy(uintptr_t arg) {
|
||||
void KEvent::PostDestroy(KernelCore& kernel, uintptr_t arg) {
|
||||
// Release the event count resource the owner process holds.
|
||||
KProcess* owner = reinterpret_cast<KProcess*>(arg);
|
||||
|
||||
if (owner != nullptr) {
|
||||
owner->GetResourceLimit()->Release(LimitableResource::EventCountMax, 1);
|
||||
owner->Close();
|
||||
owner->GetResourceLimit()->Release(kernel, LimitableResource::EventCountMax, 1);
|
||||
owner->Close(kernel);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -19,9 +22,9 @@ public:
|
||||
explicit KEvent(KernelCore& kernel);
|
||||
~KEvent() override;
|
||||
|
||||
void Initialize(KProcess* owner);
|
||||
void Initialize(KernelCore& kernel, KProcess* owner);
|
||||
|
||||
void Finalize() override;
|
||||
void Finalize(KernelCore& kernel) override;
|
||||
|
||||
bool IsInitialized() const override {
|
||||
return m_initialized;
|
||||
@@ -39,10 +42,10 @@ public:
|
||||
return m_readable_event;
|
||||
}
|
||||
|
||||
static void PostDestroy(uintptr_t arg);
|
||||
static void PostDestroy(KernelCore& kernel, uintptr_t arg);
|
||||
|
||||
Result Signal();
|
||||
Result Clear();
|
||||
Result Signal(KernelCore& kernel);
|
||||
Result Clear(KernelCore& kernel);
|
||||
|
||||
void OnReadableEventDestroyed() {
|
||||
m_readable_event_destroyed = true;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
@@ -6,28 +6,27 @@
|
||||
|
||||
#include "core/hle/kernel/k_handle_table.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
void KHandleTable::Finalize() {
|
||||
void KHandleTable::Finalize(KernelCore& kernel) {
|
||||
// Get the table and clear our record of it.
|
||||
u16 saved_table_size = 0;
|
||||
{
|
||||
KScopedDisableDispatch dd{m_kernel};
|
||||
KScopedDisableDispatch dd{kernel};
|
||||
KScopedSpinLock lk(m_lock);
|
||||
|
||||
std::swap(m_table_size, saved_table_size);
|
||||
}
|
||||
|
||||
// Close and free all entries.
|
||||
for (size_t i = 0; i < saved_table_size; i++) {
|
||||
if (KAutoObject* obj = m_objects[i]; obj != nullptr) {
|
||||
obj->Close();
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < saved_table_size; i++)
|
||||
if (KAutoObject* obj = m_objects[i]; obj != nullptr)
|
||||
obj->Close(kernel);
|
||||
}
|
||||
|
||||
bool KHandleTable::Remove(Handle handle) {
|
||||
bool KHandleTable::Remove(KernelCore& kernel, Handle handle) {
|
||||
// Don't allow removal of a pseudo-handle.
|
||||
if (Svc::IsPseudoHandle(handle)) [[unlikely]] {
|
||||
return false;
|
||||
@@ -42,7 +41,7 @@ bool KHandleTable::Remove(Handle handle) {
|
||||
// Find the object and free the entry.
|
||||
KAutoObject* obj = nullptr;
|
||||
{
|
||||
KScopedDisableDispatch dd{m_kernel};
|
||||
KScopedDisableDispatch dd{kernel};
|
||||
KScopedSpinLock lk(m_lock);
|
||||
|
||||
if (this->IsValidHandle(handle)) [[likely]] {
|
||||
@@ -56,13 +55,13 @@ bool KHandleTable::Remove(Handle handle) {
|
||||
}
|
||||
|
||||
// Close the object.
|
||||
m_kernel.UnregisterInUseObject(obj);
|
||||
obj->Close();
|
||||
kernel.UnregisterInUseObject(obj);
|
||||
obj->Close(kernel);
|
||||
return true;
|
||||
}
|
||||
|
||||
Result KHandleTable::Add(Handle* out_handle, KAutoObject* obj) {
|
||||
KScopedDisableDispatch dd{m_kernel};
|
||||
Result KHandleTable::Add(KernelCore& kernel, Handle* out_handle, KAutoObject* obj) {
|
||||
KScopedDisableDispatch dd{kernel};
|
||||
KScopedSpinLock lk(m_lock);
|
||||
|
||||
// Never exceed our capacity.
|
||||
@@ -76,7 +75,7 @@ Result KHandleTable::Add(Handle* out_handle, KAutoObject* obj) {
|
||||
m_entry_infos[index].linear_id = linear_id;
|
||||
m_objects[index] = obj;
|
||||
|
||||
obj->Open();
|
||||
obj->Open(kernel);
|
||||
|
||||
*out_handle = EncodeHandle(static_cast<u16>(index), linear_id);
|
||||
}
|
||||
@@ -84,24 +83,22 @@ Result KHandleTable::Add(Handle* out_handle, KAutoObject* obj) {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
KScopedAutoObject<KAutoObject> KHandleTable::GetObjectForIpc(Handle handle,
|
||||
KThread* cur_thread) const {
|
||||
KScopedAutoObject<KAutoObject> KHandleTable::GetObjectForIpc(KernelCore& kernel, Handle handle, KThread* cur_thread) const {
|
||||
// Handle pseudo-handles.
|
||||
ASSERT(cur_thread != nullptr);
|
||||
if (handle == Svc::PseudoHandle::CurrentProcess) {
|
||||
auto* const cur_process = cur_thread->GetOwnerProcess();
|
||||
ASSERT(cur_process != nullptr);
|
||||
return cur_process;
|
||||
return {kernel, cur_process};
|
||||
}
|
||||
if (handle == Svc::PseudoHandle::CurrentThread) {
|
||||
return cur_thread;
|
||||
return {kernel, cur_thread};
|
||||
}
|
||||
|
||||
return GetObjectForIpcWithoutPseudoHandle(handle);
|
||||
return GetObjectForIpcWithoutPseudoHandle(kernel, handle);
|
||||
}
|
||||
|
||||
Result KHandleTable::Reserve(Handle* out_handle) {
|
||||
KScopedDisableDispatch dd{m_kernel};
|
||||
Result KHandleTable::Reserve(KernelCore& kernel, Handle* out_handle) {
|
||||
KScopedDisableDispatch dd{kernel};
|
||||
KScopedSpinLock lk(m_lock);
|
||||
|
||||
// Never exceed our capacity.
|
||||
@@ -111,8 +108,8 @@ Result KHandleTable::Reserve(Handle* out_handle) {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void KHandleTable::Unreserve(Handle handle) {
|
||||
KScopedDisableDispatch dd{m_kernel};
|
||||
void KHandleTable::Unreserve(KernelCore& kernel, Handle handle) {
|
||||
KScopedDisableDispatch dd{kernel};
|
||||
KScopedSpinLock lk(m_lock);
|
||||
|
||||
// Unpack the handle.
|
||||
@@ -130,8 +127,8 @@ void KHandleTable::Unreserve(Handle handle) {
|
||||
}
|
||||
}
|
||||
|
||||
void KHandleTable::Register(Handle handle, KAutoObject* obj) {
|
||||
KScopedDisableDispatch dd{m_kernel};
|
||||
void KHandleTable::Register(KernelCore& kernel, Handle handle, KAutoObject* obj) {
|
||||
KScopedDisableDispatch dd{kernel};
|
||||
KScopedSpinLock lk(m_lock);
|
||||
|
||||
// Unpack the handle.
|
||||
@@ -149,7 +146,7 @@ void KHandleTable::Register(Handle handle, KAutoObject* obj) {
|
||||
m_entry_infos[index].linear_id = static_cast<u16>(linear_id);
|
||||
m_objects[index] = obj;
|
||||
|
||||
obj->Open();
|
||||
obj->Open(kernel);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
@@ -31,14 +31,14 @@ public:
|
||||
static constexpr size_t MaxTableSize = 1024;
|
||||
|
||||
public:
|
||||
explicit KHandleTable(KernelCore& kernel) : m_kernel(kernel) {}
|
||||
explicit KHandleTable(KernelCore& kernel) {}
|
||||
|
||||
Result Initialize(s32 size) {
|
||||
Result Initialize(KernelCore& kernel, s32 size) {
|
||||
// Check that the table size is valid.
|
||||
R_UNLESS(size <= static_cast<s32>(MaxTableSize), ResultOutOfMemory);
|
||||
|
||||
// Lock.
|
||||
KScopedDisableDispatch dd{m_kernel};
|
||||
KScopedDisableDispatch dd{kernel};
|
||||
KScopedSpinLock lk(m_lock);
|
||||
|
||||
// Initialize all fields.
|
||||
@@ -68,76 +68,72 @@ public:
|
||||
return m_max_count;
|
||||
}
|
||||
|
||||
void Finalize();
|
||||
bool Remove(Handle handle);
|
||||
void Finalize(KernelCore& kernel);
|
||||
bool Remove(KernelCore& kernel, Handle handle);
|
||||
|
||||
template <typename T = KAutoObject>
|
||||
KScopedAutoObject<T> GetObjectWithoutPseudoHandle(Handle handle) const {
|
||||
KScopedAutoObject<T> GetObjectWithoutPseudoHandle(KernelCore& kernel, Handle handle) const {
|
||||
// Lock and look up in table.
|
||||
KScopedDisableDispatch dd{m_kernel};
|
||||
KScopedDisableDispatch dd{kernel};
|
||||
KScopedSpinLock lk(m_lock);
|
||||
|
||||
if constexpr (std::is_same_v<T, KAutoObject>) {
|
||||
return this->GetObjectImpl(handle);
|
||||
return {kernel, this->GetObjectImpl(handle)};
|
||||
} else {
|
||||
if (auto* obj = this->GetObjectImpl(handle); obj != nullptr) [[likely]] {
|
||||
return obj->DynamicCast<T*>();
|
||||
return {kernel, obj->DynamicCast<T*>()};
|
||||
} else {
|
||||
return nullptr;
|
||||
return {kernel, nullptr};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T = KAutoObject>
|
||||
KScopedAutoObject<T> GetObject(Handle handle) const {
|
||||
KScopedAutoObject<T> GetObject(KernelCore& kernel, Handle handle) const {
|
||||
// Handle pseudo-handles.
|
||||
if constexpr (std::derived_from<KProcess, T>) {
|
||||
if (handle == Svc::PseudoHandle::CurrentProcess) {
|
||||
auto* const cur_process = GetCurrentProcessPointer(m_kernel);
|
||||
auto* const cur_process = GetCurrentProcessPointer(kernel);
|
||||
ASSERT(cur_process != nullptr);
|
||||
return cur_process;
|
||||
return {kernel, cur_process};
|
||||
}
|
||||
} else if constexpr (std::derived_from<KThread, T>) {
|
||||
if (handle == Svc::PseudoHandle::CurrentThread) {
|
||||
auto* const cur_thread = GetCurrentThreadPointer(m_kernel);
|
||||
auto* const cur_thread = GetCurrentThreadPointer(kernel);
|
||||
ASSERT(cur_thread != nullptr);
|
||||
return cur_thread;
|
||||
return {kernel, cur_thread};
|
||||
}
|
||||
}
|
||||
|
||||
return this->template GetObjectWithoutPseudoHandle<T>(handle);
|
||||
return this->template GetObjectWithoutPseudoHandle<T>(kernel, handle);
|
||||
}
|
||||
|
||||
KScopedAutoObject<KAutoObject> GetObjectForIpcWithoutPseudoHandle(Handle handle) const {
|
||||
KScopedAutoObject<KAutoObject> GetObjectForIpcWithoutPseudoHandle(KernelCore& kernel, Handle handle) const {
|
||||
// Lock and look up in table.
|
||||
KScopedDisableDispatch dd{m_kernel};
|
||||
KScopedDisableDispatch dd{kernel};
|
||||
KScopedSpinLock lk(m_lock);
|
||||
return {kernel, this->GetObjectImpl(handle)};
|
||||
}
|
||||
KScopedAutoObject<KAutoObject> GetObjectForIpc(KernelCore& kernel, Handle handle, KThread* cur_thread) const;
|
||||
KScopedAutoObject<KAutoObject> GetObjectByIndex(KernelCore& kernel, Handle* out_handle, size_t index) const {
|
||||
KScopedDisableDispatch dd{kernel};
|
||||
KScopedSpinLock lk(m_lock);
|
||||
|
||||
return this->GetObjectImpl(handle);
|
||||
return {kernel, this->GetObjectByIndexImpl(out_handle, index)};
|
||||
}
|
||||
|
||||
KScopedAutoObject<KAutoObject> GetObjectForIpc(Handle handle, KThread* cur_thread) const;
|
||||
Result Reserve(KernelCore& kernel, Handle* out_handle);
|
||||
void Unreserve(KernelCore& kernel, Handle handle);
|
||||
|
||||
KScopedAutoObject<KAutoObject> GetObjectByIndex(Handle* out_handle, size_t index) const {
|
||||
KScopedDisableDispatch dd{m_kernel};
|
||||
KScopedSpinLock lk(m_lock);
|
||||
|
||||
return this->GetObjectByIndexImpl(out_handle, index);
|
||||
}
|
||||
|
||||
Result Reserve(Handle* out_handle);
|
||||
void Unreserve(Handle handle);
|
||||
|
||||
Result Add(Handle* out_handle, KAutoObject* obj);
|
||||
void Register(Handle handle, KAutoObject* obj);
|
||||
Result Add(KernelCore& kernel, Handle* out_handle, KAutoObject* obj);
|
||||
void Register(KernelCore& kernel, Handle handle, KAutoObject* obj);
|
||||
|
||||
template <typename T>
|
||||
bool GetMultipleObjects(T** out, const Handle* handles, size_t num_handles) const {
|
||||
bool GetMultipleObjects(KernelCore& kernel, T** out, const Handle* handles, size_t num_handles) const {
|
||||
// Try to convert and open all the handles.
|
||||
size_t num_opened;
|
||||
{
|
||||
// Lock the table.
|
||||
KScopedDisableDispatch dd{m_kernel};
|
||||
KScopedDisableDispatch dd{kernel};
|
||||
KScopedSpinLock lk(m_lock);
|
||||
for (num_opened = 0; num_opened < num_handles; num_opened++) {
|
||||
// Get the current handle.
|
||||
@@ -150,14 +146,14 @@ public:
|
||||
}
|
||||
|
||||
// Cast the current object to the desired type.
|
||||
T* cur_t = cur_object->DynamicCast<T*>();
|
||||
if (cur_t == nullptr) [[unlikely]] {
|
||||
T* cur_thread = cur_object->DynamicCast<T*>();
|
||||
if (cur_thread == nullptr) [[unlikely]] {
|
||||
break;
|
||||
}
|
||||
|
||||
// Open a reference to the current object.
|
||||
cur_t->Open();
|
||||
out[num_opened] = cur_t;
|
||||
cur_thread->Open(kernel);
|
||||
out[num_opened] = cur_thread;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,7 +164,7 @@ public:
|
||||
|
||||
// If we didn't convert entry object, close the ones we opened.
|
||||
for (size_t i = 0; i < num_opened; i++) {
|
||||
out[i]->Close();
|
||||
out[i]->Close(kernel);
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -177,13 +173,9 @@ public:
|
||||
private:
|
||||
s32 AllocateEntry() {
|
||||
ASSERT(m_count < m_table_size);
|
||||
|
||||
const auto index = m_free_head_index;
|
||||
|
||||
m_free_head_index = m_entry_infos[index].GetNextFreeIndex();
|
||||
|
||||
m_max_count = (std::max)(m_max_count, ++m_count);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
@@ -302,7 +294,6 @@ private:
|
||||
};
|
||||
|
||||
private:
|
||||
KernelCore& m_kernel;
|
||||
std::array<EntryInfo, MaxTableSize> m_entry_infos{};
|
||||
std::array<KAutoObject*, MaxTableSize> m_objects{};
|
||||
mutable KSpinLock m_lock;
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -45,7 +48,7 @@ protected:
|
||||
this->RemoveTaskFromTree(task);
|
||||
|
||||
// Handle the task.
|
||||
task->OnTimer();
|
||||
task->OnTimer(m_kernel);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -18,19 +21,19 @@ void HandleInterrupt(KernelCore& kernel, s32 core_id) {
|
||||
|
||||
if (auto* process = GetCurrentProcessPointer(kernel); process) {
|
||||
// If the user disable count is set, we may need to pin the current thread.
|
||||
if (current_thread.GetUserDisableCount() && !process->GetPinnedThread(core_id)) {
|
||||
if (current_thread.GetUserDisableCount(kernel) && !process->GetPinnedThread(core_id)) {
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
|
||||
// Pin the current thread.
|
||||
process->PinCurrentThread();
|
||||
process->PinCurrentThread(kernel);
|
||||
|
||||
// Set the interrupt flag for the thread.
|
||||
GetCurrentThread(kernel).SetInterruptFlag();
|
||||
GetCurrentThread(kernel).SetInterruptFlag(kernel);
|
||||
}
|
||||
}
|
||||
|
||||
// Request interrupt scheduling.
|
||||
kernel.CurrentScheduler()->RequestScheduleOnInterrupt();
|
||||
kernel.CurrentScheduler()->RequestScheduleOnInterrupt(kernel);
|
||||
}
|
||||
|
||||
void SendInterProcessorInterrupt(KernelCore& kernel, u64 core_mask) {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -11,21 +14,21 @@ KLightClientSession::KLightClientSession(KernelCore& kernel) : KAutoObject(kerne
|
||||
|
||||
KLightClientSession::~KLightClientSession() = default;
|
||||
|
||||
void KLightClientSession::Destroy() {
|
||||
m_parent->OnClientClosed();
|
||||
void KLightClientSession::Destroy(KernelCore& kernel) {
|
||||
m_parent->OnClientClosed(kernel);
|
||||
}
|
||||
|
||||
void KLightClientSession::OnServerClosed() {}
|
||||
void KLightClientSession::OnServerClosed(KernelCore& kernel) {}
|
||||
|
||||
Result KLightClientSession::SendSyncRequest(u32* data) {
|
||||
Result KLightClientSession::SendSyncRequest(KernelCore& kernel, u32* data) {
|
||||
// Get the request thread.
|
||||
KThread* cur_thread = GetCurrentThreadPointer(m_kernel);
|
||||
KThread* cur_thread = GetCurrentThreadPointer(kernel);
|
||||
|
||||
// Set the light data.
|
||||
cur_thread->SetLightSessionData(data);
|
||||
|
||||
// Send the request.
|
||||
R_RETURN(m_parent->OnRequest(cur_thread));
|
||||
R_RETURN(m_parent->OnRequest(kernel, cur_thread));
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -22,15 +25,15 @@ public:
|
||||
m_parent = parent;
|
||||
}
|
||||
|
||||
virtual void Destroy() override;
|
||||
virtual void Destroy(KernelCore& kernel) override;
|
||||
|
||||
const KLightSession* GetParent() const {
|
||||
return m_parent;
|
||||
}
|
||||
|
||||
Result SendSyncRequest(u32* data);
|
||||
Result SendSyncRequest(KernelCore& kernel, u32* data);
|
||||
|
||||
void OnServerClosed();
|
||||
void OnServerClosed(KernelCore& kernel);
|
||||
|
||||
private:
|
||||
KLightSession* m_parent;
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -13,11 +16,13 @@ namespace {
|
||||
|
||||
class ThreadQueueImplForKLightConditionVariable final : public KThreadQueue {
|
||||
public:
|
||||
ThreadQueueImplForKLightConditionVariable(KernelCore& kernel, KThread::WaiterList* wl,
|
||||
bool term)
|
||||
: KThreadQueue(kernel), m_wait_list(wl), m_allow_terminating_thread(term) {}
|
||||
ThreadQueueImplForKLightConditionVariable(KernelCore& kernel, KThread::WaiterList* wl, bool term)
|
||||
: KThreadQueue(kernel)
|
||||
, m_wait_list(wl)
|
||||
, m_allow_terminating_thread(term)
|
||||
{}
|
||||
|
||||
void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override {
|
||||
virtual void CancelWait(KernelCore& kernel, KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override {
|
||||
// Only process waits if we're allowed to.
|
||||
if (ResultTerminationRequested == wait_result && m_allow_terminating_thread) {
|
||||
return;
|
||||
@@ -27,7 +32,7 @@ public:
|
||||
m_wait_list->erase(m_wait_list->iterator_to(*waiting_thread));
|
||||
|
||||
// Invoke the base cancel wait handler.
|
||||
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
|
||||
KThreadQueue::CancelWait(kernel, waiting_thread, wait_result, cancel_timer_task);
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -61,7 +66,7 @@ void KLightConditionVariable::Wait(KLightLock* lock, s64 timeout, bool allow_ter
|
||||
|
||||
// Begin waiting.
|
||||
wait_queue.SetHardwareTimer(timer);
|
||||
owner->BeginWait(std::addressof(wait_queue));
|
||||
owner->BeginWait(m_kernel, std::addressof(wait_queue));
|
||||
}
|
||||
|
||||
// Re-acquire the lock.
|
||||
@@ -73,7 +78,7 @@ void KLightConditionVariable::Broadcast() {
|
||||
|
||||
// Signal all threads.
|
||||
for (auto it = m_wait_list.begin(); it != m_wait_list.end(); it = m_wait_list.erase(it)) {
|
||||
it->EndWait(ResultSuccess);
|
||||
it->EndWait(m_kernel, ResultSuccess);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -15,29 +18,25 @@ class ThreadQueueImplForKLightLock final : public KThreadQueue {
|
||||
public:
|
||||
explicit ThreadQueueImplForKLightLock(KernelCore& kernel) : KThreadQueue(kernel) {}
|
||||
|
||||
void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override {
|
||||
void CancelWait(KernelCore& kernel, KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override {
|
||||
// Remove the thread as a waiter from its owner.
|
||||
if (KThread* owner = waiting_thread->GetLockOwner(); owner != nullptr) {
|
||||
owner->RemoveWaiter(waiting_thread);
|
||||
if (KThread* owner = waiting_thread->GetLockOwner(kernel); owner != nullptr) {
|
||||
owner->RemoveWaiter(kernel, waiting_thread);
|
||||
}
|
||||
|
||||
// Invoke the base cancel wait handler.
|
||||
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
|
||||
KThreadQueue::CancelWait(kernel, waiting_thread, wait_result, cancel_timer_task);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
void KLightLock::Lock() {
|
||||
const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(m_kernel));
|
||||
|
||||
const uintptr_t cur_thread = uintptr_t(GetCurrentThreadPointer(m_kernel));
|
||||
while (true) {
|
||||
uintptr_t old_tag = m_tag.load(std::memory_order_relaxed);
|
||||
|
||||
while (!m_tag.compare_exchange_weak(old_tag, (old_tag == 0) ? cur_thread : (old_tag | 1),
|
||||
std::memory_order_acquire)) {
|
||||
}
|
||||
|
||||
while (!m_tag.compare_exchange_weak(old_tag, (old_tag == 0) ? cur_thread : (old_tag | 1), std::memory_order_acquire))
|
||||
;
|
||||
if (old_tag == 0 || this->LockSlowPath(old_tag | 1, cur_thread)) {
|
||||
break;
|
||||
}
|
||||
@@ -69,13 +68,13 @@ bool KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) {
|
||||
// Add the current thread as a waiter on the owner.
|
||||
KThread* owner_thread = reinterpret_cast<KThread*>(_owner & ~1ULL);
|
||||
cur_thread->SetKernelAddressKey(reinterpret_cast<uintptr_t>(std::addressof(m_tag)));
|
||||
owner_thread->AddWaiter(cur_thread);
|
||||
owner_thread->AddWaiter(m_kernel, cur_thread);
|
||||
|
||||
// Begin waiting to hold the lock.
|
||||
cur_thread->BeginWait(std::addressof(wait_queue));
|
||||
cur_thread->BeginWait(m_kernel, std::addressof(wait_queue));
|
||||
|
||||
if (owner_thread->IsSuspended()) {
|
||||
owner_thread->ContinueIfHasKernelWaiters();
|
||||
owner_thread->ContinueIfHasKernelWaiters(m_kernel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,26 +90,25 @@ void KLightLock::UnlockSlowPath(uintptr_t _cur_thread) {
|
||||
|
||||
// Get the next owner.
|
||||
bool has_waiters;
|
||||
KThread* next_owner = owner_thread->RemoveKernelWaiterByKey(
|
||||
std::addressof(has_waiters), reinterpret_cast<uintptr_t>(std::addressof(m_tag)));
|
||||
KThread* next_owner = owner_thread->RemoveKernelWaiterByKey(m_kernel,
|
||||
std::addressof(has_waiters), uintptr_t(std::addressof(m_tag)));
|
||||
|
||||
// Pass the lock to the next owner.
|
||||
uintptr_t next_tag = 0;
|
||||
if (next_owner != nullptr) {
|
||||
next_tag =
|
||||
reinterpret_cast<uintptr_t>(next_owner) | static_cast<uintptr_t>(has_waiters);
|
||||
next_tag = uintptr_t(next_owner) | uintptr_t(has_waiters);
|
||||
|
||||
next_owner->EndWait(ResultSuccess);
|
||||
next_owner->EndWait(m_kernel, ResultSuccess);
|
||||
|
||||
if (next_owner->IsSuspended()) {
|
||||
next_owner->ContinueIfHasKernelWaiters();
|
||||
next_owner->ContinueIfHasKernelWaiters(m_kernel);
|
||||
}
|
||||
}
|
||||
|
||||
// We may have unsuspended in the process of acquiring the lock, so we'll re-suspend now if
|
||||
// so.
|
||||
if (owner_thread->IsSuspended()) {
|
||||
owner_thread->TrySuspend();
|
||||
owner_thread->TrySuspend(m_kernel);
|
||||
}
|
||||
|
||||
// Write the new tag value.
|
||||
@@ -119,8 +117,7 @@ void KLightLock::UnlockSlowPath(uintptr_t _cur_thread) {
|
||||
}
|
||||
|
||||
bool KLightLock::IsLockedByCurrentThread() const {
|
||||
return (m_tag.load() | 1ULL) ==
|
||||
(reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(m_kernel)) | 1ULL);
|
||||
return (m_tag.load() | 1ULL) == (uintptr_t(GetCurrentThreadPointer(m_kernel)) | 1ULL);
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
@@ -24,21 +24,20 @@ public:
|
||||
ThreadQueueImplForKLightServerSessionRequest(KernelCore& kernel, KThread::WaiterList* wl)
|
||||
: KThreadQueue(kernel), m_wait_list(wl) {}
|
||||
|
||||
virtual void EndWait(KThread* waiting_thread, Result wait_result) override {
|
||||
virtual void EndWait(KernelCore& kernel, KThread* waiting_thread, Result wait_result) override {
|
||||
// Remove the thread from our wait list.
|
||||
m_wait_list->erase(m_wait_list->iterator_to(*waiting_thread));
|
||||
|
||||
// Invoke the base end wait handler.
|
||||
KThreadQueue::EndWait(waiting_thread, wait_result);
|
||||
KThreadQueue::EndWait(kernel, waiting_thread, wait_result);
|
||||
}
|
||||
|
||||
virtual void CancelWait(KThread* waiting_thread, Result wait_result,
|
||||
bool cancel_timer_task) override {
|
||||
virtual void CancelWait(KernelCore& kernel, KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override {
|
||||
// Remove the thread from our wait list.
|
||||
m_wait_list->erase(m_wait_list->iterator_to(*waiting_thread));
|
||||
|
||||
// Invoke the base cancel wait handler.
|
||||
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
|
||||
KThreadQueue::CancelWait(kernel, waiting_thread, wait_result, cancel_timer_task);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -48,9 +47,11 @@ private:
|
||||
|
||||
public:
|
||||
ThreadQueueImplForKLightServerSessionReceive(KernelCore& kernel, KThread** st)
|
||||
: KThreadQueue(kernel), m_server_thread(st) {}
|
||||
: KThreadQueue(kernel)
|
||||
, m_server_thread(st)
|
||||
{}
|
||||
|
||||
virtual void EndWait(KThread* waiting_thread, Result wait_result) override {
|
||||
virtual void EndWait(KernelCore& kernel, KThread* waiting_thread, Result wait_result) override {
|
||||
// Clear the server thread.
|
||||
*m_server_thread = nullptr;
|
||||
|
||||
@@ -58,11 +59,10 @@ public:
|
||||
waiting_thread->ClearCancellable();
|
||||
|
||||
// Invoke the base end wait handler.
|
||||
KThreadQueue::EndWait(waiting_thread, wait_result);
|
||||
KThreadQueue::EndWait(kernel, waiting_thread, wait_result);
|
||||
}
|
||||
|
||||
virtual void CancelWait(KThread* waiting_thread, Result wait_result,
|
||||
bool cancel_timer_task) override {
|
||||
virtual void CancelWait(KernelCore& kernel, KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override {
|
||||
// Clear the server thread.
|
||||
*m_server_thread = nullptr;
|
||||
|
||||
@@ -70,7 +70,7 @@ public:
|
||||
waiting_thread->ClearCancellable();
|
||||
|
||||
// Invoke the base cancel wait handler.
|
||||
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
|
||||
KThreadQueue::CancelWait(kernel, waiting_thread, wait_result, cancel_timer_task);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -79,24 +79,21 @@ public:
|
||||
KLightServerSession::KLightServerSession(KernelCore& kernel) : KAutoObject(kernel) {}
|
||||
KLightServerSession::~KLightServerSession() = default;
|
||||
|
||||
void KLightServerSession::Destroy() {
|
||||
this->CleanupRequests();
|
||||
|
||||
m_parent->OnServerClosed();
|
||||
void KLightServerSession::Destroy(KernelCore& kernel) {
|
||||
this->CleanupRequests(kernel);
|
||||
m_parent->OnServerClosed(kernel);
|
||||
}
|
||||
|
||||
void KLightServerSession::OnClientClosed() {
|
||||
this->CleanupRequests();
|
||||
void KLightServerSession::OnClientClosed(KernelCore& kernel) {
|
||||
this->CleanupRequests(kernel);
|
||||
}
|
||||
|
||||
Result KLightServerSession::OnRequest(KThread* request_thread) {
|
||||
ThreadQueueImplForKLightServerSessionRequest wait_queue(m_kernel,
|
||||
std::addressof(m_request_list));
|
||||
|
||||
Result KLightServerSession::OnRequest(KernelCore& kernel, KThread* request_thread) {
|
||||
ThreadQueueImplForKLightServerSessionRequest wait_queue(kernel, std::addressof(m_request_list));
|
||||
// Send the request.
|
||||
{
|
||||
// Lock the scheduler.
|
||||
KScopedSchedulerLock sl(m_kernel);
|
||||
KScopedSchedulerLock sl(kernel);
|
||||
|
||||
// Check that the server isn't closed.
|
||||
R_UNLESS(!m_parent->IsServerClosed(), ResultSessionClosed);
|
||||
@@ -109,11 +106,11 @@ Result KLightServerSession::OnRequest(KThread* request_thread) {
|
||||
|
||||
// Begin waiting on the request.
|
||||
request_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC);
|
||||
request_thread->BeginWait(std::addressof(wait_queue));
|
||||
request_thread->BeginWait(kernel, std::addressof(wait_queue));
|
||||
|
||||
// If we have a server thread, end its wait.
|
||||
if (m_server_thread != nullptr) {
|
||||
m_server_thread->EndWait(ResultSuccess);
|
||||
m_server_thread->EndWait(kernel, ResultSuccess);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,13 +120,13 @@ Result KLightServerSession::OnRequest(KThread* request_thread) {
|
||||
R_RETURN(request_thread->GetWaitResult());
|
||||
}
|
||||
|
||||
Result KLightServerSession::ReplyAndReceive(u32* data) {
|
||||
Result KLightServerSession::ReplyAndReceive(KernelCore& kernel, u32* data) {
|
||||
// Set the server context.
|
||||
GetCurrentThread(m_kernel).SetLightSessionData(data);
|
||||
GetCurrentThread(kernel).SetLightSessionData(data);
|
||||
|
||||
// Reply, if we need to.
|
||||
if (data[0] & KLightSession::ReplyFlag) {
|
||||
KScopedSchedulerLock sl(m_kernel);
|
||||
KScopedSchedulerLock sl(kernel);
|
||||
|
||||
// Check that we're open.
|
||||
R_UNLESS(!m_parent->IsClientClosed(), ResultSessionClosed);
|
||||
@@ -139,17 +136,16 @@ Result KLightServerSession::ReplyAndReceive(u32* data) {
|
||||
R_UNLESS(m_current_request != nullptr, ResultInvalidState);
|
||||
|
||||
// Check that the server thread id is correct.
|
||||
R_UNLESS(m_server_thread_id == GetCurrentThread(m_kernel).GetId(), ResultInvalidState);
|
||||
R_UNLESS(m_server_thread_id == GetCurrentThread(kernel).GetId(), ResultInvalidState);
|
||||
|
||||
// If we can reply, do so.
|
||||
if (!m_current_request->IsTerminationRequested()) {
|
||||
std::memcpy(m_current_request->GetLightSessionData(),
|
||||
GetCurrentThread(m_kernel).GetLightSessionData(), KLightSession::DataSize);
|
||||
m_current_request->EndWait(ResultSuccess);
|
||||
std::memcpy(m_current_request->GetLightSessionData(), GetCurrentThread(kernel).GetLightSessionData(), KLightSession::DataSize);
|
||||
m_current_request->EndWait(kernel, ResultSuccess);
|
||||
}
|
||||
|
||||
// Close our current request.
|
||||
m_current_request->Close();
|
||||
m_current_request->Close(kernel);
|
||||
|
||||
// Clear our current request.
|
||||
m_current_request = nullptr;
|
||||
@@ -157,14 +153,13 @@ Result KLightServerSession::ReplyAndReceive(u32* data) {
|
||||
}
|
||||
|
||||
// Create the wait queue for our receive.
|
||||
ThreadQueueImplForKLightServerSessionReceive wait_queue(m_kernel,
|
||||
std::addressof(m_server_thread));
|
||||
ThreadQueueImplForKLightServerSessionReceive wait_queue(kernel, std::addressof(m_server_thread));
|
||||
|
||||
// Receive.
|
||||
while (true) {
|
||||
// Try to receive a request.
|
||||
{
|
||||
KScopedSchedulerLock sl(m_kernel);
|
||||
KScopedSchedulerLock sl(kernel);
|
||||
|
||||
// Check that we aren't already receiving.
|
||||
R_UNLESS(m_server_thread == nullptr, ResultInvalidState);
|
||||
@@ -175,20 +170,19 @@ Result KLightServerSession::ReplyAndReceive(u32* data) {
|
||||
R_UNLESS(!m_parent->IsServerClosed(), ResultSessionClosed);
|
||||
|
||||
// Check that we're not terminating.
|
||||
R_UNLESS(!GetCurrentThread(m_kernel).IsTerminationRequested(),
|
||||
ResultTerminationRequested);
|
||||
R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(), ResultTerminationRequested);
|
||||
|
||||
// If we have a request available, use it.
|
||||
if (auto head = m_request_list.begin(); head != m_request_list.end()) {
|
||||
// Set our current request.
|
||||
m_current_request = std::addressof(*head);
|
||||
m_current_request->Open();
|
||||
m_current_request->Open(kernel);
|
||||
|
||||
// Set our server thread id.
|
||||
m_server_thread_id = GetCurrentThread(m_kernel).GetId();
|
||||
m_server_thread_id = GetCurrentThread(kernel).GetId();
|
||||
|
||||
// Copy the client request data.
|
||||
std::memcpy(GetCurrentThread(m_kernel).GetLightSessionData(),
|
||||
std::memcpy(GetCurrentThread(kernel).GetLightSessionData(),
|
||||
m_current_request->GetLightSessionData(), KLightSession::DataSize);
|
||||
|
||||
// We successfully received.
|
||||
@@ -198,51 +192,51 @@ Result KLightServerSession::ReplyAndReceive(u32* data) {
|
||||
// We need to wait for a request to come in.
|
||||
|
||||
// Check if we were cancelled.
|
||||
if (GetCurrentThread(m_kernel).IsWaitCancelled()) {
|
||||
GetCurrentThread(m_kernel).ClearWaitCancelled();
|
||||
if (GetCurrentThread(kernel).IsWaitCancelled()) {
|
||||
GetCurrentThread(kernel).ClearWaitCancelled();
|
||||
R_THROW(ResultCancelled);
|
||||
}
|
||||
|
||||
// Mark ourselves as cancellable.
|
||||
GetCurrentThread(m_kernel).SetCancellable();
|
||||
GetCurrentThread(kernel).SetCancellable();
|
||||
|
||||
// Wait for a request to come in.
|
||||
m_server_thread = GetCurrentThreadPointer(m_kernel);
|
||||
GetCurrentThread(m_kernel).SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC);
|
||||
GetCurrentThread(m_kernel).BeginWait(std::addressof(wait_queue));
|
||||
m_server_thread = GetCurrentThreadPointer(kernel);
|
||||
GetCurrentThread(kernel).SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC);
|
||||
GetCurrentThread(kernel).BeginWait(kernel, std::addressof(wait_queue));
|
||||
}
|
||||
|
||||
// We waited to receive a request; if our wait failed, return the failing result.
|
||||
R_TRY(GetCurrentThread(m_kernel).GetWaitResult());
|
||||
R_TRY(GetCurrentThread(kernel).GetWaitResult());
|
||||
}
|
||||
}
|
||||
|
||||
void KLightServerSession::CleanupRequests() {
|
||||
void KLightServerSession::CleanupRequests(KernelCore& kernel) {
|
||||
// Cleanup all pending requests.
|
||||
{
|
||||
KScopedSchedulerLock sl(m_kernel);
|
||||
KScopedSchedulerLock sl(kernel);
|
||||
|
||||
// Handle the current request.
|
||||
if (m_current_request != nullptr) {
|
||||
// Reply to the current request.
|
||||
if (!m_current_request->IsTerminationRequested()) {
|
||||
m_current_request->EndWait(ResultSessionClosed);
|
||||
m_current_request->EndWait(kernel, ResultSessionClosed);
|
||||
}
|
||||
|
||||
// Clear our current request.
|
||||
m_current_request->Close();
|
||||
m_current_request->Close(kernel);
|
||||
m_current_request = nullptr;
|
||||
m_server_thread_id = InvalidThreadId;
|
||||
}
|
||||
|
||||
// Reply to all other requests.
|
||||
for (auto& thread : m_request_list) {
|
||||
thread.EndWait(ResultSessionClosed);
|
||||
thread.EndWait(kernel, ResultSessionClosed);
|
||||
}
|
||||
|
||||
// Wait up our server thread, if we have one.
|
||||
if (m_server_thread != nullptr) {
|
||||
m_server_thread->EndWait(ResultSessionClosed);
|
||||
m_server_thread->EndWait(kernel, ResultSessionClosed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user