mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-15 13:16:43 +00:00
Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b96701ef71 | |||
| 34ae949cc2 | |||
| 01ea402bcf | |||
| 52c6011d21 | |||
| 1b6abedef6 | |||
| 9adfe97082 | |||
| 577ec4d76f | |||
| ff359f9e18 | |||
| 7f0f664710 | |||
| 66b073ab8d | |||
| 4c65780f11 | |||
| a9c4c8aefd | |||
| a769505a45 | |||
| 54c3d10b86 | |||
| bdf60d79a0 | |||
| 2068b5d452 | |||
| 1b482fa99b | |||
| b6ee847947 | |||
| 6c16440996 | |||
| 09c583506b |
@@ -1,20 +0,0 @@
|
||||
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||||
index 16c6092..9e75548 100644
|
||||
--- a/CMakeLists.txt
|
||||
+++ b/CMakeLists.txt
|
||||
@@ -8,7 +8,14 @@ project(adrenotools LANGUAGES CXX C)
|
||||
|
||||
set(GEN_INSTALL_TARGET OFF CACHE BOOL "")
|
||||
|
||||
-add_subdirectory(lib/linkernsbypass)
|
||||
+include(CPM)
|
||||
+set(CPM_USE_LOCAL_PACKAGES OFF)
|
||||
+
|
||||
+CPMAddPackage(
|
||||
+ NAME linkernsbypass
|
||||
+ URL "https://github.com/bylaws/liblinkernsbypass/archive/aa3975893d.zip"
|
||||
+ URL_HASH SHA512=43d3d146facb7ec99d066a9b8990369ab7b9eec0d5f9a67131b0a0744fde0af27d884ca1f2a272cd113718a23356530ed97703c8c0659c4c25948d50c106119e
|
||||
+)
|
||||
|
||||
set(LIB_SOURCES src/bcenabler.cpp
|
||||
src/driver.cpp
|
||||
@@ -0,0 +1,26 @@
|
||||
From 52bbc5af6523daa22ad62fe4b84bc8d623d11a53 Mon Sep 17 00:00:00 2001
|
||||
From: crueter <crueter@eden-emu.dev>
|
||||
Date: Fri, 26 Jun 2026 01:09:40 -0400
|
||||
Subject: [PATCH] use cpmfile def for linkernsbypass
|
||||
|
||||
---
|
||||
CMakeLists.txt | 3 ++-
|
||||
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||||
index 16c6092..85b242c 100644
|
||||
--- a/CMakeLists.txt
|
||||
+++ b/CMakeLists.txt
|
||||
@@ -8,7 +8,8 @@ project(adrenotools LANGUAGES CXX C)
|
||||
|
||||
set(GEN_INSTALL_TARGET OFF CACHE BOOL "")
|
||||
|
||||
-add_subdirectory(lib/linkernsbypass)
|
||||
+include(CPMUtil)
|
||||
+AddJsonPackage(linkernsbypass)
|
||||
|
||||
set(LIB_SOURCES src/bcenabler.cpp
|
||||
src/driver.cpp
|
||||
--
|
||||
2.54.0
|
||||
|
||||
+1
-1
@@ -270,7 +270,7 @@ if (ANDROID AND YUZU_DOWNLOAD_ANDROID_VVL)
|
||||
set(abi ${CMAKE_ANDROID_ARCH_ABI})
|
||||
|
||||
set(vvl_lib_path "${CMAKE_CURRENT_SOURCE_DIR}/src/android/app/src/main/jniLibs/${abi}/")
|
||||
file(COPY "${VVL_SOURCE_DIR}/${abi}/libVkLayer_khronos_validation.so"
|
||||
file(COPY "${vulkan-validation-layers_SOURCE_DIR}/${abi}/libVkLayer_khronos_validation.so"
|
||||
DESTINATION "${vvl_lib_path}")
|
||||
endif()
|
||||
|
||||
|
||||
+143
-1181
File diff suppressed because it is too large
Load Diff
+639
-232
File diff suppressed because it is too large
Load Diff
+45
-79
@@ -3,11 +3,10 @@
|
||||
"hash": "1229f345b014f7ca544dedb4edb3311e41ba736f9aa9a67f88b5f26f3c983288c6bb6cdedcfb0b8a02c63088a37e6a0d7ba97d9c2a4d721b213916327cffe28a",
|
||||
"min_version": "0.9.1",
|
||||
"repo": "lioncash/biscuit",
|
||||
"tag": "v%VERSION%",
|
||||
"version": "0.19.0"
|
||||
"version": "v0.19.0"
|
||||
},
|
||||
"boost": {
|
||||
"artifact": "%TAG%-cmake.tar.xz",
|
||||
"artifact": "%VERSION%-cmake.tar.xz",
|
||||
"find_args": "CONFIG OPTIONAL_COMPONENTS headers context system fiber filesystem",
|
||||
"hash": "6ae6e94664fe7f2fb01976b59b276ac5df8085c7503fa829d810fbfe495960cfec44fa2c36e2cb23480bc19c956ed199d4952b02639a00a6c07625d4e7130c2d",
|
||||
"min_version": "1.57",
|
||||
@@ -16,15 +15,13 @@
|
||||
"0001-clang-cl.patch"
|
||||
],
|
||||
"repo": "boostorg/boost",
|
||||
"tag": "boost-%VERSION%",
|
||||
"version": "1.90.0"
|
||||
"version": "boost-1.90.0"
|
||||
},
|
||||
"boost_headers": {
|
||||
"bundled": true,
|
||||
"hash": "4ef845775e2277a8104ded6ddf749aa262ce52cf8438042869a048f9a0156dd772fbbcfa74efa1378fecef339b7286f6fe4b4feb5c45d49966b35d08e3e83507",
|
||||
"repo": "boostorg/headers",
|
||||
"tag": "boost-%VERSION%",
|
||||
"version": "1.90.0"
|
||||
"version": "boost-1.90.0"
|
||||
},
|
||||
"catch2": {
|
||||
"hash": "7eea385d79d88a5690cde131fe7ccda97d5c54ea09d6f515000d7bf07c828809d61c1ac99912c1ee507cf933f61c1c47ecdcc45df7850ffa82714034b0fccf35",
|
||||
@@ -34,8 +31,7 @@
|
||||
"0001-solaris-isnan-fix.patch"
|
||||
],
|
||||
"repo": "catchorg/Catch2",
|
||||
"tag": "v%VERSION%",
|
||||
"version": "3.13.0"
|
||||
"version": "v3.13.0"
|
||||
},
|
||||
"cpp-jwt": {
|
||||
"find_args": "CONFIG",
|
||||
@@ -48,8 +44,7 @@
|
||||
"0001-fix-missing-decl.patch"
|
||||
],
|
||||
"repo": "arun11299/cpp-jwt",
|
||||
"sha": "7f24eb4c32",
|
||||
"version": "1.5.1"
|
||||
"version": "7f24eb4c32"
|
||||
},
|
||||
"cubeb": {
|
||||
"find_args": "CONFIG",
|
||||
@@ -61,31 +56,26 @@
|
||||
"BUNDLE_SPEEX ON"
|
||||
],
|
||||
"repo": "mozilla/cubeb",
|
||||
"sha": "fa02160712",
|
||||
"version": "0.0.0"
|
||||
"version": "fa02160712"
|
||||
},
|
||||
"discord-rpc": {
|
||||
"find_args": "MODULE",
|
||||
"hash": "8213c43dcb0f7d479f5861091d111ed12fbdec1e62e6d729d65a4bc181d82f48a35d5fd3cd5c291f2393ac7c9681eabc6b76609755f55376284c8a8d67e148f3",
|
||||
"package": "DiscordRPC",
|
||||
"repo": "eden-emulator/discord-rpc",
|
||||
"sha": "0d8b2d6a37",
|
||||
"version": "3.4.1"
|
||||
"version": "0d8b2d6a37"
|
||||
},
|
||||
"enet": {
|
||||
"find_args": "MODULE",
|
||||
"hash": "a0d2fa8c957704dd49e00a726284ac5ca034b50b00d2b20a94fa1bbfbb80841467834bfdc84aa0ed0d6aab894608fd6c86c3b94eee46343f0e6d9c22e391dbf9",
|
||||
"min_version": "1.3",
|
||||
"repo": "lsalzman/enet",
|
||||
"tag": "v%VERSION%",
|
||||
"version": "1.3.18"
|
||||
"version": "v1.3.18"
|
||||
},
|
||||
"ffmpeg": {
|
||||
"bundled": true,
|
||||
"hash": "ed177621176b3961bdcaa339187d3a7688c1c8b060b79c4bb0257cbc67ad7021ae5d5adca5303b45625abbbe3d9aafdd87ce777b8690ac295290d744c875489a",
|
||||
"repo": "FFmpeg/FFmpeg",
|
||||
"sha": "c7b5f1537d",
|
||||
"version": "8.0.1"
|
||||
"version": "c7b5f1537d"
|
||||
},
|
||||
"ffmpeg-ci": {
|
||||
"ci": true,
|
||||
@@ -99,23 +89,20 @@
|
||||
"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"
|
||||
"version": "61dce5ae18"
|
||||
},
|
||||
"gamemode": {
|
||||
"find_args": "MODULE",
|
||||
"hash": "e87ec14ed3e826d578ebf095c41580069dda603792ba91efa84f45f4571a28f4d91889675055fd6f042d7dc25b0b9443daf70963ae463e38b11bcba95f4c65a9",
|
||||
"min_version": "1.7",
|
||||
"repo": "FeralInteractive/gamemode",
|
||||
"sha": "ce6fe122f3",
|
||||
"version": "1.8.2"
|
||||
"version": "ce6fe122f3"
|
||||
},
|
||||
"httplib": {
|
||||
"find_args": "MODULE GLOBAL",
|
||||
@@ -128,23 +115,20 @@
|
||||
"0001-mingw.patch"
|
||||
],
|
||||
"repo": "yhirose/cpp-httplib",
|
||||
"tag": "v%VERSION%",
|
||||
"version": "0.46.0"
|
||||
"version": "v0.46.0"
|
||||
},
|
||||
"lagoon": {
|
||||
"hash": "b9380f99c6effaeccc6d8f81d4942e852c11ad28613df637e155451556ae5826f93765bee57a5c87a9740d2bd1db463ad0f55a947772fe9d57eeabae3efa373e",
|
||||
"repo": "loongson-community/lagoon",
|
||||
"tag": "%VERSION%",
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"libadrenotools": {
|
||||
"hash": "f6526620cb752876edc5ed4c0925d57b873a8218ee09ad10859ee476e9333259784f61c1dcc55a2bcba597352d18aff22cd2e4c1925ec2ae94074e09d7da2265",
|
||||
"patches": [
|
||||
"0001-linkerns-cpm.patch"
|
||||
"0001-use-cpmfile-def-for-linkernsbypass.patch"
|
||||
],
|
||||
"repo": "eden-emulator/libadrenotools",
|
||||
"sha": "8ba23b42d7",
|
||||
"version": "1.0.0"
|
||||
"version": "8ba23b42d7"
|
||||
},
|
||||
"libusb": {
|
||||
"find_args": "MODULE",
|
||||
@@ -153,53 +137,52 @@
|
||||
"0001-netbsd-gettime.patch"
|
||||
],
|
||||
"repo": "libusb/libusb",
|
||||
"tag": "v%VERSION%",
|
||||
"version": "1.0.29"
|
||||
"version": "v1.0.29"
|
||||
},
|
||||
"linkernsbypass": {
|
||||
"bundled": "true",
|
||||
"hash": "bbe3f1f08e2bc7172b36e8f052912cc374289fc9a8e5a39ae7547a0c232de8f57ba24883451896b7a9a5d1be0e4de5c5b0f70c2022eda18d7d5a754847521800",
|
||||
"repo": "bylaws/liblinkernsbypass",
|
||||
"version": "aa3975893d"
|
||||
},
|
||||
"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"
|
||||
"version": "ebb370ca83"
|
||||
},
|
||||
"moltenvk": {
|
||||
"artifact": "MoltenVK-macOS.tar",
|
||||
"bundled": true,
|
||||
"hash": "5695b36ca5775819a71791557fcb40a4a5ee4495be6b8442e0b666d0c436bec02aae68cc6210183f7a5c986bdbec0e117aecfad5396e496e9c2fd5c89133a347",
|
||||
"repo": "V380-Ori/Ryujinx.MoltenVK",
|
||||
"tag": "v%VERSION%-ryujinx",
|
||||
"version": "1.4.1"
|
||||
"version": "v1.4.1-ryujinx"
|
||||
},
|
||||
"nlohmann": {
|
||||
"hash": "6cc1e86261f8fac21cc17a33da3b6b3c3cd5c116755651642af3c9e99bb3538fd42c1bd50397a77c8fb6821bc62d90e6b91bcdde77a78f58f2416c62fc53b97d",
|
||||
"min_version": "3.8",
|
||||
"package": "nlohmann_json",
|
||||
"repo": "nlohmann/json",
|
||||
"tag": "v%VERSION%",
|
||||
"version": "3.12.0"
|
||||
"version": "v3.12.0"
|
||||
},
|
||||
"oaknut": {
|
||||
"hash": "9697e80a7d5d9bcb3ce51051a9a24962fb90ca79d215f1f03ae6b58da8ba13a63b5dda1b4dde3d26ac6445029696b8ef2883f4e5a777b342bba01283ed293856",
|
||||
"min_version": "2.0.1",
|
||||
"repo": "eden-emulator/oaknut",
|
||||
"tag": "v%VERSION%",
|
||||
"version": "2.0.3"
|
||||
"version": "v2.0.3"
|
||||
},
|
||||
"oboe": {
|
||||
"bundled": true,
|
||||
"hash": "ce4011afe7345370d4ead3b891cd69a5ef224b129535783586c0ca75051d303ed446e6c7f10bde8da31fff58d6e307f1732a3ffd03b249f9ef1fd48fd4132715",
|
||||
"repo": "google/oboe",
|
||||
"tag": "%VERSION%",
|
||||
"version": "1.10.0"
|
||||
},
|
||||
"openssl": {
|
||||
@@ -210,8 +193,7 @@
|
||||
"0001-add-bundled-cert.patch"
|
||||
],
|
||||
"repo": "openssl/openssl",
|
||||
"tag": "openssl-%VERSION%",
|
||||
"version": "3.6.2"
|
||||
"version": "openssl-3.6.2"
|
||||
},
|
||||
"openssl-ci": {
|
||||
"ci": true,
|
||||
@@ -234,7 +216,6 @@
|
||||
"0004-use-shell-wrapper.patch"
|
||||
],
|
||||
"repo": "jimmy-park/openssl-cmake",
|
||||
"tag": "%VERSION%",
|
||||
"version": "3.6.2"
|
||||
},
|
||||
"opus": {
|
||||
@@ -250,8 +231,7 @@
|
||||
"0002-no-install.patch"
|
||||
],
|
||||
"repo": "xiph/opus",
|
||||
"sha": "a3f0ec02b3",
|
||||
"version": "1.5.2"
|
||||
"version": "a3f0ec02b3"
|
||||
},
|
||||
"quazip": {
|
||||
"hash": "609c240c7f029ac26a37d8fbab51bc16284e05e128b78b9b9c0e95d083538c36047a67d682759ac990e4adb0eeb90f04f1ea7fe2253bbda7e7e3bcce32e53dd8",
|
||||
@@ -264,16 +244,14 @@
|
||||
],
|
||||
"package": "QuaZip-Qt6",
|
||||
"repo": "stachenov/quazip",
|
||||
"sha": "2e95c9001b",
|
||||
"version": "1.5"
|
||||
"version": "2e95c9001b"
|
||||
},
|
||||
"sdl3": {
|
||||
"hash": "df5a323af7ac366661a3c0e887969c72584d232f3cc211419d59b0487b620b6b2859d4549c9e8df002ee489290062e466fcfddf7edc0872a37b1f2845e81c0f3",
|
||||
"min_version": "3.2.10",
|
||||
"package": "SDL3",
|
||||
"repo": "libsdl-org/SDL",
|
||||
"tag": "release-%VERSION%",
|
||||
"version": "3.4.8"
|
||||
"version": "release-3.4.8"
|
||||
},
|
||||
"sdl3-ci": {
|
||||
"ci": true,
|
||||
@@ -288,19 +266,16 @@
|
||||
"hash": "b937c18a7b6277d77ca7ebfb216af4984810f77af4c32d101b7685369a4bd5eb61406223f82698e167e6311a728d07415ab59639fdf19eff71ad6dc2abfda989",
|
||||
"package": "SimpleIni",
|
||||
"repo": "brofield/simpleini",
|
||||
"tag": "v%VERSION%",
|
||||
"version": "4.25"
|
||||
"version": "v4.25"
|
||||
},
|
||||
"sirit": {
|
||||
"artifact": "sirit-source-%VERSION%.tar.zst",
|
||||
"find_args": "CONFIG",
|
||||
"hash": "b7cd6885acae3fc8698288d19febba0dd45e4a02a2b6563d2eb995a988d0847046e8d16a5dea7e0bf832b4321bcec134cdbafc553f6ede039e4cf34525c5dce5",
|
||||
"options": [
|
||||
"SIRIT_USE_SYSTEM_SPIRV_HEADERS ON"
|
||||
],
|
||||
"repo": "eden-emulator/sirit",
|
||||
"tag": "v%VERSION%",
|
||||
"version": "1.0.5",
|
||||
"hash": "10b3ff60bdcad428bb4f54360ff749212333a1d24c0b3ed99e466b1bfcf99d2db6cf596c0f965854a2095dfef9b7ce4e045edb070fa9f76eb3b295ab03a4a293"
|
||||
"version": "v1.0.5"
|
||||
},
|
||||
"sirit-ci": {
|
||||
"ci": true,
|
||||
@@ -310,13 +285,13 @@
|
||||
"version": "1.0.5"
|
||||
},
|
||||
"spirv-headers": {
|
||||
"hash": "cae8cd179c9013068876908fecc1d158168310ad6ac250398a41f0f5206ceff6469e2aaeab9c820bce9d1b08950c725c89c46e94b89a692be9805432cf749396",
|
||||
"hash": "d624371dd455c66a300344c89812598ffe11b5eedba555779f789e85c29dc67317741858c60e0744a1e6755cc0d2759b8659f0674f4cc31479c4cb6fc25ed23b",
|
||||
"options": [
|
||||
"SPIRV_WERROR OFF"
|
||||
],
|
||||
"package": "SPIRV-Headers",
|
||||
"repo": "KhronosGroup/SPIRV-Headers",
|
||||
"sha": "04f10f650d"
|
||||
"version": "vulkan-sdk-1.4.341.0"
|
||||
},
|
||||
"tzdb": {
|
||||
"artifact": "%VERSION%.tar.gz",
|
||||
@@ -324,7 +299,6 @@
|
||||
"hash": "cce65a12bf90f4ead43b24a0b95dfad77ac3d9bfbaaf66c55e6701346e7a1e44ca5d2f23f47ee35ee02271eb1082bf1762af207aad9fb236f1c8476812d008ed",
|
||||
"package": "nx_tzdb",
|
||||
"repo": "eden-emu/tzdb_to_nx",
|
||||
"tag": "%VERSION%",
|
||||
"version": "230326"
|
||||
},
|
||||
"unordered-dense": {
|
||||
@@ -336,46 +310,40 @@
|
||||
"0001-avoid-memset-when-clearing-an-empty-table.patch"
|
||||
],
|
||||
"repo": "martinus/unordered_dense",
|
||||
"sha": "7b55cab841",
|
||||
"version": "4.8.1"
|
||||
"version": "7b55cab841"
|
||||
},
|
||||
"vulkan-headers": {
|
||||
"hash": "d2846ea228415772645eea4b52a9efd33e6a563043dd3de059e798be6391a8f0ca089f455ae420ff22574939ed0f48ed7c6ff3d5a9987d5231dbf3b3f89b484b",
|
||||
"min_version": "1.4.317",
|
||||
"package": "VulkanHeaders",
|
||||
"repo": "KhronosGroup/Vulkan-Headers",
|
||||
"tag": "v%VERSION%",
|
||||
"version": "1.4.345"
|
||||
"version": "v1.4.345"
|
||||
},
|
||||
"vulkan-memory-allocator": {
|
||||
"find_args": "CONFIG",
|
||||
"hash": "deb5902ef8db0e329fbd5f3f4385eb0e26bdd9f14f3a2334823fb3fe18f36bc5d235d620d6e5f6fe3551ec3ea7038638899db8778c09f6d5c278f5ff95c3344b",
|
||||
"package": "VulkanMemoryAllocator",
|
||||
"repo": "GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator",
|
||||
"tag": "v%VERSION%",
|
||||
"version": "3.3.0"
|
||||
"version": "v3.3.0"
|
||||
},
|
||||
"vulkan-utility-libraries": {
|
||||
"hash": "114f6b237a6dcba923ccc576befb5dea3f1c9b3a30de7dc741f234a831d1c2d52d8a224afb37dd57dffca67ac0df461eaaab6a5ab5e503b393f91c166680c3e1",
|
||||
"package": "VulkanUtilityLibraries",
|
||||
"repo": "KhronosGroup/Vulkan-Utility-Libraries",
|
||||
"tag": "v%VERSION%",
|
||||
"version": "1.4.345"
|
||||
"version": "v1.4.345"
|
||||
},
|
||||
"vulkan-validation-layers": {
|
||||
"artifact": "android-binaries-%VERSION%.zip",
|
||||
"artifact": "android-binaries-%NUMERIC_VERSION%.zip",
|
||||
"hash": "8812ae84cbe49e6a3418ade9c458d3be6d74a3dffd319d4502007b564d580998056e8190414368ec11b27bc83993c7a0dad713c31bcc3d9553b51243efee3753",
|
||||
"package": "VVL",
|
||||
"numeric_version": "1.4.341.0",
|
||||
"repo": "KhronosGroup/Vulkan-ValidationLayers",
|
||||
"tag": "vulkan-sdk-%VERSION%",
|
||||
"version": "1.4.341.0"
|
||||
"version": "vulkan-sdk-%NUMERIC_VERSION%"
|
||||
},
|
||||
"xbyak": {
|
||||
"hash": "b6475276b2faaeb315734ea8f4f8bd87ededcee768961b39679bee547e7f3e98884d8b7851e176d861dab30a80a76e6ea302f8c111483607dde969b4797ea95a",
|
||||
"package": "xbyak",
|
||||
"repo": "herumi/xbyak",
|
||||
"tag": "v%VERSION%",
|
||||
"version": "7.35.2"
|
||||
"version": "v7.35.2"
|
||||
},
|
||||
"zlib": {
|
||||
"hash": "16fea4df307a68cf0035858abe2fd550250618a97590e202037acd18a666f57afc10f8836cbbd472d54a0e76539d0e558cb26f059d53de52ff90634bbf4f47d4",
|
||||
@@ -386,8 +354,7 @@
|
||||
],
|
||||
"package": "ZLIB",
|
||||
"repo": "madler/zlib",
|
||||
"tag": "v%VERSION%",
|
||||
"version": "1.3.2"
|
||||
"version": "v1.3.2"
|
||||
},
|
||||
"zstd": {
|
||||
"find_args": "MODULE",
|
||||
@@ -397,8 +364,7 @@
|
||||
"ZSTD_BUILD_SHARED OFF"
|
||||
],
|
||||
"repo": "facebook/zstd",
|
||||
"sha": "b8d6101fba",
|
||||
"source_subdir": "build/cmake",
|
||||
"version": "1.5.7"
|
||||
"version": "b8d6101fba"
|
||||
}
|
||||
}
|
||||
|
||||
+103
-51
@@ -8,6 +8,7 @@ CPMUtil is a wrapper around CPM that aims to reduce boilerplate and add useful u
|
||||
- [Common Properties](#common-properties)
|
||||
- [Standard Packages](#standard-packages)
|
||||
- [Versioning](#versioning)
|
||||
- [Artifact Naming Errata](#artifact-naming-errata)
|
||||
- [Patches](#patches)
|
||||
- [Pre-built CI Packages](#pre-built-ci-packages)
|
||||
- [Usage](#usage)
|
||||
@@ -22,12 +23,18 @@ CPMUtil is a wrapper around CPM that aims to reduce boilerplate and add useful u
|
||||
- [Addendum: Module Path Packages](#addendum-module-path-packages)
|
||||
- [Example: OpenSSL](#example-openssl)
|
||||
- [Addendum: Adding Qt](#addendum-adding-qt)
|
||||
- [Addendum: Package-Specific Overrides](#addendum-package-specific-overrides)
|
||||
- [Addendum: Supply-Chain Security](#addendum-supply-chain-security)
|
||||
- [Checksumming](#checksumming)
|
||||
- [Caching](#caching)
|
||||
- [Immutable Commit Hashes](#immutable-commit-hashes)
|
||||
|
||||
## 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_SYSTEM` (default `OFF`): Require all CPM dependencies to use system packages.
|
||||
- You may optionally override this for each package: [Package-Specific Overrides](#addendum-package-specific-overrides)
|
||||
- `CPMUTIL_FORCE_BUNDLED` (default `ON` on MSVC and Android, `OFF` elsewhere): Require all CPM dependencies to use bundled packages.
|
||||
- You may optionally override this for each package: [Package-Specific Overrides](#addendum-package-specific-overrides)
|
||||
- `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.
|
||||
|
||||
@@ -50,23 +57,25 @@ And may optionally define other properties like:
|
||||
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"
|
||||
]
|
||||
{
|
||||
"fmt": {
|
||||
"repo": "fmtlib/fmt",
|
||||
"version": "12.1.0",
|
||||
"hash": "f0da8...23f2",
|
||||
"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
|
||||
- Searches for a system package named `fmt` of version 8 or higher (`find_package(fmt 8)`)
|
||||
- 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`
|
||||
@@ -85,63 +94,82 @@ 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.
|
||||
- `version`: The version of the package. This is required.
|
||||
- Generally, this should be a 10-wide Git commit hash or Git tag.
|
||||
- Tags must be fully qualified and include prefixes and suffixes, e.g. `boost-1.88.0`
|
||||
- `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`.
|
||||
- `hash`: The SHA512 hash of the downloaded artifact. CPMUtil generally computes this for you--if not, use `tools/cpmutil.sh package hash <JSON key>`
|
||||
- `min_version`: The minimum required version of the package, if a system package is desired.
|
||||
|
||||
And may optionally define:
|
||||
|
||||
- `url`: Download from a raw URL.
|
||||
- `artifact`: A GitHub/Forgejo/Gitea release artifact. Requires `repo` to be set and valid.
|
||||
- `numeric_version`: Replaces `%NUMERIC_VERSION%` in artifact and version definitions; see [Artifact Naming Errata](#artifact-naming-errata).
|
||||
|
||||
See [Versioning](#versioning) for version/artifact information.
|
||||
|
||||
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.
|
||||
- `bundled`: Force the usage of a bundled package. Useful for packages where the system package is broken or nonexistent; e.g. external fragment shaders or data archives.
|
||||
- Note that this will conflict with `CPMUTIL_FORCE_SYSTEM`; for this reason, when using non-library archives, it may be best to allow the user to download and extract the archive manually and specify a local directory to it.
|
||||
- `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.
|
||||
When fetching from a Git repository, there are generally three methods of versioning:
|
||||
|
||||
`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`:
|
||||
- Commit hashes
|
||||
- Git tags
|
||||
- Release artifacts
|
||||
|
||||
```json
|
||||
"openssl": {
|
||||
"repo": "openssl/openssl",
|
||||
"version": "3.6.2",
|
||||
"tag": "openssl-%VERSION%"
|
||||
}
|
||||
```
|
||||
When `repo` is set, `version` field can be set to any commitish value, including commit hashes or Git tags. In the case of artifacts, `version` must be a Git tag, and `artifact` must be set to a release artifact attached to that tag. Many repositories intentionally version the filenames of their release artifacts; for this purpose, CPMUtil allows you to implant the version number into the artifact name. To do so, add `%VERSION%` to the artifact name; CPMUtil will then automatically replace `%VERSION%` with the `version` field. This means that when changing versions, you only need to update the version, not the artifact!
|
||||
|
||||
`artifact` also supports `%VERSION%` replacement, and can also use `%TAG%` to be replaced by the computed tag. Take this Boost definition:
|
||||
Take Boost as an example. The artifact for Boost 1.90.0 is `boost-1.90.0-cmake.tar.xz`, and the tag is `boost-1.90.0`. Thus, we can set the artifact to `%VERSION%-cmake.tar.xz`:
|
||||
|
||||
```json
|
||||
"boost": {
|
||||
"repo": "boostorg/boost",
|
||||
"tag": "boost-%VERSION%",
|
||||
"version": "1.90.0"
|
||||
"version": "boost-1.90.0",
|
||||
"artifact": "%VERSION%-cmake.tar.xz"
|
||||
}
|
||||
```
|
||||
|
||||
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:
|
||||
The artifact will then evaluate as `boost-1.90.0-cmake.tar.xz`.
|
||||
|
||||
- `boost-%VERSION%-cmake.tar.xz`
|
||||
- Or, even simpler: `%TAG%-cmake.tar.xz`
|
||||
### Artifact Naming Errata
|
||||
|
||||
Future updates need only change the `version` identifier, and the artifact and tag will automatically be updated!
|
||||
While `%VERSION%` replacement is generally good enough for well-packaged projects, occasionally there may be some problematic packages. Take, for instance, Vulkan Validation Layers:
|
||||
|
||||
- Tag (`version`): `vulkan-sdk-1.4.341.0`
|
||||
- Artifact: `android-binaries-1.4.341.0.zip`
|
||||
|
||||
Attempting to add version replacement to the artifact definition **would not work here!** In this case, you must utilize the `numeric_version` field described earlier; we would set `numeric_version` to `1.4.341.0` and add `%NUMERIC_VERSION%` replacements into our artifact and version fields:
|
||||
|
||||
```json
|
||||
"vulkan-validation-layers": {
|
||||
"artifact": "android-binaries-%NUMERIC_VERSION%.zip",
|
||||
"repo": "KhronosGroup/Vulkan-ValidationLayers",
|
||||
"version": "vulkan-sdk-%NUMERIC_VERSION%",
|
||||
"numeric_version": "1.4.341.0"
|
||||
},
|
||||
```
|
||||
|
||||
`artifact` will thus evaluate to `android-binaries-1.4.341.0.zip`, and `version` to `vulkan-sdk-1.4.341.0`. CPMUtil's auto-updater will also account for this and only update `numeric_version`!
|
||||
|
||||
### 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:
|
||||
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`.
|
||||
|
||||
They 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": [
|
||||
@@ -151,7 +179,7 @@ CPMUtil is able to apply in-place source tree patches to downloaded packages. Th
|
||||
]
|
||||
```
|
||||
|
||||
Then, when Boost is downloaded, it will apply these patches in order to the source tree.
|
||||
Then, when Boost is downloaded, it will apply these patches to the source tree in the order they are defined (compound/dependent patches are okay!). Note that when you add, remove, or modify patches, CPMUtil will invalidate your downloaded cache and re-fetch the source.
|
||||
|
||||
To learn how to make patches, see [Addendum: Making Patches](#addendum-making-patches).
|
||||
|
||||
@@ -199,12 +227,9 @@ If you're only concerned with basic usage, you can stop reading. For more advanc
|
||||
|
||||
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:
|
||||
Within these subdirectories, additional directories are created for each individual version, corresponding directly to their `version` field. CI packages use `<platform>-<architecture>-<version>` unconditionally.
|
||||
|
||||
- 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.
|
||||
To see the cache directory for a given package, use `tools/cpmutil.sh package dir <JSON key>`.
|
||||
|
||||
## Addendum: Making Patches
|
||||
|
||||
@@ -242,7 +267,7 @@ If you are packaging a project that uses CPMUtil, read this!
|
||||
|
||||
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)
|
||||
Alternatively, if CPMUtil pulls in a package that has no suitable way to install or use a system version, download it separately and pass `-D<PackageName>_CUSTOM_DIR=/path/to/downloaded/dir`.
|
||||
|
||||
### Unsandboxed
|
||||
|
||||
@@ -263,14 +288,12 @@ Using the prior Vulkan example:
|
||||
"repo": "KhronosGroup/Vulkan-Headers",
|
||||
"package": "VulkanHeaders",
|
||||
"min_version": "1.4.317",
|
||||
"version": "1.4.342",
|
||||
"tag": "v%VERSION%"
|
||||
"version": "v1.4.342"
|
||||
},
|
||||
"vulkan-utility-libraries": {
|
||||
"repo": "KhronosGroup/Vulkan-Utility-Libraries",
|
||||
"package": "VulkanUtilityLibraries",
|
||||
"version": "1.4.342",
|
||||
"tag": "v%VERSION%"
|
||||
"version": "v1.4.342"
|
||||
}
|
||||
```
|
||||
|
||||
@@ -318,3 +341,32 @@ AddQt(QDash-CI/Qt 6.11.1)
|
||||
```
|
||||
|
||||
Then, call `find_package(Qt6 ...)` and it will pull Qt from your downloaded source.
|
||||
|
||||
## Addendum: Package-Specific Overrides
|
||||
|
||||
There are three variables that CPMUtil defines for each package; these can be overriden by the user or in your CMake. `package` refers either to the `package` value in the JSON, or the package's JSON key if unset (see `package` in [Common Properties](#common-properties)):
|
||||
|
||||
- `<package>_FORCE_BUNDLED`: Forcefully bundle the package. This has the same effect as `CPMUTIL_FORCE_BUNDLED`, but only for this package.
|
||||
- `<package>_FORCE_BUNDLED`: Forcefully use the system package, failing if it can't be found. This has the same effect as `SYSTEM`, but only for this package.
|
||||
- `<package>_CUSTOM_DIR`: Path to an extracted copy of the package. CPMUtil will not attempt to download the package and will instead use the custom directory.
|
||||
- For an example, see [CPMUtil's test case](https://git.crueter.xyz/CMake/CPMUtil/src/branch/master/tests/dir/CMakeLists.txt)
|
||||
|
||||
Additionally, in CMake, you can add `FORCE_BUNDLED_PACKAGE ON` to your `AddJsonPackage` command--note that you will have to use the `NAME <key>` syntax as described in [Module Path Packages](#addendum-module-path-packages). This will overrule *all* other overrides, including `CPMUTIL_FORCE_SYSTEM` and `<package>_FORCE_SYSTEM`--use with caution!
|
||||
|
||||
## Addendum: Supply-Chain Security
|
||||
|
||||
Many package managers suffer from the issue of supply chain security, specifically in regards to silent overwrites of existing packages or archives, e.g. tag sliding and artifact overwriting. CPMUtil has three methods to protect against this.
|
||||
|
||||
### Checksumming
|
||||
|
||||
CPMUtil *requires* SHA512 checksums for standard packages, and soon will for CI packages as well. If an attacker or compromised account slides a tag, overwrites a release artifact, or otherwise attempts to compromise anything that CPMUtil may fetch, **CPMUtil will not allow the download to continue!** This means that consumers of your build system can **only** download an artifact if the contents of the artifact are *exactly* indentical to what it was when it was configured--any changes at all will be rejected by CPMUtil.
|
||||
|
||||
### Caching
|
||||
|
||||
CPMUtil uses a mutable cache system, stored by default in `.cache/cpm`. Dependencies are downloaded and extracted here, and can be reused infinitely. This means that, for instance, if a package is compromised but you already have a cached local copy, you won't have to worry at all!
|
||||
|
||||
### Immutable Commit Hashes
|
||||
|
||||
CPMUtil is capable of using immutable Git commit hashes for its artifacts. These are (barring SHA1 collisions) completely immune to supply chain attacks--that is, unless the entire root server gets compromised to serve infected artifacts/source code; at which point there are much larger issues to worry about. This means that once you set a package to use a Git commit hash for its version, **it will stay the same forever**. This is useful if you want to ensure that consumers are never faced with download failures stemming from hash mismatches in case of compromised artifacts.
|
||||
|
||||
Do note, however, that this will render the package incompatible with CPMUtil's built-in auto-updater, so you will have to manually update the package.
|
||||
|
||||
+1
-1
@@ -269,7 +269,7 @@ class SettingsFragmentPresenter(
|
||||
// TODO(crueter): sub-submenus?
|
||||
private fun addGraphicsSettings(sl: ArrayList<SettingsItem>) {
|
||||
sl.apply {
|
||||
// add(IntSetting.RENDERER_NVDEC_EMULATION.key)
|
||||
add(IntSetting.RENDERER_NVDEC_EMULATION.key)
|
||||
|
||||
add(IntSetting.RENDERER_RESOLUTION.key)
|
||||
add(IntSetting.RENDERER_VSYNC.key)
|
||||
|
||||
@@ -27,7 +27,7 @@ if (ARCHITECTURE_arm64)
|
||||
target_link_libraries(yuzu-android PRIVATE adrenotools)
|
||||
endif()
|
||||
|
||||
target_link_libraries(yuzu-android PRIVATE OpenSSL::SSL cpp-jwt::cpp-jwt)
|
||||
target_link_libraries(yuzu-android PRIVATE FFmpeg::FFmpeg OpenSSL::SSL cpp-jwt::cpp-jwt)
|
||||
if (ENABLE_UPDATE_CHECKER)
|
||||
target_compile_definitions(yuzu-android PUBLIC ENABLE_UPDATE_CHECKER)
|
||||
endif()
|
||||
|
||||
@@ -36,6 +36,12 @@
|
||||
#include <frontend_common/content_manager.h>
|
||||
#include <jni.h>
|
||||
|
||||
extern "C" {
|
||||
// Required for FFmpeg mediacodec
|
||||
#include <libavcodec/jni.h>
|
||||
#include <libavutil/log.h>
|
||||
}
|
||||
|
||||
#include "common/android/multiplayer/multiplayer.h"
|
||||
#include "common/android/android_common.h"
|
||||
#include "common/android/id_cache.h"
|
||||
@@ -680,6 +686,15 @@ const char* fallback_cpu_detection() {
|
||||
|
||||
} // namespace
|
||||
|
||||
// referenced by common
|
||||
extern "C" {
|
||||
jint InitFFmpegOnLoad(JavaVM *vm) {
|
||||
av_jni_set_java_vm(vm, NULL);
|
||||
av_log_set_level(AV_LOG_DEBUG);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceChanged(JNIEnv* env, jobject instance,
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include "common/android/multiplayer/multiplayer.h"
|
||||
#include <network/network.h>
|
||||
|
||||
|
||||
static JavaVM *s_java_vm;
|
||||
static jclass s_native_library_class;
|
||||
static jclass s_disk_cache_progress_class;
|
||||
@@ -427,8 +426,12 @@ namespace Common::Android {
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// see on src/android/app/src/main/jni/native.cpp
|
||||
jint InitFFmpegOnLoad(JavaVM *vm);
|
||||
|
||||
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
|
||||
s_java_vm = vm;
|
||||
InitFFmpegOnLoad(vm);
|
||||
|
||||
JNIEnv *env;
|
||||
if (vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION) != JNI_OK)
|
||||
|
||||
@@ -729,7 +729,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface(ensure_token_id);
|
||||
rb.PushIpcInterface(ctx, ensure_token_id);
|
||||
}
|
||||
|
||||
void LoadIdTokenCacheDeprecated(HLERequestContext& ctx) {
|
||||
@@ -921,7 +921,7 @@ void Module::Interface::GetProfile(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IProfile>(system, user_id, *profile_manager);
|
||||
rb.PushIpcInterface<IProfile>(ctx, system, user_id, *profile_manager);
|
||||
}
|
||||
|
||||
void Module::Interface::IsUserRegistrationRequestPermitted(HLERequestContext& ctx) {
|
||||
@@ -993,7 +993,7 @@ void Module::Interface::GetBaasAccountManagerForApplication(HLERequestContext& c
|
||||
LOG_DEBUG(Service_ACC, "called");
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IManagerForApplication>(system, profile_manager);
|
||||
rb.PushIpcInterface<IManagerForApplication>(ctx, system, profile_manager);
|
||||
}
|
||||
|
||||
void Module::Interface::IsUserAccountSwitchLocked(HLERequestContext& ctx) {
|
||||
@@ -1089,7 +1089,7 @@ void Module::Interface::GetProfileEditor(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IProfileEditor>(system, user_id, *profile_manager);
|
||||
rb.PushIpcInterface<IProfileEditor>(ctx, system, user_id, *profile_manager);
|
||||
}
|
||||
|
||||
void Module::Interface::GetBaasAccountAdministrator(HLERequestContext &ctx) {
|
||||
@@ -1100,7 +1100,7 @@ void Module::Interface::GetBaasAccountAdministrator(HLERequestContext &ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IAdministrator>(system, uuid);
|
||||
rb.PushIpcInterface<IAdministrator>(ctx, system, uuid);
|
||||
}
|
||||
|
||||
void Module::Interface::ListQualifiedUsers(HLERequestContext& ctx) {
|
||||
@@ -1143,7 +1143,7 @@ void Module::Interface::GetBaasAccountManagerForSystemService(HLERequestContext&
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IManagerForSystemService>(system, uuid);
|
||||
rb.PushIpcInterface<IManagerForSystemService>(ctx, system, uuid);
|
||||
}
|
||||
|
||||
void Module::Interface::StoreSaveDataThumbnailSystem(HLERequestContext& ctx) {
|
||||
|
||||
@@ -35,7 +35,7 @@ void IAsyncContext::GetSystemEvent(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(completion_event->GetReadableEvent());
|
||||
rb.PushCopyObjects(ctx, completion_event->GetReadableEvent());
|
||||
}
|
||||
|
||||
void IAsyncContext::Cancel(HLERequestContext& ctx) {
|
||||
|
||||
@@ -82,7 +82,7 @@ void APM::OpenSession(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<ISession>(system, controller);
|
||||
rb.PushIpcInterface<ISession>(ctx, system, controller);
|
||||
}
|
||||
|
||||
void APM::GetPerformanceMode(HLERequestContext& ctx) {
|
||||
@@ -125,7 +125,7 @@ void APM_Sys::GetPerformanceEvent(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<ISession>(system, controller);
|
||||
rb.PushIpcInterface<ISession>(ctx, system, controller);
|
||||
}
|
||||
|
||||
void APM_Sys::SetCpuBoostMode(HLERequestContext& ctx) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -45,7 +48,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IRequest>(system);
|
||||
rb.PushIpcInterface<IRequest>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -184,7 +184,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(readable_event.Signal(system.Kernel()));
|
||||
rb.PushCopyObjects(readable_event);
|
||||
rb.PushCopyObjects(ctx, readable_event);
|
||||
}
|
||||
|
||||
void Cancel(HLERequestContext& ctx) {
|
||||
@@ -400,7 +400,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(notification_event->GetReadableEvent());
|
||||
rb.PushCopyObjects(ctx, notification_event->GetReadableEvent());
|
||||
}
|
||||
|
||||
void Clear(HLERequestContext& ctx) {
|
||||
@@ -476,7 +476,7 @@ private:
|
||||
void Module::Interface::CreateFriendService(HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IFriendService>(system);
|
||||
rb.PushIpcInterface<IFriendService>(ctx, system);
|
||||
LOG_DEBUG(Service_Friend, "called");
|
||||
}
|
||||
|
||||
@@ -488,12 +488,12 @@ void Module::Interface::CreateNotificationService(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<INotificationService>(system, uuid);
|
||||
rb.PushIpcInterface<INotificationService>(ctx, system, uuid);
|
||||
}
|
||||
|
||||
Module::Interface::Interface(std::shared_ptr<Module> module_, Core::System& system_,
|
||||
const char* name)
|
||||
: ServiceFramework{system_, name}, module{std::move(module_)} {}
|
||||
Module::Interface::Interface(std::shared_ptr<Module> module_, Core::System& system_, const char* name)
|
||||
: ServiceFramework{system_, name}, module{std::move(module_)}
|
||||
{}
|
||||
|
||||
Module::Interface::~Interface() = default;
|
||||
|
||||
|
||||
@@ -279,7 +279,7 @@ void ARP_W::AcquireRegistrar(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface(registrar);
|
||||
rb.PushIpcInterface(ctx, registrar);
|
||||
}
|
||||
|
||||
void ARP_W::UnregisterApplicationInstance(HLERequestContext& ctx) {
|
||||
|
||||
@@ -28,7 +28,7 @@ void BGTC_T::OpenTaskService(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<ITaskService>(system);
|
||||
rb.PushIpcInterface<ITaskService>(ctx, system);
|
||||
}
|
||||
|
||||
ITaskService::ITaskService(Core::System& system_) : ServiceFramework{system_, "ITaskService"} {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -56,7 +59,7 @@ ECTX_AW::~ECTX_AW() = default;
|
||||
void ECTX_AW::CreateContextRegistrar(HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IContextRegistrar>(std::make_shared<IContextRegistrar>(system));
|
||||
rb.PushIpcInterface<IContextRegistrar>(ctx, system);
|
||||
}
|
||||
|
||||
} // namespace Service::Glue
|
||||
|
||||
@@ -726,7 +726,7 @@ void IHidSystemServer::AcquireConnectionTriggerTimeoutEvent(HLERequestContext& c
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(acquire_device_registered_event->GetReadableEvent());
|
||||
rb.PushCopyObjects(ctx, acquire_device_registered_event->GetReadableEvent());
|
||||
}
|
||||
|
||||
void IHidSystemServer::AcquireDeviceRegisteredEventForControllerSupport(HLERequestContext& ctx) {
|
||||
@@ -734,7 +734,7 @@ void IHidSystemServer::AcquireDeviceRegisteredEventForControllerSupport(HLEReque
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(acquire_device_registered_event->GetReadableEvent());
|
||||
rb.PushCopyObjects(ctx, acquire_device_registered_event->GetReadableEvent());
|
||||
}
|
||||
|
||||
void IHidSystemServer::GetRegisteredDevices(HLERequestContext& ctx) {
|
||||
@@ -759,7 +759,7 @@ void IHidSystemServer::AcquireUniquePadConnectionEventHandle(HLERequestContext&
|
||||
LOG_WARNING(Service_HID, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.PushCopyObjects(unique_pad_connection_event->GetReadableEvent());
|
||||
rb.PushCopyObjects(ctx, unique_pad_connection_event->GetReadableEvent());
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
@@ -776,7 +776,7 @@ void IHidSystemServer::AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestConte
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(joy_detach_event->GetReadableEvent());
|
||||
rb.PushCopyObjects(ctx, joy_detach_event->GetReadableEvent());
|
||||
}
|
||||
|
||||
void IHidSystemServer::IsUsbFullKeyControllerEnabled(HLERequestContext& ctx) {
|
||||
|
||||
@@ -31,7 +31,7 @@ class Memory;
|
||||
}
|
||||
|
||||
namespace IPC {
|
||||
class ResponseBuilder;
|
||||
struct ResponseBuilder;
|
||||
}
|
||||
|
||||
namespace Service {
|
||||
@@ -392,7 +392,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
friend class IPC::ResponseBuilder;
|
||||
friend struct IPC::ResponseBuilder;
|
||||
|
||||
void ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming);
|
||||
|
||||
|
||||
@@ -24,45 +24,7 @@ namespace IPC {
|
||||
|
||||
constexpr Result ResultSessionClosed{ErrorModule::HIPC, 301};
|
||||
|
||||
class RequestHelperBase {
|
||||
protected:
|
||||
Service::HLERequestContext* context = nullptr;
|
||||
u32* cmdbuf;
|
||||
u32 index = 0;
|
||||
|
||||
public:
|
||||
explicit RequestHelperBase(u32* command_buffer) : cmdbuf(command_buffer) {}
|
||||
|
||||
explicit RequestHelperBase(Service::HLERequestContext& ctx)
|
||||
: context(&ctx), cmdbuf(ctx.CommandBuffer()) {}
|
||||
|
||||
void Skip(u32 size_in_words, bool set_to_null) {
|
||||
if (set_to_null) {
|
||||
memset(cmdbuf + index, 0, size_in_words * sizeof(u32));
|
||||
}
|
||||
index += size_in_words;
|
||||
}
|
||||
|
||||
/**
|
||||
* Aligns the current position forward to a 16-byte boundary, padding with zeros.
|
||||
*/
|
||||
void AlignWithPadding() {
|
||||
if (index & 3) {
|
||||
Skip(static_cast<u32>(4 - (index & 3)), true);
|
||||
}
|
||||
}
|
||||
|
||||
u32 GetCurrentOffset() const {
|
||||
return index;
|
||||
}
|
||||
|
||||
void SetCurrentOffset(u32 offset) {
|
||||
index = offset;
|
||||
}
|
||||
};
|
||||
|
||||
class ResponseBuilder : public RequestHelperBase {
|
||||
public:
|
||||
struct ResponseBuilder {
|
||||
/// Flags used for customizing the behavior of ResponseBuilder
|
||||
enum class Flags : u32 {
|
||||
None = 0,
|
||||
@@ -71,14 +33,13 @@ public:
|
||||
AlwaysMoveHandles = 1,
|
||||
};
|
||||
|
||||
explicit ResponseBuilder(Service::HLERequestContext& ctx, u32 normal_params_size_,
|
||||
u32 num_handles_to_copy_ = 0, u32 num_objects_to_move_ = 0,
|
||||
Flags flags = Flags::None)
|
||||
: RequestHelperBase(ctx), normal_params_size(normal_params_size_),
|
||||
num_handles_to_copy(num_handles_to_copy_),
|
||||
num_objects_to_move(num_objects_to_move_), kernel{ctx.kernel} {
|
||||
|
||||
memset(cmdbuf, 0, sizeof(u32) * IPC::COMMAND_BUFFER_LENGTH);
|
||||
inline explicit ResponseBuilder(Service::HLERequestContext& ctx, u32 normal_params_size_, u32 num_handles_to_copy_ = 0, u32 num_objects_to_move_ = 0, Flags flags = Flags::None)
|
||||
: cmdbuf(ctx.CommandBuffer())
|
||||
, normal_params_size(normal_params_size_)
|
||||
, num_handles_to_copy(num_handles_to_copy_)
|
||||
, num_objects_to_move(num_objects_to_move_)
|
||||
{
|
||||
std::memset(cmdbuf, 0, sizeof(u32) * IPC::COMMAND_BUFFER_LENGTH);
|
||||
|
||||
IPC::CommandHeader header{};
|
||||
auto const mgr = ctx.GetManager().get();
|
||||
@@ -117,9 +78,7 @@ public:
|
||||
handle_descriptor_header.num_handles_to_copy.Assign(num_handles_to_copy_);
|
||||
handle_descriptor_header.num_handles_to_move.Assign(num_handles_to_move);
|
||||
PushRaw(handle_descriptor_header);
|
||||
|
||||
ctx.handles_offset = index;
|
||||
|
||||
Skip(num_handles_to_copy + num_handles_to_move, true);
|
||||
}
|
||||
|
||||
@@ -131,7 +90,6 @@ public:
|
||||
domain_header.num_objects = num_domain_objects;
|
||||
PushRaw(domain_header);
|
||||
}
|
||||
|
||||
IPC::DataPayloadHeader data_payload_header{};
|
||||
data_payload_header.magic = Common::MakeMagic('S', 'F', 'C', 'O');
|
||||
PushRaw(data_payload_header);
|
||||
@@ -141,34 +99,39 @@ public:
|
||||
|
||||
ctx.data_payload_offset = index;
|
||||
ctx.write_size += index;
|
||||
ctx.domain_offset = static_cast<u32>(index + raw_data_size / sizeof(u32));
|
||||
ctx.domain_offset = u32(index + raw_data_size / sizeof(u32));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void PushIpcInterface(std::shared_ptr<T> iface) {
|
||||
auto manager{context->GetManager()};
|
||||
inline void Skip(u32 size_in_words, bool set_to_null) {
|
||||
if (set_to_null) std::memset(cmdbuf + index, 0, size_in_words * sizeof(u32));
|
||||
index += size_in_words;
|
||||
}
|
||||
/// @brief Aligns the current position forward to a 16-byte boundary, padding with zeros.
|
||||
inline void AlignWithPadding() { if (index & 3) Skip(u32(4 - (index & 3)), true); }
|
||||
inline u32 GetCurrentOffset() const { return index; }
|
||||
inline void SetCurrentOffset(u32 offset) { index = offset; }
|
||||
|
||||
template <class T> inline void PushIpcInterface(Service::HLERequestContext& ctx, std::shared_ptr<T> iface) {
|
||||
auto manager = ctx.GetManager();
|
||||
if (manager->IsDomain()) {
|
||||
context->AddDomainObject(std::move(iface));
|
||||
ctx.AddDomainObject(std::move(iface));
|
||||
} else {
|
||||
ASSERT(Kernel::GetCurrentProcess(kernel).GetResourceLimit()->Reserve(kernel, Kernel::LimitableResource::SessionCountMax, 1));
|
||||
ASSERT(Kernel::GetCurrentProcess(ctx.kernel).GetResourceLimit()->Reserve(ctx.kernel, Kernel::LimitableResource::SessionCountMax, 1));
|
||||
|
||||
auto* session = Kernel::KSession::Create(kernel);
|
||||
session->Initialize(kernel, nullptr, 0);
|
||||
Kernel::KSession::Register(kernel, session);
|
||||
auto* session = Kernel::KSession::Create(ctx.kernel);
|
||||
session->Initialize(ctx.kernel, nullptr, 0);
|
||||
Kernel::KSession::Register(ctx.kernel, session);
|
||||
|
||||
auto next_manager = std::make_shared<Service::SessionRequestManager>(
|
||||
kernel, manager->GetServerManager());
|
||||
auto next_manager = std::make_shared<Service::SessionRequestManager>(ctx.kernel, manager->GetServerManager());
|
||||
next_manager->SetSessionHandler(iface);
|
||||
manager->GetServerManager().RegisterSession(&session->GetServerSession(), next_manager);
|
||||
|
||||
context->AddMoveObject(&session->GetClientSession());
|
||||
ctx.AddMoveObject(&session->GetClientSession());
|
||||
}
|
||||
}
|
||||
|
||||
template <class T, class... Args>
|
||||
void PushIpcInterface(Args&&... args) {
|
||||
PushIpcInterface<T>(std::make_shared<T>(std::forward<Args>(args)...));
|
||||
template <class T, class... Args> inline void PushIpcInterface(Service::HLERequestContext& ctx, Args&&... args) {
|
||||
PushIpcInterface<T>(ctx, std::make_shared<T>(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
void PushImpl(s8 value);
|
||||
@@ -184,59 +147,38 @@ public:
|
||||
void PushImpl(bool value);
|
||||
void PushImpl(Result value);
|
||||
|
||||
template <typename T>
|
||||
void Push(T value) {
|
||||
template <typename T> inline void Push(T value) {
|
||||
return PushImpl(value);
|
||||
}
|
||||
|
||||
template <typename First, typename... Other>
|
||||
void Push(const First& first_value, const Other&... other_values);
|
||||
|
||||
/**
|
||||
* Helper function for pushing strongly-typed enumeration values.
|
||||
*
|
||||
* @tparam Enum The enumeration type to be pushed
|
||||
*
|
||||
* @param value The value to push.
|
||||
*
|
||||
* @note The underlying size of the enumeration type is the size of the
|
||||
* data that gets pushed. e.g. "enum class SomeEnum : u16" will
|
||||
* push a u16-sized amount of data.
|
||||
*/
|
||||
template <typename Enum>
|
||||
void PushEnum(Enum value) {
|
||||
/// @brief Helper function for pushing strongly-typed enumeration values.
|
||||
/// @tparam Enum The enumeration type to be pushed
|
||||
/// @param value The value to push.
|
||||
/// @note The underlying size of the enumeration type is the size of the data that gets pushed.
|
||||
/// e.g. "enum class SomeEnum : u16" will push a u16-sized amount of data.
|
||||
template <typename Enum> inline void PushEnum(Enum value) {
|
||||
static_assert(std::is_enum_v<Enum>, "T must be an enum type within a PushEnum call.");
|
||||
static_assert(!std::is_convertible_v<Enum, int>,
|
||||
"enum type in PushEnum must be a strongly typed enum.");
|
||||
static_assert(!std::is_convertible_v<Enum, int>, "enum type in PushEnum must be a strongly typed enum.");
|
||||
Push(static_cast<std::underlying_type_t<Enum>>(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copies the content of the given trivially copyable class to the buffer as a normal
|
||||
* param
|
||||
* @note: The input class must be correctly packed/padded to fit hardware layout.
|
||||
*/
|
||||
template <typename T>
|
||||
void PushRaw(const T& value);
|
||||
/// @brief Copies the content of the given trivially copyable class to the buffer as a normal param
|
||||
/// @note: The input class must be correctly packed/padded to fit hardware layout.
|
||||
template <typename T> void PushRaw(const T& value);
|
||||
template <typename... O> void PushMoveObjects(Service::HLERequestContext& ctx, O*... pointers);
|
||||
template <typename... O> void PushMoveObjects(Service::HLERequestContext& ctx, O&... pointers);
|
||||
template <typename... O> void PushCopyObjects(Service::HLERequestContext& ctx, O*... pointers);
|
||||
template <typename... O> void PushCopyObjects(Service::HLERequestContext& ctx, O&... pointers);
|
||||
|
||||
template <typename... O>
|
||||
void PushMoveObjects(O*... pointers);
|
||||
|
||||
template <typename... O>
|
||||
void PushMoveObjects(O&... pointers);
|
||||
|
||||
template <typename... O>
|
||||
void PushCopyObjects(O*... pointers);
|
||||
|
||||
template <typename... O>
|
||||
void PushCopyObjects(O&... pointers);
|
||||
|
||||
private:
|
||||
u32* cmdbuf;
|
||||
u32 index = 0;
|
||||
u32 normal_params_size{};
|
||||
u32 num_handles_to_copy{};
|
||||
u32 num_objects_to_move{}; ///< Domain objects or move handles, context dependent
|
||||
u32 data_payload_index{};
|
||||
Kernel::KernelCore& kernel;
|
||||
};
|
||||
|
||||
/// Push ///
|
||||
@@ -251,8 +193,7 @@ inline void ResponseBuilder::PushImpl(u32 value) {
|
||||
|
||||
template <typename T>
|
||||
void ResponseBuilder::PushRaw(const T& value) {
|
||||
static_assert(std::is_trivially_copyable_v<T>,
|
||||
"It's undefined behavior to use memcpy with non-trivially copyable objects");
|
||||
static_assert(std::is_trivially_copyable_v<T>, "It's undefined behavior to use memcpy with non-trivially copyable objects");
|
||||
std::memcpy(cmdbuf + index, &value, sizeof(T));
|
||||
index += (sizeof(T) + 3) / 4; // round up to word length
|
||||
}
|
||||
@@ -272,8 +213,8 @@ inline void ResponseBuilder::PushImpl(s16 value) {
|
||||
}
|
||||
|
||||
inline void ResponseBuilder::PushImpl(s64 value) {
|
||||
PushImpl(static_cast<u32>(value));
|
||||
PushImpl(static_cast<u32>(value >> 32));
|
||||
PushImpl(u32(value));
|
||||
PushImpl(u32(value >> 32));
|
||||
}
|
||||
|
||||
inline void ResponseBuilder::PushImpl(u8 value) {
|
||||
@@ -285,8 +226,8 @@ inline void ResponseBuilder::PushImpl(u16 value) {
|
||||
}
|
||||
|
||||
inline void ResponseBuilder::PushImpl(u64 value) {
|
||||
PushImpl(static_cast<u32>(value));
|
||||
PushImpl(static_cast<u32>(value >> 32));
|
||||
PushImpl(u32(value));
|
||||
PushImpl(u32(value >> 32));
|
||||
}
|
||||
|
||||
inline void ResponseBuilder::PushImpl(float value) {
|
||||
@@ -312,90 +253,88 @@ void ResponseBuilder::Push(const First& first_value, const Other&... other_value
|
||||
}
|
||||
|
||||
template <typename... O>
|
||||
inline void ResponseBuilder::PushCopyObjects(O*... pointers) {
|
||||
inline void ResponseBuilder::PushCopyObjects(Service::HLERequestContext& ctx, O*... pointers) {
|
||||
auto objects = {pointers...};
|
||||
for (auto& object : objects) {
|
||||
context->AddCopyObject(object);
|
||||
ctx.AddCopyObject(object);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... O>
|
||||
inline void ResponseBuilder::PushCopyObjects(O&... pointers) {
|
||||
inline void ResponseBuilder::PushCopyObjects(Service::HLERequestContext& ctx, O&... pointers) {
|
||||
auto objects = {&pointers...};
|
||||
for (auto& object : objects) {
|
||||
context->AddCopyObject(object);
|
||||
ctx.AddCopyObject(object);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... O>
|
||||
inline void ResponseBuilder::PushMoveObjects(O*... pointers) {
|
||||
inline void ResponseBuilder::PushMoveObjects(Service::HLERequestContext& ctx, O*... pointers) {
|
||||
auto objects = {pointers...};
|
||||
for (auto& object : objects) {
|
||||
context->AddMoveObject(object);
|
||||
ctx.AddMoveObject(object);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... O>
|
||||
inline void ResponseBuilder::PushMoveObjects(O&... pointers) {
|
||||
inline void ResponseBuilder::PushMoveObjects(Service::HLERequestContext& ctx, O&... pointers) {
|
||||
auto objects = {&pointers...};
|
||||
for (auto& object : objects) {
|
||||
context->AddMoveObject(object);
|
||||
ctx.AddMoveObject(object);
|
||||
}
|
||||
}
|
||||
|
||||
class RequestParser : public RequestHelperBase {
|
||||
public:
|
||||
explicit RequestParser(u32* command_buffer) : RequestHelperBase(command_buffer) {}
|
||||
|
||||
explicit RequestParser(Service::HLERequestContext& ctx) : RequestHelperBase(ctx) {
|
||||
struct RequestParser {
|
||||
inline explicit RequestParser(u32* command_buffer) : cmdbuf(command_buffer) {}
|
||||
inline explicit RequestParser(Service::HLERequestContext& ctx)
|
||||
: cmdbuf(ctx.CommandBuffer())
|
||||
{
|
||||
// TIPC does not have data payload offset
|
||||
if (!ctx.IsTipc()) {
|
||||
ASSERT_MSG(ctx.GetDataPayloadOffset(), "context is incomplete");
|
||||
Skip(ctx.GetDataPayloadOffset(), false);
|
||||
}
|
||||
|
||||
// Skip the u64 command id, it's already stored in the context
|
||||
static constexpr u32 CommandIdSize = 2;
|
||||
Skip(CommandIdSize, false);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T Pop();
|
||||
inline void Skip(u32 size_in_words, bool set_to_null) {
|
||||
if (set_to_null) std::memset(cmdbuf + index, 0, size_in_words * sizeof(u32));
|
||||
index += size_in_words;
|
||||
}
|
||||
/// @brief Aligns the current position forward to a 16-byte boundary, padding with zeros.
|
||||
inline void AlignWithPadding() { if (index & 3) Skip(u32(4 - (index & 3)), true); }
|
||||
inline u32 GetCurrentOffset() const { return index; }
|
||||
inline void SetCurrentOffset(u32 offset) { index = offset; }
|
||||
|
||||
template <typename T>
|
||||
void Pop(T& value);
|
||||
|
||||
template <typename First, typename... Other>
|
||||
void Pop(First& first_value, Other&... other_values);
|
||||
template <typename T> T Pop();
|
||||
template <typename T> void Pop(T& value);
|
||||
template <typename First, typename... Other> void Pop(First& first_value, Other&... other_values);
|
||||
|
||||
template <typename T>
|
||||
T PopEnum() {
|
||||
static_assert(std::is_enum_v<T>, "T must be an enum type within a PopEnum call.");
|
||||
static_assert(!std::is_convertible_v<T, int>,
|
||||
"enum type in PopEnum must be a strongly typed enum.");
|
||||
return static_cast<T>(Pop<std::underlying_type_t<T>>());
|
||||
static_assert(!std::is_convertible_v<T, int>, "enum type in PopEnum must be a strongly typed enum.");
|
||||
return T(Pop<std::underlying_type_t<T>>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reads the next normal parameters as a struct, by copying it
|
||||
* @note: The output class must be correctly packed/padded to fit hardware layout.
|
||||
*/
|
||||
template <typename T>
|
||||
void PopRaw(T& value);
|
||||
/// @brief Reads the next normal parameters as a struct, by copying it
|
||||
/// @note: The output class must be correctly packed/padded to fit hardware layout.
|
||||
template <typename T> void PopRaw(T& value);
|
||||
|
||||
/**
|
||||
* @brief Reads the next normal parameters as a struct, by copying it into a new value
|
||||
* @note: The output class must be correctly packed/padded to fit hardware layout.
|
||||
*/
|
||||
template <typename T>
|
||||
T PopRaw();
|
||||
/// @brief Reads the next normal parameters as a struct, by copying it into a new value
|
||||
/// @note: The output class must be correctly packed/padded to fit hardware layout.
|
||||
template <typename T> T PopRaw();
|
||||
|
||||
template <class T>
|
||||
std::weak_ptr<T> PopIpcInterface() {
|
||||
ASSERT(context->GetManager()->IsDomain());
|
||||
ASSERT(context->GetDomainMessageHeader().input_object_count > 0);
|
||||
return context->GetDomainHandler<T>(Pop<u32>() - 1);
|
||||
template <class T> [[nodiscard]] std::weak_ptr<T> PopIpcInterface(Service::HLERequestContext& ctx) {
|
||||
ASSERT(ctx.GetManager()->IsDomain());
|
||||
ASSERT(ctx.GetDomainMessageHeader().input_object_count > 0);
|
||||
return ctx.GetDomainHandler<T>(Pop<u32>() - 1);
|
||||
}
|
||||
|
||||
u32* cmdbuf;
|
||||
u32 index = 0;
|
||||
};
|
||||
|
||||
/// Pop ///
|
||||
@@ -407,7 +346,7 @@ inline u32 RequestParser::Pop() {
|
||||
|
||||
template <>
|
||||
inline s32 RequestParser::Pop() {
|
||||
return static_cast<s32>(Pop<u32>());
|
||||
return s32(Pop<u32>());
|
||||
}
|
||||
|
||||
// Ignore the -Wclass-memaccess warning on memcpy for non-trivially default constructible objects.
|
||||
@@ -417,8 +356,7 @@ inline s32 RequestParser::Pop() {
|
||||
#endif
|
||||
template <typename T>
|
||||
void RequestParser::PopRaw(T& value) {
|
||||
static_assert(std::is_trivially_copyable_v<T>,
|
||||
"It's undefined behavior to use memcpy with non-trivially copyable objects");
|
||||
static_assert(std::is_trivially_copyable_v<T>, "It's undefined behavior to use memcpy with non-trivially copyable objects");
|
||||
std::memcpy(&value, cmdbuf + index, sizeof(T));
|
||||
index += (sizeof(T) + 3) / 4; // round up to word length
|
||||
}
|
||||
|
||||
@@ -353,7 +353,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<ILogger>(system);
|
||||
rb.PushIpcInterface<ILogger>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -151,7 +151,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IAm>(system);
|
||||
rb.PushIpcInterface<IAm>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -173,7 +173,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<MFIUser>(system);
|
||||
rb.PushIpcInterface<MFIUser>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -195,7 +195,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IUser>(system);
|
||||
rb.PushIpcInterface<IUser>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -217,7 +217,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<ISystem>(system);
|
||||
rb.PushIpcInterface<ISystem>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -143,7 +143,7 @@ void NfcInterface::AttachAvailabilityChangeEvent(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(GetManager()->AttachAvailabilityChangeEvent());
|
||||
rb.PushCopyObjects(ctx, GetManager()->AttachAvailabilityChangeEvent());
|
||||
}
|
||||
|
||||
void NfcInterface::StartDetection(HLERequestContext& ctx) {
|
||||
@@ -203,7 +203,7 @@ void NfcInterface::AttachActivateEvent(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(result);
|
||||
rb.PushCopyObjects(out_event);
|
||||
rb.PushCopyObjects(ctx, out_event);
|
||||
}
|
||||
|
||||
void NfcInterface::AttachDeactivateEvent(HLERequestContext& ctx) {
|
||||
@@ -217,7 +217,7 @@ void NfcInterface::AttachDeactivateEvent(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(result);
|
||||
rb.PushCopyObjects(out_event);
|
||||
rb.PushCopyObjects(ctx, out_event);
|
||||
}
|
||||
|
||||
void NfcInterface::SetNfcEnabled(HLERequestContext& ctx) {
|
||||
|
||||
@@ -157,7 +157,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IUser>(system);
|
||||
rb.PushIpcInterface<IUser>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -179,7 +179,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<ISystem>(system);
|
||||
rb.PushIpcInterface<ISystem>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -201,7 +201,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IDebug>(system);
|
||||
rb.PushIpcInterface<IDebug>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -307,7 +307,7 @@ private:
|
||||
void GetSystemEventReadableHandle(HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(evt_scan_complete->GetReadableEvent(),
|
||||
rb.PushCopyObjects(ctx, evt_scan_complete->GetReadableEvent(),
|
||||
evt_processing->GetReadableEvent());
|
||||
}
|
||||
|
||||
@@ -452,7 +452,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(event1->GetReadableEvent(), event2->GetReadableEvent());
|
||||
rb.PushCopyObjects(ctx, event1->GetReadableEvent(), event2->GetReadableEvent());
|
||||
}
|
||||
|
||||
void Cancel(HLERequestContext& ctx) {
|
||||
@@ -528,7 +528,7 @@ void IGeneralService::CreateScanRequest(HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IScanRequest>(system);
|
||||
rb.PushIpcInterface<IScanRequest>(ctx, system);
|
||||
}
|
||||
|
||||
void IGeneralService::CreateRequest(HLERequestContext& ctx) {
|
||||
@@ -537,7 +537,7 @@ void IGeneralService::CreateRequest(HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IRequest>(system);
|
||||
rb.PushIpcInterface<IRequest>(ctx, system);
|
||||
}
|
||||
|
||||
void IGeneralService::GetCurrentNetworkProfile(HLERequestContext& ctx) {
|
||||
@@ -716,7 +716,7 @@ void IGeneralService::GetNetworkProfile(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<INetworkProfile>(system);
|
||||
rb.PushIpcInterface<INetworkProfile>(ctx, system);
|
||||
}
|
||||
|
||||
void IGeneralService::SetNetworkProfile(HLERequestContext& ctx) {
|
||||
@@ -869,7 +869,7 @@ void IGeneralService::CreateTemporaryNetworkProfile(HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 6, 0, 1};
|
||||
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<INetworkProfile>(system);
|
||||
rb.PushIpcInterface<INetworkProfile>(ctx, system);
|
||||
rb.PushRaw<u128>(uuid);
|
||||
}
|
||||
|
||||
@@ -1124,7 +1124,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IGeneralService>(system);
|
||||
rb.PushIpcInterface<IGeneralService>(ctx, system);
|
||||
}
|
||||
|
||||
void CreateGeneralService(HLERequestContext& ctx) {
|
||||
@@ -1132,7 +1132,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IGeneralService>(system);
|
||||
rb.PushIpcInterface<IGeneralService>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ private:
|
||||
LOG_WARNING(Service_NIM, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IShopServiceAsync>(system);
|
||||
rb.PushIpcInterface<IShopServiceAsync>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -75,7 +75,7 @@ private:
|
||||
LOG_WARNING(Service_NIM, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IShopServiceAccessor>(system);
|
||||
rb.PushIpcInterface<IShopServiceAccessor>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -336,7 +336,7 @@ private:
|
||||
LOG_DEBUG(Service_NIM, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IShopServiceAccessServer>(system);
|
||||
rb.PushIpcInterface<IShopServiceAccessServer>(ctx, system);
|
||||
}
|
||||
|
||||
void IsLargeResourceAvailable(HLERequestContext& ctx) {
|
||||
@@ -356,7 +356,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IShopServiceAccessServer>(system);
|
||||
rb.PushIpcInterface<IShopServiceAccessServer>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -439,7 +439,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(finished_event->GetReadableEvent());
|
||||
rb.PushCopyObjects(ctx, finished_event->GetReadableEvent());
|
||||
}
|
||||
|
||||
void GetResult(HLERequestContext& ctx) {
|
||||
@@ -500,7 +500,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IEnsureNetworkClockAvailabilityService>(system);
|
||||
rb.PushIpcInterface<IEnsureNetworkClockAvailabilityService>(ctx, system);
|
||||
}
|
||||
|
||||
// TODO(ogniK): Do we need these?
|
||||
|
||||
@@ -359,8 +359,8 @@ void IReadOnlyApplicationControlDataInterface::ListApplicationTitle(HLERequestCo
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(async_value->ReadableEvent());
|
||||
rb.PushIpcInterface(std::move(async_value));
|
||||
rb.PushCopyObjects(ctx, async_value->ReadableEvent());
|
||||
rb.PushIpcInterface(ctx, std::move(async_value));
|
||||
}
|
||||
|
||||
Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData3(
|
||||
|
||||
@@ -199,7 +199,7 @@ void NVDRV::QueryEvent(HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 3, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
auto& readable_event = event->GetReadableEvent();
|
||||
rb.PushCopyObjects(readable_event);
|
||||
rb.PushCopyObjects(ctx, readable_event);
|
||||
rb.PushEnum(NvResult::Success);
|
||||
} else {
|
||||
LOG_ERROR(Service_NVDRV, "Invalid event request!");
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Core {
|
||||
@@ -28,6 +30,15 @@ public:
|
||||
inline explicit Process(Core::System& system) noexcept : m_system(system) {}
|
||||
inline ~Process() { this->Finalize(); }
|
||||
|
||||
Process(const Process&) = delete;
|
||||
Process& operator=(const Process&) = delete;
|
||||
Process& operator=(Process&&) = delete;
|
||||
inline Process(Process&& other) noexcept
|
||||
: m_system(other.m_system), m_process(std::exchange(other.m_process, nullptr)),
|
||||
m_main_thread_stack_size(std::exchange(other.m_main_thread_stack_size, 0)),
|
||||
m_main_thread_priority(std::exchange(other.m_main_thread_priority, 0)),
|
||||
m_process_started(std::exchange(other.m_process_started, false)) {}
|
||||
|
||||
bool Initialize(Loader::AppLoader& loader, Loader::ResultStatus& out_load_result);
|
||||
void Finalize();
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -124,7 +127,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IClkrstSession>(system, device_code);
|
||||
rb.PushIpcInterface<IClkrstSession>(ctx, system, device_code);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -161,7 +161,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 10, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(*process);
|
||||
rb.PushCopyObjects(ctx, *process);
|
||||
rb.PushRaw(program_location);
|
||||
rb.PushRaw(override_status);
|
||||
}
|
||||
|
||||
@@ -147,7 +147,7 @@ void IAlarmService::CreateWakeupAlarm(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<ISteadyClockAlarm>(system, m_alarms, AlarmType::WakeupAlarm);
|
||||
rb.PushIpcInterface<ISteadyClockAlarm>(ctx, system, m_alarms, AlarmType::WakeupAlarm);
|
||||
}
|
||||
|
||||
void IAlarmService::CreateBackgroundTaskAlarm(HLERequestContext& ctx) {
|
||||
@@ -155,7 +155,7 @@ void IAlarmService::CreateBackgroundTaskAlarm(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<ISteadyClockAlarm>(system, m_alarms, AlarmType::BackgroundTaskAlarm);
|
||||
rb.PushIpcInterface<ISteadyClockAlarm>(ctx, system, m_alarms, AlarmType::BackgroundTaskAlarm);
|
||||
}
|
||||
|
||||
ISteadyClockAlarm::ISteadyClockAlarm(Core::System& system_, Alarms& alarms, AlarmType type)
|
||||
@@ -179,7 +179,7 @@ void ISteadyClockAlarm::GetAlarmEvent(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(m_alarm.GetEventHandle());
|
||||
rb.PushCopyObjects(ctx, m_alarm.GetEventHandle());
|
||||
}
|
||||
|
||||
void ISteadyClockAlarm::Enable(HLERequestContext& ctx) {
|
||||
|
||||
@@ -67,7 +67,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(state_change_event->GetReadableEvent());
|
||||
rb.PushCopyObjects(ctx, state_change_event->GetReadableEvent());
|
||||
}
|
||||
|
||||
void UnbindStateChangeEvent(HLERequestContext& ctx) {
|
||||
@@ -186,7 +186,7 @@ void PSM::OpenSession(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IPsmSession>(system);
|
||||
rb.PushIpcInterface<IPsmSession>(ctx, system);
|
||||
}
|
||||
|
||||
void PSM::GetBatteryVoltageState(HLERequestContext& ctx) {
|
||||
|
||||
@@ -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-3.0-or-later
|
||||
|
||||
@@ -82,7 +85,7 @@ void TS::OpenSession(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<ISession>(system);
|
||||
rb.PushIpcInterface<ISession>(ctx, system);
|
||||
}
|
||||
|
||||
} // namespace Service::PTM
|
||||
|
||||
@@ -140,7 +140,7 @@ void SM::GetServiceCmif(HLERequestContext& ctx) {
|
||||
if (result == ResultSuccess) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
|
||||
rb.Push(result);
|
||||
rb.PushMoveObjects(client_session);
|
||||
rb.PushMoveObjects(ctx, client_session);
|
||||
} else {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
@@ -157,7 +157,7 @@ void SM::GetServiceTipc(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
|
||||
rb.Push(result);
|
||||
rb.PushMoveObjects(result == ResultSuccess ? client_session : nullptr);
|
||||
rb.PushMoveObjects(ctx, result == ResultSuccess ? client_session : nullptr);
|
||||
}
|
||||
|
||||
static std::string PopServiceName(IPC::RequestParser& rp) {
|
||||
@@ -230,8 +230,7 @@ void SM::RegisterServiceImpl(HLERequestContext& ctx, std::string name, u32 max_s
|
||||
max_session_count, is_light);
|
||||
|
||||
Kernel::KServerPort* server_port{};
|
||||
if (const auto result = service_manager.RegisterService(std::addressof(server_port), name,
|
||||
max_session_count, nullptr);
|
||||
if (const auto result = service_manager.RegisterService(std::addressof(server_port), name, max_session_count, nullptr);
|
||||
result.IsError()) {
|
||||
LOG_ERROR(Service_SM, "failed to register service with error_code={:08X}", result.raw);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@@ -241,7 +240,7 @@ void SM::RegisterServiceImpl(HLERequestContext& ctx, std::string name, u32 max_s
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushMoveObjects(server_port);
|
||||
rb.PushMoveObjects(ctx, server_port);
|
||||
}
|
||||
|
||||
void SM::UnregisterService(HLERequestContext& ctx) {
|
||||
|
||||
@@ -60,7 +60,7 @@ void Controller::CloneCurrentObject(HLERequestContext& ctx) {
|
||||
// We succeeded.
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushMoveObjects(session->GetClientSession());
|
||||
rb.PushMoveObjects(ctx, session->GetClientSession());
|
||||
}
|
||||
|
||||
void Controller::CloneCurrentObjectEx(HLERequestContext& ctx) {
|
||||
|
||||
@@ -543,8 +543,7 @@ private:
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(res);
|
||||
if (res == ResultSuccess) {
|
||||
rb.PushIpcInterface<ISslConnection>(system, ssl_version, shared_data,
|
||||
std::move(backend));
|
||||
rb.PushIpcInterface<ISslConnection>(ctx, system, ssl_version, shared_data, std::move(backend));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -624,12 +623,11 @@ private:
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto parameters = rp.PopRaw<Parameters>();
|
||||
|
||||
LOG_WARNING(Service_SSL, "(STUBBED) called, api_version={}, pid_placeholder={}",
|
||||
parameters.ssl_version.api_version, parameters.pid_placeholder);
|
||||
LOG_WARNING(Service_SSL, "(STUBBED) called, api_version={}, pid_placeholder={}", parameters.ssl_version.api_version, parameters.pid_placeholder);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<ISslContext>(system, parameters.ssl_version);
|
||||
rb.PushIpcInterface<ISslContext>(ctx, system, parameters.ssl_version);
|
||||
}
|
||||
|
||||
void SetInterfaceVersion(HLERequestContext& ctx) {
|
||||
|
||||
@@ -155,7 +155,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IPdSession>(system);
|
||||
rb.PushIpcInterface<IPdSession>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -199,7 +199,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IPdCradleSession>(system);
|
||||
rb.PushIpcInterface<IPdCradleSession>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -50,8 +50,8 @@ NPad::NPad(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service
|
||||
auto& controller = controller_data[aruid_index][i];
|
||||
controller.device = hid_core.GetEmulatedControllerByIndex(i);
|
||||
Core::HID::ControllerUpdateCallback engine_callback{
|
||||
.on_change = [this, i](Core::HID::ControllerTriggerType type) {
|
||||
ControllerUpdate(hid_core.kernel, type, i);
|
||||
.on_change = [this, i, kernel = &hid_core.kernel](Core::HID::ControllerTriggerType type) {
|
||||
ControllerUpdate(*kernel, type, i);
|
||||
},
|
||||
.is_npad_service = true,
|
||||
};
|
||||
|
||||
@@ -236,8 +236,11 @@ void LowerGeometryPassthrough(const IR::Program& program, const HostTranslateInf
|
||||
|
||||
IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool,
|
||||
Environment& env, Flow::CFG& cfg, const HostTranslateInfo& host_info) {
|
||||
HostTranslateInfo normalized_host_info{host_info};
|
||||
normalized_host_info.ApplyDescriptorLimitPolicy();
|
||||
|
||||
IR::Program program;
|
||||
program.syntax_list = BuildASL(inst_pool, block_pool, env, cfg, host_info);
|
||||
program.syntax_list = BuildASL(inst_pool, block_pool, env, cfg, normalized_host_info);
|
||||
program.blocks = GenerateBlocks(program.syntax_list);
|
||||
program.post_order_blocks = PostOrder(program.syntax_list.front());
|
||||
program.stage = env.ShaderStage();
|
||||
@@ -260,9 +263,9 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
|
||||
program.info.passthrough.mask[i] = ((mask[i / 32] >> (i % 32)) & 1) == 0;
|
||||
}
|
||||
|
||||
if (!host_info.support_geometry_shader_passthrough) {
|
||||
if (!normalized_host_info.support_geometry_shader_passthrough) {
|
||||
program.output_vertices = GetOutputTopologyVertices(program.output_topology);
|
||||
LowerGeometryPassthrough(program, host_info);
|
||||
LowerGeometryPassthrough(program, normalized_host_info);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -277,16 +280,16 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
|
||||
RemoveUnreachableBlocks(program);
|
||||
|
||||
// Replace instructions before the SSA rewrite
|
||||
if (!host_info.support_float64) {
|
||||
if (!normalized_host_info.support_float64) {
|
||||
Optimization::LowerFp64ToFp32(program);
|
||||
}
|
||||
if (!host_info.support_float16) {
|
||||
if (!normalized_host_info.support_float16) {
|
||||
Optimization::LowerFp16ToFp32(program);
|
||||
}
|
||||
if (!host_info.support_int64) {
|
||||
if (!normalized_host_info.support_int64) {
|
||||
Optimization::LowerInt64ToInt32(program);
|
||||
}
|
||||
if (!host_info.support_conditional_barrier) {
|
||||
if (!normalized_host_info.support_conditional_barrier) {
|
||||
Optimization::ConditionalBarrierPass(program);
|
||||
}
|
||||
Optimization::SsaRewritePass(program);
|
||||
@@ -295,8 +298,8 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
|
||||
|
||||
Optimization::PositionPass(env, program);
|
||||
|
||||
Optimization::GlobalMemoryToStorageBufferPass(program, host_info);
|
||||
Optimization::TexturePass(env, program, host_info);
|
||||
Optimization::GlobalMemoryToStorageBufferPass(program, normalized_host_info);
|
||||
Optimization::TexturePass(env, program, normalized_host_info);
|
||||
|
||||
if (Settings::values.resolution_info.active || Settings::values.rescale_hack.GetValue()) {
|
||||
Optimization::RescalingPass(program);
|
||||
@@ -306,7 +309,7 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
|
||||
Optimization::VerificationPass(program);
|
||||
}
|
||||
Optimization::CollectShaderInfoPass(env, program);
|
||||
Optimization::LayerPass(program, host_info);
|
||||
Optimization::LayerPass(program, normalized_host_info);
|
||||
Optimization::VendorWorkaroundPass(program);
|
||||
|
||||
CollectInterpolationInfo(env, program);
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Shader {
|
||||
|
||||
// Try to keep entries here to a minimum
|
||||
@@ -13,20 +15,52 @@ namespace Shader {
|
||||
|
||||
/// Misc information about the host
|
||||
struct HostTranslateInfo {
|
||||
static constexpr u32 DEFAULT_DESCRIPTOR_LIMIT = 1024;
|
||||
|
||||
u64 min_ssbo_alignment{}; ///< Minimum alignment supported by the device for SSBOs
|
||||
u32 max_per_stage_descriptor_sampled_images{}; ///< maximum sampled descriptors per stage
|
||||
u32 max_per_stage_resources{}; ///< maximum resources per stage
|
||||
u32 max_descriptor_set_samplers{};
|
||||
u32 max_descriptor_set_uniform_buffers{};
|
||||
u32 max_descriptor_set_uniform_buffers_dynamic{};
|
||||
u32 max_descriptor_set_storage_buffers{};
|
||||
u32 max_descriptor_set_storage_buffers_dynamic{};
|
||||
u32 max_descriptor_set_sampled_images{};
|
||||
u32 max_descriptor_set_storage_images{};
|
||||
u32 max_descriptor_set_input_attachements{};
|
||||
bool support_float64{}; ///< True when the device supports 64-bit floats
|
||||
bool support_float16{}; ///< True when the device supports 16-bit floats
|
||||
bool support_int64{}; ///< True when the device supports 64-bit integers
|
||||
bool needs_demote_reorder{}; ///< True when the device needs DemoteToHelperInvocation reordered
|
||||
bool support_snorm_render_buffer{}; ///< True when the device supports SNORM render buffers
|
||||
bool support_viewport_index_layer{}; ///< True when the device supports gl_Layer in VS
|
||||
u32 min_ssbo_alignment{}; ///< Minimum alignment supported by the device for SSBOs
|
||||
u32 max_per_stage_descriptor_sampled_images{1024}; ///< maximum sampled descriptors per stage
|
||||
u32 max_per_stage_resources{4096}; ///< maximum resources per stage
|
||||
u32 max_descriptor_set_sampled_images{1024}; ///< maximum sampled descriptors per set
|
||||
bool support_geometry_shader_passthrough{}; ///< True when the device supports geometry
|
||||
///< passthrough shaders
|
||||
bool support_conditional_barrier{}; ///< True when the device supports barriers in conditional
|
||||
///< control flow
|
||||
|
||||
void ApplyDescriptorLimitPolicy() noexcept {
|
||||
if (min_ssbo_alignment == 0) {
|
||||
min_ssbo_alignment = 1;
|
||||
}
|
||||
ApplyDescriptorLimitFallback(max_per_stage_descriptor_sampled_images);
|
||||
ApplyDescriptorLimitFallback(max_per_stage_resources);
|
||||
ApplyDescriptorLimitFallback(max_descriptor_set_samplers);
|
||||
ApplyDescriptorLimitFallback(max_descriptor_set_uniform_buffers);
|
||||
ApplyDescriptorLimitFallback(max_descriptor_set_uniform_buffers_dynamic);
|
||||
ApplyDescriptorLimitFallback(max_descriptor_set_storage_buffers);
|
||||
ApplyDescriptorLimitFallback(max_descriptor_set_storage_buffers_dynamic);
|
||||
ApplyDescriptorLimitFallback(max_descriptor_set_sampled_images);
|
||||
ApplyDescriptorLimitFallback(max_descriptor_set_storage_images);
|
||||
ApplyDescriptorLimitFallback(max_descriptor_set_input_attachements);
|
||||
}
|
||||
|
||||
private:
|
||||
static void ApplyDescriptorLimitFallback(u32& limit) noexcept {
|
||||
if (limit == 0) {
|
||||
limit = DEFAULT_DESCRIPTOR_LIMIT;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Shader
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -545,7 +548,7 @@ void GlobalMemoryToStorageBufferPass(IR::Program& program, const HostTranslateIn
|
||||
IR::Block* const block{storage_inst.block};
|
||||
IR::Inst* const inst{storage_inst.inst};
|
||||
const IR::U32 offset{
|
||||
StorageOffset(*block, *inst, storage_buffer, host_info.min_ssbo_alignment)};
|
||||
StorageOffset(*block, *inst, storage_buffer, u32(host_info.min_ssbo_alignment))};
|
||||
Replace(*block, *inst, index, offset);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,71 +32,62 @@ struct TextureInst {
|
||||
using TextureInstVector = boost::container::small_vector<TextureInst, 24>;
|
||||
|
||||
constexpr u32 DESCRIPTOR_SIZE = 8;
|
||||
constexpr u32 DESCRIPTOR_SIZE_SHIFT = static_cast<u32>(std::countr_zero(DESCRIPTOR_SIZE));
|
||||
constexpr u32 DYNAMIC_DESCRIPTOR_CBUF_BYTES = 16 * 1024;
|
||||
constexpr u32 MAX_DYNAMIC_DESCRIPTOR_COUNT = 1024;
|
||||
constexpr u32 DESCRIPTOR_SIZE_SHIFT = u32(std::countr_zero(DESCRIPTOR_SIZE));
|
||||
constexpr u32 DESCRIPTOR_MAX_COUNT = 1024;
|
||||
|
||||
u32 DynamicDescriptorSizeShift(const IR::U32& dynamic_offset) {
|
||||
const IR::Inst* const inst{dynamic_offset.InstRecursive()};
|
||||
if (!inst || inst->GetOpcode() != IR::Opcode::ShiftLeftLogical32) {
|
||||
const IR::Inst* const inst = dynamic_offset.InstRecursive();
|
||||
if (!inst || inst->GetOpcode() != IR::Opcode::ShiftLeftLogical32)
|
||||
return DESCRIPTOR_SIZE_SHIFT;
|
||||
}
|
||||
const IR::Value shift{inst->Arg(1)};
|
||||
if (!shift.IsImmediate()) {
|
||||
const IR::Value shift = inst->Arg(1);
|
||||
if (!shift.IsImmediate())
|
||||
return DESCRIPTOR_SIZE_SHIFT;
|
||||
}
|
||||
const u32 size_shift{shift.U32()};
|
||||
return size_shift >= DESCRIPTOR_SIZE_SHIFT && size_shift < 31 ? size_shift
|
||||
: DESCRIPTOR_SIZE_SHIFT;
|
||||
const u32 size_shift = shift.U32();
|
||||
return size_shift >= DESCRIPTOR_SIZE_SHIFT && size_shift < 31 ? size_shift : DESCRIPTOR_SIZE_SHIFT;
|
||||
}
|
||||
|
||||
u32 DynamicDescriptorCount(u32 base_offset, u32 size_shift) {
|
||||
if (size_shift >= 31 || base_offset >= DYNAMIC_DESCRIPTOR_CBUF_BYTES) {
|
||||
u32 DynamicDescriptorCount(u32 base_offset, u32 size_shift, u32 max_descriptors) {
|
||||
auto const descriptor_limit = (std::max)(1U, max_descriptors);
|
||||
auto const max_cbuf_bytes = 16 * descriptor_limit;
|
||||
if (size_shift >= 31 || base_offset >= max_cbuf_bytes)
|
||||
return 1;
|
||||
}
|
||||
const u32 stride{1U << size_shift};
|
||||
const u32 available{DYNAMIC_DESCRIPTOR_CBUF_BYTES - base_offset};
|
||||
if (available < DESCRIPTOR_SIZE) {
|
||||
auto const stride = 1U << size_shift;
|
||||
auto const available = max_cbuf_bytes - base_offset;
|
||||
if (available < DESCRIPTOR_SIZE)
|
||||
return 1;
|
||||
}
|
||||
const u32 available_count{1U + (available - DESCRIPTOR_SIZE) / stride};
|
||||
return std::min(MAX_DYNAMIC_DESCRIPTOR_COUNT, available_count);
|
||||
auto const available_count = 1U + (available - DESCRIPTOR_SIZE) / stride;
|
||||
return std::min(descriptor_limit, available_count);
|
||||
}
|
||||
|
||||
u32 SaturatingSub(u32 lhs, u32 rhs) {
|
||||
return lhs > rhs ? lhs - rhs : 0;
|
||||
}
|
||||
|
||||
template <typename Descriptors>
|
||||
u32 StaticDescriptorCount(const Descriptors& descriptors) {
|
||||
u32 count{};
|
||||
for (const auto& desc : descriptors) {
|
||||
if (desc.count <= 1) {
|
||||
count += desc.count;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
template <typename T>
|
||||
[[nodiscard]] u32 StaticDescriptorCount(T const& descriptors) noexcept {
|
||||
return std::accumulate(descriptors.cbegin(), descriptors.cend(), 0U, [](auto const& acc, auto const& e) {
|
||||
return acc + (e.count <= 1 ? e.count : 0);
|
||||
});
|
||||
}
|
||||
|
||||
u32 DynamicSampledTextureCap(const Info& info, const HostTranslateInfo& host_info,
|
||||
u32 dynamic_arrays) {
|
||||
if (dynamic_arrays == 0) {
|
||||
return MAX_DYNAMIC_DESCRIPTOR_COUNT;
|
||||
u32 DynamicSampledTextureCap(const Info& info, const HostTranslateInfo& host_info, u32 dynamic_arrays) {
|
||||
auto const sampled_limit = (std::max)(1U, std::min(host_info.max_per_stage_descriptor_sampled_images,
|
||||
host_info.max_descriptor_set_sampled_images));
|
||||
auto const resource_limit = (std::max)(1U, host_info.max_per_stage_resources);
|
||||
if (dynamic_arrays > 0) {
|
||||
auto const sampled_static_count = StaticDescriptorCount(info.texture_buffer_descriptors) + StaticDescriptorCount(info.texture_descriptors);
|
||||
auto const resource_static_count =
|
||||
NumDescriptors(info.constant_buffer_descriptors)
|
||||
+ NumDescriptors(info.storage_buffers_descriptors)
|
||||
+ sampled_static_count + NumDescriptors(info.image_buffer_descriptors)
|
||||
+ NumDescriptors(info.image_descriptors);
|
||||
auto const sampled_budget = SaturatingSub(sampled_limit, sampled_static_count);
|
||||
auto const resource_budget = SaturatingSub(resource_limit, resource_static_count);
|
||||
auto const sampled_cap = sampled_budget / dynamic_arrays;
|
||||
auto const resource_cap = resource_budget / dynamic_arrays;
|
||||
return (std::max)(1U, (std::min)(sampled_cap, resource_cap));
|
||||
}
|
||||
const u32 sampled_static_count{StaticDescriptorCount(info.texture_buffer_descriptors) +
|
||||
StaticDescriptorCount(info.texture_descriptors)};
|
||||
const u32 resource_static_count{
|
||||
NumDescriptors(info.constant_buffer_descriptors) +
|
||||
NumDescriptors(info.storage_buffers_descriptors) + sampled_static_count +
|
||||
NumDescriptors(info.image_buffer_descriptors) + NumDescriptors(info.image_descriptors)};
|
||||
const u32 sampled_limit{std::min(host_info.max_per_stage_descriptor_sampled_images,
|
||||
host_info.max_descriptor_set_sampled_images)};
|
||||
const u32 sampled_budget{SaturatingSub(sampled_limit, sampled_static_count)};
|
||||
const u32 resource_budget{SaturatingSub(host_info.max_per_stage_resources,
|
||||
resource_static_count)};
|
||||
const u32 sampled_cap{sampled_budget / dynamic_arrays};
|
||||
const u32 resource_cap{resource_budget / dynamic_arrays};
|
||||
return std::max(1U, std::min({MAX_DYNAMIC_DESCRIPTOR_COUNT, sampled_cap, resource_cap}));
|
||||
return (std::min)({DESCRIPTOR_MAX_COUNT, sampled_limit, resource_limit});
|
||||
}
|
||||
|
||||
IR::Opcode IndexedInstruction(const IR::Inst& inst) {
|
||||
@@ -304,21 +295,23 @@ static inline bool IsTexturePixelFormatIntegerCached(Environment& env,
|
||||
}
|
||||
|
||||
|
||||
std::optional<ConstBufferAddr> Track(const IR::Value& value, Environment& env);
|
||||
static inline std::optional<ConstBufferAddr> TrackCached(const IR::Value& v, Environment& env) {
|
||||
std::optional<ConstBufferAddr> Track(const IR::Value& value, Environment& env, const HostTranslateInfo& host_info);
|
||||
static inline std::optional<ConstBufferAddr> TrackCached(const IR::Value& v, Environment& env, const HostTranslateInfo& host_info) {
|
||||
if (const IR::Inst* key = v.InstRecursive()) {
|
||||
if (auto it = env.track_cache.find(key); it != env.track_cache.end()) return it->second;
|
||||
auto found = Track(v, env);
|
||||
auto found = Track(v, env, host_info);
|
||||
if (found) env.track_cache.emplace(key, *found);
|
||||
return found;
|
||||
}
|
||||
return Track(v, env);
|
||||
return Track(v, env, host_info);
|
||||
}
|
||||
|
||||
std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environment& env);
|
||||
std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environment& env, const HostTranslateInfo& host_info);
|
||||
|
||||
std::optional<ConstBufferAddr> Track(const IR::Value& value, Environment& env) {
|
||||
return IR::BreadthFirstSearch(value, [&env](const IR::Inst* inst) { return TryGetConstBuffer(inst, env); });
|
||||
std::optional<ConstBufferAddr> Track(const IR::Value& value, Environment& env, const HostTranslateInfo& host_info) {
|
||||
return IR::BreadthFirstSearch(value, [&env, &host_info](const IR::Inst* inst) {
|
||||
return TryGetConstBuffer(inst, env, host_info);
|
||||
});
|
||||
}
|
||||
|
||||
std::optional<u32> TryGetConstant(IR::Value& value, Environment& env) {
|
||||
@@ -342,13 +335,13 @@ std::optional<u32> TryGetConstant(IR::Value& value, Environment& env) {
|
||||
return ReadCbufCached(env, index_number, offset_number);
|
||||
}
|
||||
|
||||
std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environment& env) {
|
||||
std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environment& env, const HostTranslateInfo& host_info) {
|
||||
switch (inst->GetOpcode()) {
|
||||
default:
|
||||
return std::nullopt;
|
||||
case IR::Opcode::BitwiseOr32: {
|
||||
std::optional lhs{TrackCached(inst->Arg(0), env)};
|
||||
std::optional rhs{TrackCached(inst->Arg(1), env)};
|
||||
std::optional lhs{TrackCached(inst->Arg(0), env, host_info)};
|
||||
std::optional rhs{TrackCached(inst->Arg(1), env, host_info)};
|
||||
if (!lhs || !rhs) {
|
||||
return std::nullopt;
|
||||
}
|
||||
@@ -378,12 +371,11 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environme
|
||||
if (!shift.IsImmediate()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
std::optional lhs{TrackCached(inst->Arg(0), env)};
|
||||
std::optional lhs{TrackCached(inst->Arg(0), env, host_info)};
|
||||
if (lhs) {
|
||||
lhs->shift_left = shift.U32();
|
||||
}
|
||||
return lhs;
|
||||
break;
|
||||
}
|
||||
case IR::Opcode::BitwiseAnd32: {
|
||||
IR::Value op1{inst->Arg(0)};
|
||||
@@ -407,7 +399,7 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environme
|
||||
return std::nullopt;
|
||||
} while (false);
|
||||
}
|
||||
std::optional lhs{TrackCached(op1, env)};
|
||||
std::optional lhs{TrackCached(op1, env, host_info)};
|
||||
if (lhs) {
|
||||
lhs->shift_left = static_cast<u32>(std::countr_zero(op2.U32()));
|
||||
}
|
||||
@@ -453,7 +445,10 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environme
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
const u32 size_shift{DynamicDescriptorSizeShift(dynamic_offset)};
|
||||
auto const size_shift = DynamicDescriptorSizeShift(dynamic_offset);
|
||||
auto const sampled_limit = (std::max)(1U, (std::min)(host_info.max_per_stage_descriptor_sampled_images,
|
||||
host_info.max_descriptor_set_sampled_images));
|
||||
auto const resource_limit = (std::max)(1U, host_info.max_per_stage_resources);
|
||||
return ConstBufferAddr{
|
||||
.index = index.U32(),
|
||||
.offset = base_offset,
|
||||
@@ -462,15 +457,15 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environme
|
||||
.secondary_offset = 0,
|
||||
.secondary_shift_left = 0,
|
||||
.dynamic_offset = dynamic_offset,
|
||||
.count = DynamicDescriptorCount(base_offset, size_shift),
|
||||
.count = DynamicDescriptorCount(base_offset, size_shift, (std::min)({DESCRIPTOR_MAX_COUNT, sampled_limit, resource_limit})),
|
||||
.has_secondary = false,
|
||||
};
|
||||
}
|
||||
|
||||
TextureInst MakeInst(Environment& env, IR::Block* block, IR::Inst& inst) {
|
||||
TextureInst MakeInst(Environment& env, IR::Block* block, IR::Inst& inst, const HostTranslateInfo& host_info) {
|
||||
ConstBufferAddr addr;
|
||||
if (IsBindless(inst)) {
|
||||
const std::optional<ConstBufferAddr> track_addr{TrackCached(inst.Arg(0), env)};
|
||||
const std::optional<ConstBufferAddr> track_addr{TrackCached(inst.Arg(0), env, host_info)};
|
||||
|
||||
if (!track_addr) {
|
||||
throw NotImplementedException("Failed to track bindless texture constant buffer");
|
||||
@@ -506,15 +501,15 @@ u32 GetTextureHandle(Environment& env, const ConstBufferAddr& cbuf) {
|
||||
return lhs_raw | rhs_raw;
|
||||
}
|
||||
|
||||
[[maybe_unused]]TextureType ReadTextureType(Environment& env, const ConstBufferAddr& cbuf) {
|
||||
[[maybe_unused]] TextureType ReadTextureType(Environment& env, const ConstBufferAddr& cbuf) {
|
||||
return env.ReadTextureType(GetTextureHandle(env, cbuf));
|
||||
}
|
||||
|
||||
[[maybe_unused]]TexturePixelFormat ReadTexturePixelFormat(Environment& env, const ConstBufferAddr& cbuf) {
|
||||
[[maybe_unused]] TexturePixelFormat ReadTexturePixelFormat(Environment& env, const ConstBufferAddr& cbuf) {
|
||||
return env.ReadTexturePixelFormat(GetTextureHandle(env, cbuf));
|
||||
}
|
||||
|
||||
[[maybe_unused]]bool IsTexturePixelFormatInteger(Environment& env, const ConstBufferAddr& cbuf) {
|
||||
[[maybe_unused]] bool IsTexturePixelFormatInteger(Environment& env, const ConstBufferAddr& cbuf) {
|
||||
return env.IsTexturePixelFormatInteger(GetTextureHandle(env, cbuf));
|
||||
}
|
||||
|
||||
@@ -675,7 +670,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
|
||||
if (!IsTextureInstruction(inst)) {
|
||||
continue;
|
||||
}
|
||||
to_replace.push_back(MakeInst(env, block, inst));
|
||||
to_replace.push_back(MakeInst(env, block, inst, host_info));
|
||||
}
|
||||
}
|
||||
// Sort instructions to visit textures by constant buffer index, then by offset
|
||||
@@ -689,8 +684,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
|
||||
program.info.texture_descriptors,
|
||||
program.info.image_descriptors,
|
||||
};
|
||||
const u32 sampled_dynamic_cap{
|
||||
DynamicSampledTextureCap(program.info, host_info, DynamicSampledTextureArrayCount(to_replace))};
|
||||
const u32 sampled_dynamic_cap = DynamicSampledTextureCap(program.info, host_info, DynamicSampledTextureArrayCount(to_replace));
|
||||
for (TextureInst& texture_inst : to_replace) {
|
||||
// TODO: Handle arrays
|
||||
IR::Inst* const inst{texture_inst.inst};
|
||||
|
||||
@@ -92,7 +92,6 @@ struct Profile {
|
||||
bool has_broken_robust{};
|
||||
|
||||
u64 min_ssbo_alignment{};
|
||||
|
||||
u32 max_user_clip_distances{};
|
||||
};
|
||||
|
||||
|
||||
@@ -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,12 +18,19 @@ static constexpr auto PERMS = Common::MemoryPermission::ReadWrite;
|
||||
static constexpr auto HEAP = false;
|
||||
|
||||
TEST_CASE("HostMemory: Initialize and deinitialize", "[common]") {
|
||||
{ HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); }
|
||||
{ HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); }
|
||||
{
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
REQUIRE(mem.BackingBasePointer() != nullptr);
|
||||
}
|
||||
{
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
REQUIRE(mem.BackingBasePointer() != nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("HostMemory: Simple map", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
REQUIRE(mem.BackingBasePointer() != nullptr);
|
||||
mem.Map(0x5000, 0x8000, 0x1000, PERMS, HEAP);
|
||||
|
||||
volatile u8* const data = mem.VirtualBasePointer() + 0x5000;
|
||||
@@ -30,6 +40,7 @@ TEST_CASE("HostMemory: Simple map", "[common]") {
|
||||
|
||||
TEST_CASE("HostMemory: Simple mirror map", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
REQUIRE(mem.BackingBasePointer() != nullptr);
|
||||
mem.Map(0x5000, 0x3000, 0x2000, PERMS, HEAP);
|
||||
mem.Map(0x8000, 0x4000, 0x1000, PERMS, HEAP);
|
||||
|
||||
@@ -41,6 +52,7 @@ TEST_CASE("HostMemory: Simple mirror map", "[common]") {
|
||||
|
||||
TEST_CASE("HostMemory: Simple unmap", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
REQUIRE(mem.BackingBasePointer() != nullptr);
|
||||
mem.Map(0x5000, 0x3000, 0x2000, PERMS, HEAP);
|
||||
|
||||
volatile u8* const data = mem.VirtualBasePointer() + 0x5000;
|
||||
@@ -52,6 +64,7 @@ TEST_CASE("HostMemory: Simple unmap", "[common]") {
|
||||
|
||||
TEST_CASE("HostMemory: Simple unmap and remap", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
REQUIRE(mem.BackingBasePointer() != nullptr);
|
||||
mem.Map(0x5000, 0x3000, 0x2000, PERMS, HEAP);
|
||||
|
||||
volatile u8* const data = mem.VirtualBasePointer() + 0x5000;
|
||||
@@ -69,6 +82,7 @@ TEST_CASE("HostMemory: Simple unmap and remap", "[common]") {
|
||||
|
||||
TEST_CASE("HostMemory: Nieche allocation", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
REQUIRE(mem.BackingBasePointer() != nullptr);
|
||||
mem.Map(0x0000, 0, 0x20000, PERMS, HEAP);
|
||||
mem.Unmap(0x0000, 0x4000, HEAP);
|
||||
mem.Map(0x1000, 0, 0x2000, PERMS, HEAP);
|
||||
@@ -78,6 +92,7 @@ TEST_CASE("HostMemory: Nieche allocation", "[common]") {
|
||||
|
||||
TEST_CASE("HostMemory: Full unmap", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
REQUIRE(mem.BackingBasePointer() != nullptr);
|
||||
mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
|
||||
mem.Unmap(0x8000, 0x4000, HEAP);
|
||||
mem.Map(0x6000, 0, 0x16000, PERMS, HEAP);
|
||||
@@ -85,6 +100,7 @@ TEST_CASE("HostMemory: Full unmap", "[common]") {
|
||||
|
||||
TEST_CASE("HostMemory: Right out of bounds unmap", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
REQUIRE(mem.BackingBasePointer() != nullptr);
|
||||
mem.Map(0x0000, 0, 0x4000, PERMS, HEAP);
|
||||
mem.Unmap(0x2000, 0x4000, HEAP);
|
||||
mem.Map(0x2000, 0x80000, 0x4000, PERMS, HEAP);
|
||||
@@ -92,6 +108,7 @@ TEST_CASE("HostMemory: Right out of bounds unmap", "[common]") {
|
||||
|
||||
TEST_CASE("HostMemory: Left out of bounds unmap", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
|
||||
mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
|
||||
mem.Unmap(0x6000, 0x4000, HEAP);
|
||||
mem.Map(0x8000, 0, 0x2000, PERMS, HEAP);
|
||||
@@ -99,6 +116,7 @@ TEST_CASE("HostMemory: Left out of bounds unmap", "[common]") {
|
||||
|
||||
TEST_CASE("HostMemory: Multiple placeholder unmap", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
REQUIRE(mem.BackingBasePointer() != nullptr);
|
||||
mem.Map(0x0000, 0, 0x4000, PERMS, HEAP);
|
||||
mem.Map(0x4000, 0, 0x1b000, PERMS, HEAP);
|
||||
mem.Unmap(0x3000, 0x1c000, HEAP);
|
||||
@@ -107,6 +125,7 @@ TEST_CASE("HostMemory: Multiple placeholder unmap", "[common]") {
|
||||
|
||||
TEST_CASE("HostMemory: Unmap between placeholders", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
REQUIRE(mem.BackingBasePointer() != nullptr);
|
||||
mem.Map(0x0000, 0, 0x4000, PERMS, HEAP);
|
||||
mem.Map(0x4000, 0, 0x4000, PERMS, HEAP);
|
||||
mem.Unmap(0x2000, 0x4000, HEAP);
|
||||
@@ -115,6 +134,7 @@ TEST_CASE("HostMemory: Unmap between placeholders", "[common]") {
|
||||
|
||||
TEST_CASE("HostMemory: Unmap to origin", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
REQUIRE(mem.BackingBasePointer() != nullptr);
|
||||
mem.Map(0x4000, 0, 0x4000, PERMS, HEAP);
|
||||
mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
|
||||
mem.Unmap(0x4000, 0x4000, HEAP);
|
||||
@@ -124,6 +144,7 @@ TEST_CASE("HostMemory: Unmap to origin", "[common]") {
|
||||
|
||||
TEST_CASE("HostMemory: Unmap to right", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
REQUIRE(mem.BackingBasePointer() != nullptr);
|
||||
mem.Map(0x4000, 0, 0x4000, PERMS, HEAP);
|
||||
mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
|
||||
mem.Unmap(0x8000, 0x4000, HEAP);
|
||||
@@ -132,6 +153,7 @@ TEST_CASE("HostMemory: Unmap to right", "[common]") {
|
||||
|
||||
TEST_CASE("HostMemory: Partial right unmap check bindings", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
REQUIRE(mem.BackingBasePointer() != nullptr);
|
||||
mem.Map(0x4000, 0x10000, 0x4000, PERMS, HEAP);
|
||||
|
||||
volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000;
|
||||
@@ -144,6 +166,7 @@ TEST_CASE("HostMemory: Partial right unmap check bindings", "[common]") {
|
||||
|
||||
TEST_CASE("HostMemory: Partial left unmap check bindings", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
REQUIRE(mem.BackingBasePointer() != nullptr);
|
||||
mem.Map(0x4000, 0x10000, 0x4000, PERMS, HEAP);
|
||||
|
||||
volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000;
|
||||
@@ -158,6 +181,7 @@ TEST_CASE("HostMemory: Partial left unmap check bindings", "[common]") {
|
||||
|
||||
TEST_CASE("HostMemory: Partial middle unmap check bindings", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
REQUIRE(mem.BackingBasePointer() != nullptr);
|
||||
mem.Map(0x4000, 0x10000, 0x4000, PERMS, HEAP);
|
||||
|
||||
volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000;
|
||||
@@ -172,6 +196,7 @@ TEST_CASE("HostMemory: Partial middle unmap check bindings", "[common]") {
|
||||
|
||||
TEST_CASE("HostMemory: Partial sparse middle unmap and check bindings", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
REQUIRE(mem.BackingBasePointer() != nullptr);
|
||||
mem.Map(0x4000, 0x10000, 0x2000, PERMS, HEAP);
|
||||
mem.Map(0x6000, 0x20000, 0x2000, PERMS, HEAP);
|
||||
|
||||
|
||||
@@ -20,58 +20,53 @@ Decoder::Decoder(Host1x::Host1x& host1x_, s32 id_, const Host1x::NvdecCommon::Nv
|
||||
|
||||
Decoder::~Decoder() = default;
|
||||
|
||||
void Decoder::SetFrameDimensions(s32 width, s32 height) {
|
||||
if (width <= 0 || height <= 0) {
|
||||
frame_dimensions.reset();
|
||||
return;
|
||||
}
|
||||
frame_dimensions = FFmpeg::FrameDimensions{width, height};
|
||||
}
|
||||
|
||||
void Decoder::Decode() {
|
||||
if (!initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto packet_data = ComposeFrame();
|
||||
// Send assembled bitstream to decoder.
|
||||
if (!decode_api.SendPacket(packet_data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only receive/store visible frames.
|
||||
if (vp9_hidden_frame) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Receive output frames from decoder.
|
||||
auto frame = decode_api.ReceiveFrame();
|
||||
|
||||
if (!frame) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsInterlaced()) {
|
||||
auto [luma_top, luma_bottom, chroma_top, chroma_bottom] = GetInterlacedOffsets();
|
||||
auto frame_copy = frame;
|
||||
|
||||
if (!frame.get()) {
|
||||
LOG_ERROR(HW_GPU,
|
||||
"Nvdec {} failed to decode interlaced frame for top {:#X} bottom 0x{:X}", id,
|
||||
luma_top, luma_bottom);
|
||||
}
|
||||
|
||||
if (UsingDecodeOrder()) {
|
||||
host1x.frame_queue.PushDecodeOrder(id, luma_top, std::move(frame));
|
||||
host1x.frame_queue.PushDecodeOrder(id, luma_bottom, std::move(frame_copy));
|
||||
} else {
|
||||
host1x.frame_queue.PushPresentOrder(id, luma_top, std::move(frame));
|
||||
host1x.frame_queue.PushPresentOrder(id, luma_bottom, std::move(frame_copy));
|
||||
}
|
||||
FFmpeg::FrameOffsets offsets{};
|
||||
offsets.hidden = vp9_hidden_frame;
|
||||
offsets.interlaced = IsInterlaced();
|
||||
if (offsets.interlaced) {
|
||||
std::tie(offsets.luma, offsets.luma_bottom, std::ignore, std::ignore) =
|
||||
GetInterlacedOffsets();
|
||||
} else {
|
||||
auto [luma_offset, chroma_offset] = GetProgressiveOffsets();
|
||||
std::tie(offsets.luma, std::ignore) = GetProgressiveOffsets();
|
||||
}
|
||||
|
||||
if (!frame.get()) {
|
||||
LOG_ERROR(HW_GPU, "Nvdec {} failed to decode progressive frame for luma {:#X}", id,
|
||||
luma_offset);
|
||||
}
|
||||
if (!decode_api.SendPacket(packet_data, offsets, GetFrameDimensions())) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto push = [&](u64 luma, std::shared_ptr<FFmpeg::Frame> frame) {
|
||||
if (UsingDecodeOrder()) {
|
||||
host1x.frame_queue.PushDecodeOrder(id, luma_offset, std::move(frame));
|
||||
host1x.frame_queue.PushDecodeOrder(id, luma, std::move(frame));
|
||||
} else {
|
||||
host1x.frame_queue.PushPresentOrder(id, luma_offset, std::move(frame));
|
||||
host1x.frame_queue.PushPresentOrder(id, luma, std::move(frame));
|
||||
}
|
||||
};
|
||||
|
||||
while (auto result = decode_api.ReceiveFrame()) {
|
||||
auto& [frame, o] = *result;
|
||||
if (o.hidden || !frame) {
|
||||
continue;
|
||||
}
|
||||
if (o.interlaced) {
|
||||
auto frame_copy = frame;
|
||||
push(o.luma, std::move(frame));
|
||||
push(o.luma_bottom, std::move(frame_copy));
|
||||
} else {
|
||||
push(o.luma, std::move(frame));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <queue>
|
||||
|
||||
@@ -46,12 +47,19 @@ protected:
|
||||
virtual std::tuple<u64, u64, u64, u64> GetInterlacedOffsets() = 0;
|
||||
virtual bool IsInterlaced() = 0;
|
||||
|
||||
void SetFrameDimensions(s32 width, s32 height);
|
||||
|
||||
std::optional<FFmpeg::FrameDimensions> GetFrameDimensions() const {
|
||||
return frame_dimensions;
|
||||
}
|
||||
|
||||
FFmpeg::DecodeApi decode_api;
|
||||
Host1x::Host1x& host1x;
|
||||
const Host1x::NvdecCommon::NvdecRegisters& regs;
|
||||
s32 id;
|
||||
bool initialized : 1 = false;
|
||||
bool vp9_hidden_frame : 1 = false;
|
||||
std::optional<FFmpeg::FrameDimensions> frame_dimensions;
|
||||
};
|
||||
|
||||
} // namespace Tegra
|
||||
|
||||
@@ -52,6 +52,10 @@ bool H264::IsInterlaced() {
|
||||
|
||||
std::span<const u8> H264::ComposeFrame() {
|
||||
host1x.gmmu_manager.ReadBlock(regs.picture_info_offset.Address(), ¤t_context, sizeof(H264DecoderContext));
|
||||
const auto& params = current_context.h264_parameter_set;
|
||||
SetFrameDimensions(static_cast<s32>(params.pic_width_in_mbs) * 16,
|
||||
static_cast<s32>(params.frame_height_in_mbs) * 16);
|
||||
|
||||
const s64 frame_number = current_context.h264_parameter_set.frame_number.Value();
|
||||
if (!is_first_frame && frame_number != 0) {
|
||||
frame_scratch.resize_destructive(current_context.stream_len);
|
||||
|
||||
@@ -35,6 +35,7 @@ std::tuple<u64, u64, u64, u64> VP8::GetInterlacedOffsets() {
|
||||
|
||||
std::span<const u8> VP8::ComposeFrame() {
|
||||
host1x.gmmu_manager.ReadBlock(regs.picture_info_offset.Address(), ¤t_context, sizeof(VP8PictureInfo));
|
||||
SetFrameDimensions(current_context.frame_width, current_context.frame_height);
|
||||
|
||||
const bool is_key_frame = current_context.key_frame == 1u;
|
||||
const auto bitstream_size = size_t(current_context.vld_buffer_size);
|
||||
|
||||
@@ -841,6 +841,7 @@ std::span<const u8> VP9::ComposeFrame() {
|
||||
{
|
||||
Vp9FrameContainer curr_frame = GetCurrentFrame();
|
||||
current_frame_info = curr_frame.info;
|
||||
SetFrameDimensions(current_frame_info.frame_size.width, current_frame_info.frame_size.height);
|
||||
bitstream = std::move(curr_frame.bit_stream);
|
||||
}
|
||||
// The uncompressed header routine sets PrevProb parameters needed for the compressed header
|
||||
|
||||
@@ -4,6 +4,11 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging.h"
|
||||
#include "common/scope_exit.h"
|
||||
@@ -19,15 +24,16 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
#include <libavutil/hwcontext.h>
|
||||
#include <libavutil/log.h>
|
||||
}
|
||||
|
||||
namespace FFmpeg {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr AVPixelFormat PreferredGpuFormat = AV_PIX_FMT_NV12;
|
||||
constexpr AVPixelFormat PreferredCpuFormat = AV_PIX_FMT_YUV420P;
|
||||
constexpr std::array PreferredGpuDecoders = {
|
||||
constexpr AVPixelFormat PREFERRED_GPU_FORMAT = AV_PIX_FMT_NV12;
|
||||
constexpr AVPixelFormat PREFERRED_CPU_FORMAT = AV_PIX_FMT_YUV420P;
|
||||
constexpr std::array PREFERRED_GPU_DECODERS = {
|
||||
#if defined(_WIN32)
|
||||
AV_HWDEVICE_TYPE_CUDA,
|
||||
AV_HWDEVICE_TYPE_D3D11VA,
|
||||
@@ -54,15 +60,14 @@ AVPixelFormat GetGpuFormat(AVCodecContext* codec_context, const AVPixelFormat* p
|
||||
if (desc && !(desc->flags & AV_PIX_FMT_FLAG_HWACCEL)) {
|
||||
for (int i = 0;; i++) {
|
||||
const AVCodecHWConfig* config = avcodec_get_hw_config(codec_context->codec, i);
|
||||
if (!config) {
|
||||
if (config) {
|
||||
for (const auto type : PREFERRED_GPU_DECODERS)
|
||||
if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX && config->device_type == type) {
|
||||
codec_context->pix_fmt = config->pix_fmt;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
for (const auto type : PreferredGpuDecoders) {
|
||||
if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX && config->device_type == type) {
|
||||
codec_context->pix_fmt = config->pix_fmt;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +79,7 @@ AVPixelFormat GetGpuFormat(AVCodecContext* codec_context, const AVPixelFormat* p
|
||||
|
||||
LOG_INFO(HW_GPU, "Could not find supported GPU pixel format, falling back to CPU decoder");
|
||||
av_buffer_unref(&codec_context->hw_device_ctx);
|
||||
codec_context->pix_fmt = PreferredCpuFormat;
|
||||
codec_context->pix_fmt = PREFERRED_CPU_FORMAT;
|
||||
return codec_context->pix_fmt;
|
||||
}
|
||||
|
||||
@@ -84,6 +89,57 @@ std::string AVError(int errnum) {
|
||||
return errbuf;
|
||||
}
|
||||
|
||||
#ifdef __ANDROID__
|
||||
// Match a 3- or 4-byte annex-B NAL start code at `i`. Returns its length, or 0.
|
||||
size_t FindNalStartCode(std::span<const u8> data, size_t i) {
|
||||
const size_t n = data.size();
|
||||
if (i + 3 < n && data[i] == 0 && data[i + 1] == 0 && data[i + 2] == 0 && data[i + 3] == 1) {
|
||||
return 4;
|
||||
}
|
||||
if (i + 2 < n && data[i] == 0 && data[i + 1] == 0 && data[i + 2] == 1) {
|
||||
return 3;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Pull SPS (NAL type 7) + PPS (NAL type 8) out of an annex-B frame into an
|
||||
// extradata buffer, each prefixed with a 4-byte start code. Eden synthesizes
|
||||
// these inline into the very first frame; h264_mediacodec wants them at open.
|
||||
std::vector<u8> ExtractH264ParameterSetExtradata(std::span<const u8> packet) {
|
||||
std::vector<u8> extradata;
|
||||
const size_t size = packet.size();
|
||||
size_t i = 0;
|
||||
while (i < size) {
|
||||
const size_t sc = FindNalStartCode(packet, i);
|
||||
if (sc == 0) {
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
const size_t nal_start = i + sc;
|
||||
if (nal_start >= size) {
|
||||
break;
|
||||
}
|
||||
const u8 nal_type = packet[nal_start] & 0x1F;
|
||||
|
||||
size_t j = nal_start + 1;
|
||||
while (j < size && FindNalStartCode(packet, j) == 0) {
|
||||
++j;
|
||||
}
|
||||
|
||||
if (nal_type == 7 || nal_type == 8) {
|
||||
constexpr u8 start[4] = {0, 0, 0, 1};
|
||||
extradata.insert(extradata.end(), start, start + sizeof(start));
|
||||
extradata.insert(extradata.end(), packet.begin() + nal_start, packet.begin() + j);
|
||||
} else if (nal_type == 1 || nal_type == 5) {
|
||||
break;
|
||||
}
|
||||
i = j;
|
||||
}
|
||||
return extradata;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
Packet::Packet(std::span<const u8> data) {
|
||||
@@ -118,7 +174,26 @@ Decoder::Decoder(Tegra::Host1x::NvdecCommon::VideoCodec codec) {
|
||||
return AV_CODEC_ID_NONE;
|
||||
}
|
||||
}();
|
||||
m_codec = avcodec_find_decoder(av_codec);
|
||||
|
||||
#ifdef __ANDROID__
|
||||
// FFmpeg exposes MediaCodec via dedicated decoders rather than as a
|
||||
// hw_config on the regular ones.
|
||||
if (Settings::values.nvdec_emulation.GetValue() == Settings::NvdecEmulation::Gpu) {
|
||||
const char* mc_name = nullptr;
|
||||
switch (av_codec) {
|
||||
case AV_CODEC_ID_H264: mc_name = "h264_mediacodec"; break;
|
||||
case AV_CODEC_ID_VP8: mc_name = "vp8_mediacodec"; break;
|
||||
case AV_CODEC_ID_VP9: mc_name = "vp9_mediacodec"; break;
|
||||
default: break;
|
||||
}
|
||||
if (mc_name) {
|
||||
m_codec = avcodec_find_decoder_by_name(mc_name);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (!m_codec) {
|
||||
m_codec = avcodec_find_decoder(av_codec);
|
||||
}
|
||||
}
|
||||
|
||||
bool Decoder::SupportsDecodingOnDevice(AVPixelFormat* out_pix_fmt, AVHWDeviceType type) const {
|
||||
@@ -142,13 +217,10 @@ bool Decoder::SupportsDecodingOnDevice(AVPixelFormat* out_pix_fmt, AVHWDeviceTyp
|
||||
std::vector<AVHWDeviceType> HardwareContext::GetSupportedDeviceTypes() {
|
||||
std::vector<AVHWDeviceType> types;
|
||||
AVHWDeviceType current_device_type = AV_HWDEVICE_TYPE_NONE;
|
||||
|
||||
while (true) {
|
||||
current_device_type = av_hwdevice_iterate_types(current_device_type);
|
||||
if (current_device_type == AV_HWDEVICE_TYPE_NONE) {
|
||||
if (current_device_type == AV_HWDEVICE_TYPE_NONE)
|
||||
return types;
|
||||
}
|
||||
|
||||
types.push_back(current_device_type);
|
||||
}
|
||||
}
|
||||
@@ -158,25 +230,20 @@ HardwareContext::~HardwareContext() {
|
||||
}
|
||||
|
||||
bool HardwareContext::InitializeForDecoder(DecoderContext& decoder_context, const Decoder& decoder) {
|
||||
const auto supported_types = GetSupportedDeviceTypes();
|
||||
for (const auto type : PreferredGpuDecoders) {
|
||||
AVPixelFormat hw_pix_fmt;
|
||||
|
||||
auto const supported_types = GetSupportedDeviceTypes();
|
||||
for (auto const type : PREFERRED_GPU_DECODERS) {
|
||||
if (std::ranges::find(supported_types, type) == supported_types.end()) {
|
||||
LOG_DEBUG(HW_GPU, "{} explicitly unsupported", av_hwdevice_get_type_name(type));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!this->InitializeWithType(type)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (decoder.SupportsDecodingOnDevice(&hw_pix_fmt, type)) {
|
||||
decoder_context.InitializeHardwareDecoder(*this, hw_pix_fmt);
|
||||
return true;
|
||||
if (InitializeWithType(type)) {
|
||||
AVPixelFormat hw_pix_fmt{};
|
||||
if (decoder.SupportsDecodingOnDevice(&hw_pix_fmt, type)) {
|
||||
decoder_context.InitializeHardwareDecoder(*this, hw_pix_fmt);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -184,7 +251,7 @@ bool HardwareContext::InitializeWithType(AVHWDeviceType type) {
|
||||
av_buffer_unref(&m_gpu_decoder);
|
||||
|
||||
if (const int ret = av_hwdevice_ctx_create(&m_gpu_decoder, type, nullptr, nullptr, 0); ret < 0) {
|
||||
LOG_DEBUG(HW_GPU, "av_hwdevice_ctx_create({}) failed: {}", av_hwdevice_get_type_name(type), AVError(ret));
|
||||
LOG_INFO(HW_GPU, "av_hwdevice_ctx_create({}) failed: {}", av_hwdevice_get_type_name(type), AVError(ret));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -214,6 +281,9 @@ DecoderContext::DecoderContext(const Decoder& decoder) : m_decoder{decoder} {
|
||||
av_opt_set(m_codec_context->priv_data, "tune", "zerolatency", 0);
|
||||
m_codec_context->thread_count = 0;
|
||||
m_codec_context->thread_type &= ~FF_THREAD_FRAME;
|
||||
// Forwarded into MediaCodec as KEY_LOW_LATENCY on Android.
|
||||
m_codec_context->flags |= AV_CODEC_FLAG_LOW_DELAY;
|
||||
m_codec_context->flags2 |= AV_CODEC_FLAG2_FAST;
|
||||
}
|
||||
|
||||
DecoderContext::~DecoderContext() {
|
||||
@@ -227,7 +297,19 @@ void DecoderContext::InitializeHardwareDecoder(const HardwareContext& context, A
|
||||
m_codec_context->pix_fmt = hw_pix_fmt;
|
||||
}
|
||||
|
||||
bool DecoderContext::OpenContext(const Decoder& decoder) {
|
||||
bool DecoderContext::OpenContext(const Decoder& decoder, std::span<const u8> extradata) {
|
||||
if (!extradata.empty()) {
|
||||
av_freep(&m_codec_context->extradata);
|
||||
m_codec_context->extradata = static_cast<u8*>(
|
||||
av_mallocz(extradata.size() + AV_INPUT_BUFFER_PADDING_SIZE));
|
||||
if (!m_codec_context->extradata) {
|
||||
LOG_ERROR(HW_GPU, "Failed to allocate extradata");
|
||||
return false;
|
||||
}
|
||||
std::memcpy(m_codec_context->extradata, extradata.data(), extradata.size());
|
||||
m_codec_context->extradata_size = static_cast<int>(extradata.size());
|
||||
}
|
||||
|
||||
if (const int ret = avcodec_open2(m_codec_context, decoder.GetCodec(), nullptr); ret < 0) {
|
||||
LOG_ERROR(HW_GPU, "avcodec_open2 error: {}", AVError(ret));
|
||||
return false;
|
||||
@@ -265,11 +347,17 @@ std::shared_ptr<Frame> DecoderContext::ReceiveFrame() {
|
||||
|
||||
m_final_frame = std::make_shared<Frame>();
|
||||
if (m_codec_context->hw_device_ctx) {
|
||||
m_final_frame->SetFormat(PreferredGpuFormat);
|
||||
#ifdef __ANDROID__
|
||||
// c2.mtk.vp9.decoder, c2.mtk.vp89.decoder will be fine if we don't
|
||||
// re-encode stuff twice :>
|
||||
m_final_frame = std::move(intermediate_frame);
|
||||
#else
|
||||
m_final_frame->SetFormat(PREFERRED_GPU_FORMAT);
|
||||
if (const int ret = av_hwframe_transfer_data(m_final_frame->GetFrame(), intermediate_frame->GetFrame(), 0); ret < 0) {
|
||||
LOG_ERROR(HW_GPU, "av_hwframe_transfer_data error: {}", AVError(ret));
|
||||
return {};
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
m_final_frame = std::move(intermediate_frame);
|
||||
}
|
||||
@@ -281,9 +369,18 @@ void DecodeApi::Reset() {
|
||||
m_hardware_context.reset();
|
||||
m_decoder_context.reset();
|
||||
m_decoder.reset();
|
||||
m_opened = false;
|
||||
m_defer_android_mediacodec_open = false;
|
||||
m_needs_h264_extradata = false;
|
||||
m_next_pts = 0;
|
||||
while (!m_pending_offsets.empty()) {
|
||||
m_pending_offsets.pop();
|
||||
}
|
||||
}
|
||||
|
||||
bool DecodeApi::Initialize(Tegra::Host1x::NvdecCommon::VideoCodec codec) {
|
||||
av_log_set_level(AV_LOG_DEBUG);
|
||||
|
||||
this->Reset();
|
||||
m_decoder.emplace(codec);
|
||||
m_decoder_context.emplace(*m_decoder);
|
||||
@@ -294,23 +391,80 @@ bool DecodeApi::Initialize(Tegra::Host1x::NvdecCommon::VideoCodec codec) {
|
||||
m_hardware_context->InitializeForDecoder(*m_decoder_context, *m_decoder);
|
||||
}
|
||||
|
||||
// Open the decoder context.
|
||||
#ifdef __ANDROID__
|
||||
const std::string_view decoder_name = m_decoder->GetCodec() ? m_decoder->GetCodec()->name : "";
|
||||
|
||||
// MediaCodec decoders need the frame dimensions before avcodec_open2().
|
||||
m_defer_android_mediacodec_open = decoder_name == "h264_mediacodec" ||
|
||||
decoder_name == "vp8_mediacodec" ||
|
||||
decoder_name == "vp9_mediacodec";
|
||||
|
||||
// h264_mediacodec also needs SPS/PPS in extradata at open. We pull them
|
||||
// from the first frame's bitstream in SendPacket.
|
||||
m_needs_h264_extradata = decoder_name == "h264_mediacodec";
|
||||
if (m_defer_android_mediacodec_open) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!m_decoder_context->OpenContext(*m_decoder)) {
|
||||
this->Reset();
|
||||
return false;
|
||||
}
|
||||
m_opened = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DecodeApi::SendPacket(std::span<const u8> packet_data) {
|
||||
bool DecodeApi::SendPacket(std::span<const u8> packet_data, const FrameOffsets& offsets,
|
||||
std::optional<FrameDimensions> dimensions) {
|
||||
if (!m_opened) {
|
||||
std::vector<u8> extradata;
|
||||
#ifdef __ANDROID__
|
||||
if (m_defer_android_mediacodec_open) {
|
||||
if (!dimensions) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto* ctx = m_decoder_context->GetCodecContext();
|
||||
ctx->width = dimensions->width;
|
||||
ctx->height = dimensions->height;
|
||||
ctx->coded_width = dimensions->width;
|
||||
ctx->coded_height = dimensions->height;
|
||||
}
|
||||
|
||||
if (m_needs_h264_extradata) {
|
||||
extradata = ExtractH264ParameterSetExtradata(packet_data);
|
||||
if (extradata.empty()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (!m_decoder_context->OpenContext(*m_decoder, extradata)) {
|
||||
this->Reset();
|
||||
return false;
|
||||
}
|
||||
m_opened = true;
|
||||
}
|
||||
m_pending_offsets.push(offsets);
|
||||
FFmpeg::Packet packet(packet_data);
|
||||
packet.GetPacket()->pts = m_next_pts;
|
||||
packet.GetPacket()->dts = m_next_pts;
|
||||
++m_next_pts;
|
||||
return m_decoder_context->SendPacket(packet);
|
||||
}
|
||||
|
||||
std::shared_ptr<Frame> DecodeApi::ReceiveFrame() {
|
||||
// Receive raw frame from decoder.
|
||||
return m_decoder_context->ReceiveFrame();
|
||||
std::optional<DecodeApi::DecodedFrame> DecodeApi::ReceiveFrame() {
|
||||
auto frame = m_decoder_context->ReceiveFrame();
|
||||
if (!frame) {
|
||||
return std::nullopt;
|
||||
}
|
||||
FrameOffsets offsets{};
|
||||
if (!m_pending_offsets.empty()) {
|
||||
offsets = m_pending_offsets.front();
|
||||
m_pending_offsets.pop();
|
||||
}
|
||||
return DecodedFrame{std::move(frame), offsets};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -179,7 +179,7 @@ public:
|
||||
~DecoderContext();
|
||||
|
||||
void InitializeHardwareDecoder(const HardwareContext& context, AVPixelFormat hw_pix_fmt);
|
||||
bool OpenContext(const Decoder& decoder);
|
||||
bool OpenContext(const Decoder& decoder, std::span<const u8> extradata = {});
|
||||
bool SendPacket(const Packet& packet);
|
||||
std::shared_ptr<Frame> ReceiveFrame();
|
||||
|
||||
@@ -198,6 +198,18 @@ private:
|
||||
bool m_decode_order{};
|
||||
};
|
||||
|
||||
struct FrameOffsets {
|
||||
bool interlaced{};
|
||||
bool hidden{};
|
||||
u64 luma{};
|
||||
u64 luma_bottom{};
|
||||
};
|
||||
|
||||
struct FrameDimensions {
|
||||
s32 width{};
|
||||
s32 height{};
|
||||
};
|
||||
|
||||
class DecodeApi {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(DecodeApi);
|
||||
@@ -213,13 +225,24 @@ public:
|
||||
return m_decoder_context->UsingDecodeOrder();
|
||||
}
|
||||
|
||||
bool SendPacket(std::span<const u8> packet_data);
|
||||
std::shared_ptr<Frame> ReceiveFrame();
|
||||
bool SendPacket(std::span<const u8> packet_data, const FrameOffsets& offsets,
|
||||
std::optional<FrameDimensions> dimensions = std::nullopt);
|
||||
|
||||
struct DecodedFrame {
|
||||
std::shared_ptr<Frame> frame;
|
||||
FrameOffsets offsets;
|
||||
};
|
||||
std::optional<DecodedFrame> ReceiveFrame();
|
||||
|
||||
private:
|
||||
std::optional<FFmpeg::Decoder> m_decoder;
|
||||
std::optional<FFmpeg::DecoderContext> m_decoder_context;
|
||||
std::optional<FFmpeg::HardwareContext> m_hardware_context;
|
||||
bool m_opened{};
|
||||
bool m_defer_android_mediacodec_open{};
|
||||
bool m_needs_h264_extradata{};
|
||||
s64 m_next_pts{};
|
||||
std::queue<FrameOffsets> m_pending_offsets;
|
||||
};
|
||||
|
||||
} // namespace FFmpeg
|
||||
|
||||
@@ -31,6 +31,7 @@ Nvdec::Nvdec(Host1x& host1x_, s32 id_, u32 syncpt)
|
||||
|
||||
Nvdec::~Nvdec() {
|
||||
LOG_INFO(HW_GPU, "Destroying nvdec {}", id);
|
||||
host1x.frame_queue.Close(id);
|
||||
}
|
||||
|
||||
void Nvdec::ProcessMethod(u32 method, u32 argument) {
|
||||
|
||||
@@ -118,6 +118,7 @@ void Vic::Execute() noexcept {
|
||||
output_surface.resize(output_width * output_height);
|
||||
|
||||
if (Settings::values.nvdec_emulation.GetValue() != Settings::NvdecEmulation::Off) {
|
||||
bool decoded_frame = false;
|
||||
for (size_t i = 0; i < config.slot_structs.size(); i++) {
|
||||
if (auto& slot_config = config.slot_structs[i]; slot_config.config.slot_enable) {
|
||||
auto const luma_offset = regs.surfaces[i][SurfaceIndex::Current].luma.Address();
|
||||
@@ -136,11 +137,17 @@ void Vic::Execute() noexcept {
|
||||
break;
|
||||
}
|
||||
Blend(config, slot_config, config.output_surface_config.out_pixel_format);
|
||||
decoded_frame = true;
|
||||
} else {
|
||||
LOG_ERROR(HW_GPU, "Vic {} failed to get frame with offset {:#X}", id, luma_offset);
|
||||
LOG_TRACE(HW_GPU, "Vic {} failed to get frame with offset {:#X}", id, luma_offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (decoded_frame) {
|
||||
has_decoded_frame = true;
|
||||
} else if (!has_decoded_frame) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// Fill the frame with black, as otherwise they can have random data and be very glitchy.
|
||||
std::fill(output_surface.begin(), output_surface.end(), Pixel{});
|
||||
|
||||
@@ -628,6 +628,7 @@ private:
|
||||
|
||||
s32 id;
|
||||
s32 nvdec_id{-1};
|
||||
bool has_decoded_frame{};
|
||||
u32 syncpoint;
|
||||
};
|
||||
|
||||
|
||||
@@ -11,9 +11,8 @@
|
||||
#define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants {
|
||||
#define END_PUSH_CONSTANTS };
|
||||
#define UNIFORM(n)
|
||||
#define BINDING_SWIZZLE_BUFFER 0
|
||||
#define BINDING_INPUT_BUFFER 1
|
||||
#define BINDING_OUTPUT_IMAGE 2
|
||||
#define BINDING_INPUT_BUFFER 0
|
||||
#define BINDING_OUTPUT_IMAGE 1
|
||||
|
||||
#else // ^^^ Vulkan ^^^ // vvv OpenGL vvv
|
||||
|
||||
@@ -26,8 +25,7 @@
|
||||
#define BEGIN_PUSH_CONSTANTS
|
||||
#define END_PUSH_CONSTANTS
|
||||
#define UNIFORM(n) layout (location = n) uniform
|
||||
#define BINDING_SWIZZLE_BUFFER 0
|
||||
#define BINDING_INPUT_BUFFER 1
|
||||
#define BINDING_INPUT_BUFFER 0
|
||||
#define BINDING_OUTPUT_IMAGE 0
|
||||
|
||||
#endif
|
||||
@@ -43,10 +41,6 @@ UNIFORM(6) uint block_height;
|
||||
UNIFORM(7) uint block_height_mask;
|
||||
END_PUSH_CONSTANTS
|
||||
|
||||
layout(binding = BINDING_SWIZZLE_BUFFER, std430) readonly buffer SwizzleTable {
|
||||
uint swizzle_table[];
|
||||
};
|
||||
|
||||
#if HAS_EXTENDED_TYPES
|
||||
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU8 { uint8_t u8data[]; };
|
||||
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU16 { uint16_t u16data[]; };
|
||||
@@ -71,9 +65,19 @@ const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT + GOB_SIZE_Z_SHI
|
||||
|
||||
const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1, GOB_SIZE_Y - 1);
|
||||
|
||||
uint SwizzleTable(uint pos) {
|
||||
const uint t[8] = uint[](
|
||||
0x12100200, 0x13110301, 0x16140604, 0x17150705,
|
||||
0x1a180a08, 0x1b190b09, 0x1e1c0e0c, 0x1f1d0f0d
|
||||
);
|
||||
const uint i = pos >> 4;
|
||||
const uint h = (t[i / 4] >> ((i % 4) * 8)) & 0xff;
|
||||
return (h << 4) | (pos & 0xf);
|
||||
}
|
||||
|
||||
uint SwizzleOffset(uvec2 pos) {
|
||||
pos = pos & SWIZZLE_MASK;
|
||||
return swizzle_table[pos.y * 64 + pos.x];
|
||||
return SwizzleTable(pos.y * 64 + pos.x);
|
||||
}
|
||||
|
||||
uvec4 ReadTexel(uint offset) {
|
||||
|
||||
@@ -11,9 +11,8 @@
|
||||
#define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants {
|
||||
#define END_PUSH_CONSTANTS };
|
||||
#define UNIFORM(n)
|
||||
#define BINDING_SWIZZLE_BUFFER 0
|
||||
#define BINDING_INPUT_BUFFER 1
|
||||
#define BINDING_OUTPUT_IMAGE 2
|
||||
#define BINDING_INPUT_BUFFER 0
|
||||
#define BINDING_OUTPUT_IMAGE 1
|
||||
|
||||
#else // ^^^ Vulkan ^^^ // vvv OpenGL vvv
|
||||
|
||||
@@ -26,8 +25,7 @@
|
||||
#define BEGIN_PUSH_CONSTANTS
|
||||
#define END_PUSH_CONSTANTS
|
||||
#define UNIFORM(n) layout (location = n) uniform
|
||||
#define BINDING_SWIZZLE_BUFFER 0
|
||||
#define BINDING_INPUT_BUFFER 1
|
||||
#define BINDING_INPUT_BUFFER 0
|
||||
#define BINDING_OUTPUT_IMAGE 0
|
||||
|
||||
#endif
|
||||
@@ -45,10 +43,6 @@ UNIFORM(8) uint block_depth;
|
||||
UNIFORM(9) uint block_depth_mask;
|
||||
END_PUSH_CONSTANTS
|
||||
|
||||
layout(binding = BINDING_SWIZZLE_BUFFER, std430) readonly buffer SwizzleTable {
|
||||
uint swizzle_table[];
|
||||
};
|
||||
|
||||
#if HAS_EXTENDED_TYPES
|
||||
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU8 { uint8_t u8data[]; };
|
||||
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU16 { uint16_t u16data[]; };
|
||||
@@ -73,9 +67,19 @@ const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT + GOB_SIZE_Z_SHI
|
||||
|
||||
const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1, GOB_SIZE_Y - 1);
|
||||
|
||||
uint SwizzleTable(uint pos) {
|
||||
const uint t[8] = uint[](
|
||||
0x12100200, 0x13110301, 0x16140604, 0x17150705,
|
||||
0x1a180a08, 0x1b190b09, 0x1e1c0e0c, 0x1f1d0f0d
|
||||
);
|
||||
const uint i = pos >> 4;
|
||||
const uint h = (t[i / 4] >> ((i % 4) * 8)) & 0xff;
|
||||
return (h << 4) | (pos & 0xf);
|
||||
}
|
||||
|
||||
uint SwizzleOffset(uvec2 pos) {
|
||||
pos = pos & SWIZZLE_MASK;
|
||||
return swizzle_table[pos.y * 64 + pos.x];
|
||||
return SwizzleTable(pos.y * 64 + pos.x);
|
||||
}
|
||||
|
||||
uvec4 ReadTexel(uint offset) {
|
||||
|
||||
@@ -10,9 +10,8 @@
|
||||
#define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants {
|
||||
#define END_PUSH_CONSTANTS };
|
||||
#define UNIFORM(n)
|
||||
#define BINDING_SWIZZLE_BUFFER 0
|
||||
#define BINDING_INPUT_BUFFER 1
|
||||
#define BINDING_OUTPUT_BUFFER 2
|
||||
#define BINDING_INPUT_BUFFER 0
|
||||
#define BINDING_OUTPUT_BUFFER 1
|
||||
#else
|
||||
#extension GL_NV_gpu_shader5 : enable
|
||||
#ifdef GL_NV_gpu_shader5
|
||||
@@ -23,7 +22,6 @@
|
||||
#define BEGIN_PUSH_CONSTANTS
|
||||
#define END_PUSH_CONSTANTS
|
||||
#define UNIFORM(n) layout(location = n) uniform
|
||||
#define BINDING_SWIZZLE_BUFFER 0
|
||||
#define BINDING_INPUT_BUFFER 1
|
||||
#define BINDING_OUTPUT_BUFFER 0
|
||||
#endif
|
||||
@@ -66,13 +64,9 @@ END_PUSH_CONSTANTS
|
||||
#endif
|
||||
|
||||
// --- Buffers ---
|
||||
layout(binding = BINDING_SWIZZLE_BUFFER, std430) readonly buffer SwizzleTable {
|
||||
uint swizzle_table[];
|
||||
};
|
||||
|
||||
#if HAS_EXTENDED_TYPES
|
||||
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU8 { uint8_t u8data[]; };
|
||||
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU16 { uint16_t u16data[]; };
|
||||
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU8 { uint8_t u8data[]; };
|
||||
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU16 { uint16_t u16data[]; };
|
||||
#endif
|
||||
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU32 { uint u32data[]; };
|
||||
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU64 { uvec2 u64data[]; };
|
||||
@@ -96,10 +90,20 @@ const uint GOB_SIZE_Z_SHIFT = 0;
|
||||
const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT + GOB_SIZE_Z_SHIFT;
|
||||
const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1u, GOB_SIZE_Y - 1u);
|
||||
|
||||
uint SwizzleTable(uint pos) {
|
||||
const uint t[8] = uint[](
|
||||
0x12100200, 0x13110301, 0x16140604, 0x17150705,
|
||||
0x1a180a08, 0x1b190b09, 0x1e1c0e0c, 0x1f1d0f0d
|
||||
);
|
||||
const uint i = pos >> 4;
|
||||
const uint h = (t[i / 4] >> ((i % 4) * 8)) & 0xff;
|
||||
return (h << 4) | (pos & 0xf);
|
||||
}
|
||||
|
||||
// --- Helpers ---
|
||||
uint SwizzleOffset(uvec2 pos) {
|
||||
pos &= SWIZZLE_MASK;
|
||||
return swizzle_table[pos.y * 64u + pos.x];
|
||||
return SwizzleTable(pos.y * 64u + pos.x);
|
||||
}
|
||||
|
||||
uvec4 ReadTexel(uint offset) {
|
||||
|
||||
@@ -245,16 +245,31 @@ ShaderCache::ShaderCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
|
||||
std::min<u32>(device.GetMaxUserClipDistances(), Maxwell::Regs::NumClipDistances),
|
||||
},
|
||||
host_info{
|
||||
.support_float64 = true,
|
||||
.support_float16 = false,
|
||||
.support_int64 = device.HasShaderInt64(),
|
||||
.needs_demote_reorder = device.IsAmd(),
|
||||
.support_snorm_render_buffer = false,
|
||||
.support_viewport_index_layer = device.HasVertexViewportLayer(),
|
||||
.min_ssbo_alignment = static_cast<u32>(device.GetShaderStorageBufferAlignment()),
|
||||
.support_geometry_shader_passthrough = device.HasGeometryShaderPassthrough(),
|
||||
.support_conditional_barrier = device.SupportsConditionalBarriers(),
|
||||
.min_ssbo_alignment = static_cast<u32>(device.GetShaderStorageBufferAlignment()),
|
||||
.max_per_stage_descriptor_sampled_images =
|
||||
Shader::HostTranslateInfo::DEFAULT_DESCRIPTOR_LIMIT,
|
||||
.max_per_stage_resources = Shader::HostTranslateInfo::DEFAULT_DESCRIPTOR_LIMIT,
|
||||
.max_descriptor_set_samplers = Shader::HostTranslateInfo::DEFAULT_DESCRIPTOR_LIMIT,
|
||||
.max_descriptor_set_uniform_buffers = Shader::HostTranslateInfo::DEFAULT_DESCRIPTOR_LIMIT,
|
||||
.max_descriptor_set_uniform_buffers_dynamic =
|
||||
Shader::HostTranslateInfo::DEFAULT_DESCRIPTOR_LIMIT,
|
||||
.max_descriptor_set_storage_buffers = Shader::HostTranslateInfo::DEFAULT_DESCRIPTOR_LIMIT,
|
||||
.max_descriptor_set_storage_buffers_dynamic =
|
||||
Shader::HostTranslateInfo::DEFAULT_DESCRIPTOR_LIMIT,
|
||||
.max_descriptor_set_sampled_images = Shader::HostTranslateInfo::DEFAULT_DESCRIPTOR_LIMIT,
|
||||
.max_descriptor_set_storage_images = Shader::HostTranslateInfo::DEFAULT_DESCRIPTOR_LIMIT,
|
||||
.max_descriptor_set_input_attachements =
|
||||
Shader::HostTranslateInfo::DEFAULT_DESCRIPTOR_LIMIT,
|
||||
.support_float64 = true,
|
||||
.support_float16 = false,
|
||||
.support_int64 = device.HasShaderInt64(),
|
||||
.needs_demote_reorder = device.IsAmd(),
|
||||
.support_snorm_render_buffer = false,
|
||||
.support_viewport_index_layer = device.HasVertexViewportLayer(),
|
||||
.support_geometry_shader_passthrough = device.HasGeometryShaderPassthrough(),
|
||||
.support_conditional_barrier = device.SupportsConditionalBarriers(),
|
||||
} {
|
||||
host_info.ApplyDescriptorLimitPolicy();
|
||||
if (use_asynchronous_shaders) {
|
||||
workers = CreateWorkers();
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -56,10 +59,8 @@ UtilShaders::UtilShaders(ProgramManager& program_manager_)
|
||||
copy_bc4_program(MakeProgram(OPENGL_COPY_BC4_COMP)),
|
||||
convert_s8d24_program(MakeProgram(OPENGL_CONVERT_S8D24_COMP)),
|
||||
convert_ms_to_nonms_program(MakeProgram(CONVERT_MSAA_TO_NON_MSAA_COMP)),
|
||||
convert_nonms_to_ms_program(MakeProgram(CONVERT_NON_MSAA_TO_MSAA_COMP)) {
|
||||
const auto swizzle_table = Tegra::Texture::MakeSwizzleTable();
|
||||
swizzle_table_buffer.Create();
|
||||
glNamedBufferStorage(swizzle_table_buffer.handle, sizeof(swizzle_table), &swizzle_table, 0);
|
||||
convert_nonms_to_ms_program(MakeProgram(CONVERT_NON_MSAA_TO_MSAA_COMP))
|
||||
{
|
||||
}
|
||||
|
||||
UtilShaders::~UtilShaders() = default;
|
||||
@@ -116,13 +117,11 @@ void UtilShaders::ASTCDecode(Image& image, const StagingBufferMap& map,
|
||||
void UtilShaders::BlockLinearUpload2D(Image& image, const StagingBufferMap& map,
|
||||
std::span<const SwizzleParameters> swizzles) {
|
||||
static constexpr Extent3D WORKGROUP_SIZE{32, 32, 1};
|
||||
static constexpr GLuint BINDING_SWIZZLE_BUFFER = 0;
|
||||
static constexpr GLuint BINDING_INPUT_BUFFER = 1;
|
||||
static constexpr GLuint BINDING_INPUT_BUFFER = 0;
|
||||
static constexpr GLuint BINDING_OUTPUT_IMAGE = 0;
|
||||
|
||||
program_manager.BindComputeProgram(block_linear_unswizzle_2d_program.handle);
|
||||
glFlushMappedNamedBufferRange(map.buffer, map.offset, image.guest_size_bytes);
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, BINDING_SWIZZLE_BUFFER, swizzle_table_buffer.handle);
|
||||
|
||||
const GLenum store_format = StoreFormat(BytesPerBlock(image.info.format));
|
||||
for (const SwizzleParameters& swizzle : swizzles) {
|
||||
@@ -153,14 +152,11 @@ void UtilShaders::BlockLinearUpload2D(Image& image, const StagingBufferMap& map,
|
||||
void UtilShaders::BlockLinearUpload3D(Image& image, const StagingBufferMap& map,
|
||||
std::span<const SwizzleParameters> swizzles) {
|
||||
static constexpr Extent3D WORKGROUP_SIZE{16, 8, 8};
|
||||
|
||||
static constexpr GLuint BINDING_SWIZZLE_BUFFER = 0;
|
||||
static constexpr GLuint BINDING_INPUT_BUFFER = 1;
|
||||
static constexpr GLuint BINDING_INPUT_BUFFER = 0;
|
||||
static constexpr GLuint BINDING_OUTPUT_IMAGE = 0;
|
||||
|
||||
glFlushMappedNamedBufferRange(map.buffer, map.offset, image.guest_size_bytes);
|
||||
program_manager.BindComputeProgram(block_linear_unswizzle_3d_program.handle);
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, BINDING_SWIZZLE_BUFFER, swizzle_table_buffer.handle);
|
||||
|
||||
const GLenum store_format = StoreFormat(BytesPerBlock(image.info.format));
|
||||
for (const SwizzleParameters& swizzle : swizzles) {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -45,9 +48,6 @@ public:
|
||||
|
||||
private:
|
||||
ProgramManager& program_manager;
|
||||
|
||||
OGLBuffer swizzle_table_buffer;
|
||||
|
||||
OGLProgram astc_decoder_program;
|
||||
OGLProgram block_linear_unswizzle_2d_program;
|
||||
OGLProgram block_linear_unswizzle_3d_program;
|
||||
|
||||
@@ -22,6 +22,15 @@ namespace Vulkan {
|
||||
|
||||
using Shader::Backend::SPIRV::NUM_TEXTURE_AND_IMAGE_SCALING_WORDS;
|
||||
|
||||
[[nodiscard]] inline u32 NumDescriptorEntries(const Shader::Info& info) {
|
||||
return Shader::NumDescriptors(info.constant_buffer_descriptors) +
|
||||
Shader::NumDescriptors(info.storage_buffers_descriptors) +
|
||||
Shader::NumDescriptors(info.texture_buffer_descriptors) +
|
||||
Shader::NumDescriptors(info.image_buffer_descriptors) +
|
||||
Shader::NumDescriptors(info.texture_descriptors) +
|
||||
Shader::NumDescriptors(info.image_descriptors);
|
||||
}
|
||||
|
||||
class DescriptorLayoutBuilder {
|
||||
public:
|
||||
DescriptorLayoutBuilder(const Device& device_) : device{&device_} {}
|
||||
@@ -194,7 +203,11 @@ inline void PushImageDescriptors(TextureCache& texture_cache,
|
||||
const VideoCommon::ImageViewId image_view_id{(views++)->id};
|
||||
const VideoCommon::SamplerId sampler_id{*(samplers++)};
|
||||
ImageView& image_view{texture_cache.GetImageView(image_view_id)};
|
||||
const VkImageView vk_image_view{image_view.Handle(desc.type)};
|
||||
VkImageView vk_image_view{image_view.Handle(desc.type)};
|
||||
if (vk_image_view == VK_NULL_HANDLE) {
|
||||
const VkImageView null_image_view{texture_cache.GetImageView(VideoCommon::NULL_IMAGE_VIEW_ID).Handle(desc.type)};
|
||||
if (null_image_view != VK_NULL_HANDLE) vk_image_view = null_image_view;
|
||||
}
|
||||
const Sampler& sampler{texture_cache.GetSampler(sampler_id)};
|
||||
const bool use_fallback_sampler{sampler.HasAddedAnisotropy() &&
|
||||
!image_view.SupportsAnisotropy()};
|
||||
|
||||
@@ -326,7 +326,7 @@ std::pair<VkBuffer, VkDeviceSize> Uint8Pass::Assemble(u32 num_vertices, VkBuffer
|
||||
const u32 staging_size = static_cast<u32>(num_vertices * sizeof(u16));
|
||||
const auto staging = staging_buffer_pool.Request(staging_size, MemoryUsage::DeviceLocal);
|
||||
|
||||
compute_pass_descriptor_queue.Acquire();
|
||||
compute_pass_descriptor_queue.Acquire(scheduler, 2);
|
||||
compute_pass_descriptor_queue.AddBuffer(src_buffer, src_offset, num_vertices);
|
||||
compute_pass_descriptor_queue.AddBuffer(staging.buffer, staging.offset, staging_size);
|
||||
const void* const descriptor_data{compute_pass_descriptor_queue.UpdateData()};
|
||||
@@ -384,7 +384,7 @@ std::pair<VkBuffer, VkDeviceSize> QuadIndexedPass::Assemble(
|
||||
const std::size_t staging_size = num_tri_vertices * sizeof(u32);
|
||||
const auto staging = staging_buffer_pool.Request(staging_size, MemoryUsage::DeviceLocal);
|
||||
|
||||
compute_pass_descriptor_queue.Acquire();
|
||||
compute_pass_descriptor_queue.Acquire(scheduler, 2);
|
||||
compute_pass_descriptor_queue.AddBuffer(src_buffer, src_offset, input_size);
|
||||
compute_pass_descriptor_queue.AddBuffer(staging.buffer, staging.offset, staging_size);
|
||||
const void* const descriptor_data{compute_pass_descriptor_queue.UpdateData()};
|
||||
@@ -429,7 +429,7 @@ void ConditionalRenderingResolvePass::Resolve(VkBuffer dst_buffer, VkBuffer src_
|
||||
}
|
||||
const size_t compare_size = compare_to_zero ? 8 : 24;
|
||||
|
||||
compute_pass_descriptor_queue.Acquire();
|
||||
compute_pass_descriptor_queue.Acquire(scheduler, 2);
|
||||
compute_pass_descriptor_queue.AddBuffer(src_buffer, src_offset, compare_size);
|
||||
compute_pass_descriptor_queue.AddBuffer(dst_buffer, 0, sizeof(u32));
|
||||
const void* const descriptor_data{compute_pass_descriptor_queue.UpdateData()};
|
||||
@@ -498,7 +498,7 @@ void QueriesPrefixScanPass::Run(VkBuffer accumulation_buffer, VkBuffer dst_buffe
|
||||
static constexpr size_t DISPATCH_SIZE = 2048U;
|
||||
size_t runs_to_do = std::min<size_t>(current_runs, DISPATCH_SIZE);
|
||||
current_runs -= runs_to_do;
|
||||
compute_pass_descriptor_queue.Acquire();
|
||||
compute_pass_descriptor_queue.Acquire(scheduler, 3);
|
||||
compute_pass_descriptor_queue.AddBuffer(src_buffer, 0, number_of_sums * sizeof(u64));
|
||||
compute_pass_descriptor_queue.AddBuffer(dst_buffer, 0, number_of_sums * sizeof(u64));
|
||||
compute_pass_descriptor_queue.AddBuffer(accumulation_buffer, 0, sizeof(u64));
|
||||
@@ -600,7 +600,7 @@ void ASTCDecoderPass::Assemble(Image& image, const StagingBufferRef& map,
|
||||
const u32 num_dispatches_y = Common::DivCeil(swizzle.num_tiles.height, 8U);
|
||||
const u32 num_dispatches_z = image.info.resources.layers;
|
||||
|
||||
compute_pass_descriptor_queue.Acquire();
|
||||
compute_pass_descriptor_queue.Acquire(scheduler, 2);
|
||||
compute_pass_descriptor_queue.AddBuffer(map.buffer, input_offset,
|
||||
image.guest_size_bytes - swizzle.buffer_offset);
|
||||
compute_pass_descriptor_queue.AddImage(image.StorageImageView(swizzle.level));
|
||||
@@ -653,71 +653,8 @@ void ASTCDecoderPass::Assemble(Image& image, const StagingBufferRef& map,
|
||||
scheduler.Finish();
|
||||
}
|
||||
|
||||
constexpr u32 BL3D_BINDING_SWIZZLE_TABLE = 0;
|
||||
constexpr u32 BL3D_BINDING_INPUT_BUFFER = 1;
|
||||
constexpr u32 BL3D_BINDING_OUTPUT_BUFFER = 2;
|
||||
|
||||
constexpr std::array<VkDescriptorSetLayoutBinding, 3> BL3D_DESCRIPTOR_SET_BINDINGS{{
|
||||
{
|
||||
.binding = BL3D_BINDING_SWIZZLE_TABLE,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, // swizzle_table[]
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
|
||||
.pImmutableSamplers = nullptr,
|
||||
},
|
||||
{
|
||||
.binding = BL3D_BINDING_INPUT_BUFFER,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, // block-linear input
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
|
||||
.pImmutableSamplers = nullptr,
|
||||
},
|
||||
{
|
||||
.binding = BL3D_BINDING_OUTPUT_BUFFER,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
|
||||
.pImmutableSamplers = nullptr,
|
||||
},
|
||||
}};
|
||||
|
||||
constexpr DescriptorBankInfo BL3D_BANK_INFO{
|
||||
.uniform_buffers = 0,
|
||||
.storage_buffers = 3,
|
||||
.texture_buffers = 0,
|
||||
.image_buffers = 0,
|
||||
.textures = 0,
|
||||
.images = 0,
|
||||
.score = 3,
|
||||
};
|
||||
|
||||
constexpr std::array<VkDescriptorUpdateTemplateEntry, 3>
|
||||
BL3D_DESCRIPTOR_UPDATE_TEMPLATE_ENTRY{{
|
||||
{
|
||||
.dstBinding = BL3D_BINDING_SWIZZLE_TABLE,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||
.offset = BL3D_BINDING_SWIZZLE_TABLE * sizeof(DescriptorUpdateEntry),
|
||||
.stride = sizeof(DescriptorUpdateEntry),
|
||||
},
|
||||
{
|
||||
.dstBinding = BL3D_BINDING_INPUT_BUFFER,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||
.offset = BL3D_BINDING_INPUT_BUFFER * sizeof(DescriptorUpdateEntry),
|
||||
.stride = sizeof(DescriptorUpdateEntry),
|
||||
},
|
||||
{
|
||||
.dstBinding = BL3D_BINDING_OUTPUT_BUFFER,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||
.offset = BL3D_BINDING_OUTPUT_BUFFER * sizeof(DescriptorUpdateEntry),
|
||||
.stride = sizeof(DescriptorUpdateEntry),
|
||||
}
|
||||
}};
|
||||
constexpr u32 BL3D_BINDING_INPUT_BUFFER = 0;
|
||||
constexpr u32 BL3D_BINDING_OUTPUT_BUFFER = 1;
|
||||
|
||||
struct alignas(16) BlockLinearUnswizzle3DPushConstants {
|
||||
u32 blocks_dim[3]; // Offset 0
|
||||
@@ -745,11 +682,50 @@ BlockLinearUnswizzle3DPass::BlockLinearUnswizzle3DPass(
|
||||
DescriptorPool& descriptor_pool_,
|
||||
StagingBufferPool& staging_buffer_pool_,
|
||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue_)
|
||||
: ComputePass(
|
||||
device_, scheduler_, descriptor_pool_,
|
||||
BL3D_DESCRIPTOR_SET_BINDINGS,
|
||||
BL3D_DESCRIPTOR_UPDATE_TEMPLATE_ENTRY,
|
||||
BL3D_BANK_INFO,
|
||||
: ComputePass(device_, scheduler_, descriptor_pool_,
|
||||
std::array<VkDescriptorSetLayoutBinding, 2>{{
|
||||
{
|
||||
.binding = BL3D_BINDING_INPUT_BUFFER,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, // block-linear input
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
|
||||
.pImmutableSamplers = nullptr,
|
||||
},
|
||||
{
|
||||
.binding = BL3D_BINDING_OUTPUT_BUFFER,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
|
||||
.pImmutableSamplers = nullptr,
|
||||
},
|
||||
}},
|
||||
std::array<VkDescriptorUpdateTemplateEntry, 2>{{
|
||||
{
|
||||
.dstBinding = BL3D_BINDING_INPUT_BUFFER,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||
.offset = BL3D_BINDING_INPUT_BUFFER * sizeof(DescriptorUpdateEntry),
|
||||
.stride = sizeof(DescriptorUpdateEntry),
|
||||
},
|
||||
{
|
||||
.dstBinding = BL3D_BINDING_OUTPUT_BUFFER,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||
.offset = BL3D_BINDING_OUTPUT_BUFFER * sizeof(DescriptorUpdateEntry),
|
||||
.stride = sizeof(DescriptorUpdateEntry),
|
||||
}
|
||||
}},
|
||||
DescriptorBankInfo{
|
||||
.uniform_buffers = 0,
|
||||
.storage_buffers = 2,
|
||||
.texture_buffers = 0,
|
||||
.image_buffers = 0,
|
||||
.textures = 0,
|
||||
.images = 0,
|
||||
.score = 2,
|
||||
},
|
||||
COMPUTE_PUSH_CONSTANT_RANGE<sizeof(BlockLinearUnswizzle3DPushConstants)>,
|
||||
BLOCK_LINEAR_UNSWIZZLE_3D_BCN_COMP_SPV),
|
||||
scheduler{scheduler_},
|
||||
@@ -821,9 +797,7 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk(
|
||||
pc.blocks_dim[1] = blocks_y;
|
||||
pc.blocks_dim[2] = z_count; // Only process the count
|
||||
|
||||
compute_pass_descriptor_queue.Acquire();
|
||||
compute_pass_descriptor_queue.AddBuffer(*image.runtime->swizzle_table_buffer, 0,
|
||||
image.runtime->swizzle_table_size);
|
||||
compute_pass_descriptor_queue.Acquire(scheduler, 3);
|
||||
compute_pass_descriptor_queue.AddBuffer(swizzled.buffer,
|
||||
sw.buffer_offset + swizzled.offset,
|
||||
image.guest_size_bytes - sw.buffer_offset);
|
||||
@@ -989,7 +963,7 @@ void MSAACopyPass::CopyImage(Image& dst_image, Image& src_image,
|
||||
ASSERT(copy.dst_subresource.base_layer == 0);
|
||||
ASSERT(copy.dst_subresource.num_layers == 1);
|
||||
|
||||
compute_pass_descriptor_queue.Acquire();
|
||||
compute_pass_descriptor_queue.Acquire(scheduler, 2);
|
||||
compute_pass_descriptor_queue.AddImage(
|
||||
src_image.StorageImageView(copy.src_subresource.base_level));
|
||||
compute_pass_descriptor_queue.AddImage(
|
||||
|
||||
@@ -45,6 +45,7 @@ ComputePipeline::ComputePipeline(const Device& device_, Scheduler& scheduler, vk
|
||||
}
|
||||
std::copy_n(info.constant_buffer_used_sizes.begin(), uniform_buffer_sizes.size(),
|
||||
uniform_buffer_sizes.begin());
|
||||
num_descriptor_entries = NumDescriptorEntries(info);
|
||||
|
||||
auto func{[this, &scheduler, &descriptor_pool, shader_notify, pipeline_statistics] {
|
||||
DescriptorLayoutBuilder builder{device};
|
||||
@@ -113,7 +114,7 @@ ComputePipeline::ComputePipeline(const Device& device_, Scheduler& scheduler, vk
|
||||
void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute,
|
||||
Tegra::MemoryManager& gpu_memory, Scheduler& scheduler,
|
||||
BufferCache& buffer_cache, TextureCache& texture_cache) {
|
||||
guest_descriptor_queue.Acquire();
|
||||
guest_descriptor_queue.Acquire(scheduler, num_descriptor_entries);
|
||||
|
||||
buffer_cache.SetComputeUniformBufferState(info.constant_buffer_mask, &uniform_buffer_sizes);
|
||||
buffer_cache.UnbindComputeStorageBuffers();
|
||||
|
||||
@@ -53,6 +53,7 @@ private:
|
||||
vk::PipelineCache& pipeline_cache;
|
||||
GuestDescriptorQueue& guest_descriptor_queue;
|
||||
Shader::Info info;
|
||||
u32 num_descriptor_entries{};
|
||||
|
||||
VideoCommon::ComputeUniformBufferSizes uniform_buffer_sizes{};
|
||||
|
||||
|
||||
@@ -37,8 +37,6 @@
|
||||
|
||||
namespace Vulkan {
|
||||
namespace {
|
||||
using boost::container::small_vector;
|
||||
using boost::container::static_vector;
|
||||
using Shader::ImageBufferDescriptor;
|
||||
using Shader::Backend::SPIRV::RENDERAREA_LAYOUT_OFFSET;
|
||||
using Shader::Backend::SPIRV::RESCALING_LAYOUT_DOWN_FACTOR_OFFSET;
|
||||
@@ -268,6 +266,7 @@ GraphicsPipeline::GraphicsPipeline(
|
||||
num_textures += Shader::NumDescriptors(info->texture_descriptors);
|
||||
num_image_elements += Shader::NumDescriptors(info->texture_descriptors);
|
||||
num_image_elements += Shader::NumDescriptors(info->image_descriptors);
|
||||
num_descriptor_entries += NumDescriptorEntries(*info);
|
||||
}
|
||||
fragment_has_color0_output = stage_infos[NUM_STAGES - 1].stores_frag_color[0];
|
||||
auto func{[this, shader_notify, &render_pass_cache, &descriptor_pool, pipeline_statistics] {
|
||||
@@ -473,7 +472,7 @@ bool GraphicsPipeline::ConfigureImpl(bool is_indexed) {
|
||||
buffer_cache.UpdateGraphicsBuffers(is_indexed);
|
||||
buffer_cache.BindHostGeometryBuffers(is_indexed);
|
||||
|
||||
guest_descriptor_queue.Acquire();
|
||||
guest_descriptor_queue.Acquire(scheduler, num_descriptor_entries);
|
||||
|
||||
RescalingPushConstant rescaling;
|
||||
RenderAreaPushConstant render_area;
|
||||
@@ -584,9 +583,9 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
|
||||
} else {
|
||||
dynamic.raw1 = key.state.dynamic_state.raw1;
|
||||
}
|
||||
static_vector<VkVertexInputBindingDescription, 32> vertex_bindings;
|
||||
static_vector<VkVertexInputBindingDivisorDescriptionEXT, 32> vertex_binding_divisors;
|
||||
static_vector<VkVertexInputAttributeDescription, 32> vertex_attributes;
|
||||
boost::container::static_vector<VkVertexInputBindingDescription, 32> vertex_bindings;
|
||||
boost::container::static_vector<VkVertexInputBindingDivisorDescriptionEXT, 32> vertex_binding_divisors;
|
||||
boost::container::static_vector<VkVertexInputAttributeDescription, 32> vertex_attributes;
|
||||
if (!key.state.dynamic_vertex_input) {
|
||||
const size_t num_vertex_arrays = (std::min)(
|
||||
Maxwell::NumVertexArrays, static_cast<size_t>(device.GetMaxVertexInputBindings()));
|
||||
@@ -811,7 +810,7 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
|
||||
if (dynamic.depth_bounds_enable && !device.IsDepthBoundsSupported()) {
|
||||
LOG_WARNING(Render_Vulkan, "Depth bounds is enabled but not supported");
|
||||
}
|
||||
static_vector<VkPipelineColorBlendAttachmentState, Maxwell::NumRenderTargets> cb_attachments;
|
||||
boost::container::static_vector<VkPipelineColorBlendAttachmentState, Maxwell::NumRenderTargets> cb_attachments;
|
||||
const size_t num_attachments{NumAttachments(key.state)};
|
||||
for (size_t index = 0; index < num_attachments; ++index) {
|
||||
static constexpr std::array mask_table{
|
||||
@@ -847,7 +846,7 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
|
||||
.pAttachments = cb_attachments.data(),
|
||||
.blendConstants = {}
|
||||
};
|
||||
static_vector<VkDynamicState, 34> dynamic_states{
|
||||
boost::container::static_vector<VkDynamicState, 34> dynamic_states{
|
||||
VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR,
|
||||
VK_DYNAMIC_STATE_DEPTH_BIAS, VK_DYNAMIC_STATE_BLEND_CONSTANTS,
|
||||
VK_DYNAMIC_STATE_DEPTH_BOUNDS, VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK,
|
||||
@@ -942,12 +941,9 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
|
||||
.pNext = nullptr,
|
||||
.requiredSubgroupSize = GuestWarpSize,
|
||||
};
|
||||
static_vector<VkPipelineShaderStageCreateInfo, 5> shader_stages;
|
||||
boost::container::static_vector<VkPipelineShaderStageCreateInfo, 5> shader_stages;
|
||||
for (size_t stage = 0; stage < Maxwell::MaxShaderStage; ++stage) {
|
||||
if (!spv_modules[stage]) {
|
||||
continue;
|
||||
}
|
||||
[[maybe_unused]] auto& stage_ci =
|
||||
if (spv_modules[stage]) {
|
||||
shader_stages.emplace_back(VkPipelineShaderStageCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
@@ -957,6 +953,7 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
|
||||
.pName = "main",
|
||||
.pSpecializationInfo = nullptr,
|
||||
});
|
||||
}
|
||||
}
|
||||
VkPipelineCreateFlags flags{};
|
||||
if (device.IsKhrPipelineExecutablePropertiesEnabled() && Settings::values.renderer_debug.GetValue()) {
|
||||
|
||||
@@ -159,6 +159,7 @@ private:
|
||||
std::array<Shader::Info, NUM_STAGES> stage_infos;
|
||||
std::array<u32, 5> enabled_uniform_buffer_masks{};
|
||||
VideoCommon::UniformBufferSizes uniform_buffer_sizes{};
|
||||
u32 num_descriptor_entries{};
|
||||
size_t num_image_elements{};
|
||||
u32 num_textures{};
|
||||
bool fragment_has_color0_output{};
|
||||
|
||||
@@ -439,10 +439,21 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
|
||||
.has_broken_robust =
|
||||
device.IsNvidia() && device.GetNvidiaArch() <= NvidiaArchitecture::Arch_Pascal,
|
||||
.min_ssbo_alignment = device.GetStorageBufferAlignment(),
|
||||
.max_user_clip_distances = device.GetMaxUserClipDistances(),
|
||||
.max_user_clip_distances = device.GetMaxUserClipDistances()
|
||||
};
|
||||
|
||||
host_info = Shader::HostTranslateInfo{
|
||||
.min_ssbo_alignment = device.GetStorageBufferAlignment(),
|
||||
.max_per_stage_descriptor_sampled_images = device.GetMaxPerStageDescriptorSampledImages(),
|
||||
.max_per_stage_resources = device.GetMaxPerStageResources(),
|
||||
.max_descriptor_set_samplers = device.GetMaxDescriptorSetSamplers(),
|
||||
.max_descriptor_set_uniform_buffers = device.GetMaxDescriptorSetUniformBuffers(),
|
||||
.max_descriptor_set_uniform_buffers_dynamic = device.GetMaxDescriptorSetUniformBuffersDynamic(),
|
||||
.max_descriptor_set_storage_buffers = device.GetMaxDescriptorSetStorageBuffers(),
|
||||
.max_descriptor_set_storage_buffers_dynamic = device.GetMaxDescriptorSetStorageBuffersDynamic(),
|
||||
.max_descriptor_set_sampled_images = device.GetMaxDescriptorSetSampledImages(),
|
||||
.max_descriptor_set_storage_images = device.GetMaxDescriptorSetStorageImages(),
|
||||
.max_descriptor_set_input_attachements = device.GetMaxDescriptorSetInputAttachments(),
|
||||
.support_float64 = device.IsFloat64Supported(),
|
||||
.support_float16 = device.IsFloat16Supported(),
|
||||
.support_int64 = device.IsShaderInt64Supported(),
|
||||
@@ -451,13 +462,10 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
|
||||
driver_id == VK_DRIVER_ID_SAMSUNG_PROPRIETARY,
|
||||
.support_snorm_render_buffer = true,
|
||||
.support_viewport_index_layer = device.IsExtShaderViewportIndexLayerSupported(),
|
||||
.min_ssbo_alignment = static_cast<u32>(device.GetStorageBufferAlignment()),
|
||||
.max_per_stage_descriptor_sampled_images = device.GetMaxPerStageDescriptorSampledImages(),
|
||||
.max_per_stage_resources = device.GetMaxPerStageResources(),
|
||||
.max_descriptor_set_sampled_images = device.GetMaxDescriptorSetSampledImages(),
|
||||
.support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(),
|
||||
.support_conditional_barrier = device.SupportsConditionalBarriers(),
|
||||
};
|
||||
host_info.ApplyDescriptorLimitPolicy();
|
||||
|
||||
if (device.GetMaxVertexInputAttributes() < Maxwell::NumVertexAttributes) {
|
||||
LOG_WARNING(Render_Vulkan, "maxVertexInputAttributes is too low: {} < {}",
|
||||
@@ -492,10 +500,14 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
|
||||
device.IsExtExtendedDynamicState3BlendingSupported();
|
||||
dynamic_features.has_extended_dynamic_state_3_enables =
|
||||
device.IsExtExtendedDynamicState3EnablesSupported();
|
||||
dynamic_features.has_dynamic_state3_depth_clamp_enable = false;
|
||||
dynamic_features.has_dynamic_state3_depth_clamp_enable =
|
||||
dynamic_features.has_extended_dynamic_state_3_enables &&
|
||||
device.SupportsDynamicState3DepthClampEnable();
|
||||
dynamic_features.has_dynamic_state3_logic_op_enable =
|
||||
dynamic_features.has_extended_dynamic_state_3_enables &&
|
||||
device.SupportsDynamicState3LogicOpEnable();
|
||||
dynamic_features.has_dynamic_state3_line_stipple_enable =
|
||||
dynamic_features.has_extended_dynamic_state_3_enables &&
|
||||
device.SupportsDynamicState3LineStippleEnable();
|
||||
|
||||
// VIDS: Independent toggle (not affected by dyna_state levels)
|
||||
|
||||
@@ -203,7 +203,7 @@ RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra
|
||||
: gpu{gpu_}, device_memory{device_memory_}, device{device_},
|
||||
memory_allocator{memory_allocator_}, state_tracker{state_tracker_}, scheduler{scheduler_},
|
||||
staging_pool(device, memory_allocator, scheduler), descriptor_pool(device, scheduler),
|
||||
guest_descriptor_queue(device, scheduler), compute_pass_descriptor_queue(device, scheduler),
|
||||
guest_descriptor_queue(device), compute_pass_descriptor_queue(device),
|
||||
blit_image(device, scheduler, state_tracker, descriptor_pool), render_pass_cache(device),
|
||||
texture_cache_runtime{
|
||||
device, scheduler, memory_allocator, staging_pool,
|
||||
|
||||
@@ -155,15 +155,14 @@ void Scheduler::WaitWorker() {
|
||||
}
|
||||
|
||||
void Scheduler::DispatchWork() {
|
||||
if (chunk->Empty()) {
|
||||
return;
|
||||
if (chunk && !chunk->Empty()) {
|
||||
{
|
||||
std::scoped_lock ql{queue_mutex};
|
||||
work_queue.push(std::move(chunk));
|
||||
}
|
||||
event_cv.notify_all();
|
||||
AcquireNewChunk();
|
||||
}
|
||||
{
|
||||
std::scoped_lock ql{queue_mutex};
|
||||
work_queue.push(std::move(chunk));
|
||||
}
|
||||
event_cv.notify_all();
|
||||
AcquireNewChunk();
|
||||
}
|
||||
|
||||
void Scheduler::RequestRenderpass(const Framebuffer* framebuffer) {
|
||||
|
||||
@@ -909,40 +909,6 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, Scheduler& sched
|
||||
bl3d_unswizzle_pass.emplace(device, scheduler, descriptor_pool,
|
||||
staging_buffer_pool, compute_pass_descriptor_queue);
|
||||
}
|
||||
|
||||
// --- Create swizzle table buffer ---
|
||||
{
|
||||
auto table = Tegra::Texture::MakeSwizzleTable();
|
||||
|
||||
swizzle_table_size = static_cast<VkDeviceSize>(table.size() * sizeof(table[0]));
|
||||
|
||||
auto staging = staging_buffer_pool.Request(swizzle_table_size, MemoryUsage::Upload);
|
||||
std::memcpy(staging.mapped_span.data(), table.data(), static_cast<size_t>(swizzle_table_size));
|
||||
|
||||
VkBufferCreateInfo ci{
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
||||
.size = swizzle_table_size,
|
||||
.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
|
||||
VK_BUFFER_USAGE_TRANSFER_DST_BIT |
|
||||
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||
};
|
||||
swizzle_table_buffer = memory_allocator.CreateBuffer(ci, MemoryUsage::DeviceLocal);
|
||||
|
||||
scheduler.RequestOutsideRenderPassOperationContext();
|
||||
scheduler.Record([staging_buf = staging.buffer,
|
||||
dst_buf = *swizzle_table_buffer,
|
||||
size = swizzle_table_size,
|
||||
src_off = staging.offset](vk::CommandBuffer cmdbuf) {
|
||||
|
||||
const VkBufferCopy region{
|
||||
.srcOffset = src_off,
|
||||
.dstOffset = 0,
|
||||
.size = size,
|
||||
};
|
||||
cmdbuf.CopyBuffer(staging_buf, dst_buf, region);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void TextureCacheRuntime::Finish() {
|
||||
@@ -2484,18 +2450,13 @@ void TextureCacheRuntime::AccelerateImageUpload(
|
||||
|
||||
if (!Settings::values.gpu_unswizzle_enabled.GetValue() || !bl3d_unswizzle_pass) {
|
||||
if (IsPixelFormatBCn(image.info.format) && image.info.type == ImageType::e3D) {
|
||||
ASSERT_MSG(false, "GPU unswizzle is disabled for BCn 3D texture");
|
||||
ASSERT(false && "GPU unswizzle is disabled for BCn 3D texture");
|
||||
}
|
||||
ASSERT(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (bl3d_unswizzle_pass &&
|
||||
IsPixelFormatBCn(image.info.format) &&
|
||||
image.info.type == ImageType::e3D &&
|
||||
image.info.resources.levels == 1 &&
|
||||
image.info.resources.layers == 1) {
|
||||
|
||||
if (bl3d_unswizzle_pass && IsPixelFormatBCn(image.info.format) && image.info.type == ImageType::e3D && image.info.resources.levels == 1 && image.info.resources.layers == 1) {
|
||||
return bl3d_unswizzle_pass->Unswizzle(image, map, swizzles, z_start, z_count);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 2019 yuzu Emulator Project
|
||||
@@ -130,9 +130,6 @@ public:
|
||||
std::optional<ASTCDecoderPass> astc_decoder_pass;
|
||||
|
||||
std::optional<BlockLinearUnswizzle3DPass> bl3d_unswizzle_pass;
|
||||
vk::Buffer swizzle_table_buffer;
|
||||
VkDeviceSize swizzle_table_size = 0;
|
||||
|
||||
std::optional<MSAACopyPass> msaa_copy_pass;
|
||||
const Settings::ResolutionScalingInfo& resolution;
|
||||
std::array<std::vector<VkFormat>, VideoCore::Surface::MaxPixelFormat> view_formats;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <variant>
|
||||
#include <boost/container/static_vector.hpp>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging.h"
|
||||
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
||||
#include "video_core/renderer_vulkan/vk_update_descriptor.h"
|
||||
@@ -15,8 +16,9 @@
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
UpdateDescriptorQueue::UpdateDescriptorQueue(const Device& device_, Scheduler& scheduler_)
|
||||
: device{device_}, scheduler{scheduler_} {
|
||||
UpdateDescriptorQueue::UpdateDescriptorQueue(const Device& device_)
|
||||
: device{device_}
|
||||
{
|
||||
payload_start = payload.data();
|
||||
payload_cursor = payload.data();
|
||||
}
|
||||
@@ -31,13 +33,15 @@ void UpdateDescriptorQueue::TickFrame() {
|
||||
payload_cursor = payload_start;
|
||||
}
|
||||
|
||||
void UpdateDescriptorQueue::Acquire() {
|
||||
// Minimum number of entries required.
|
||||
// This is the maximum number of entries a single draw call might use.
|
||||
static constexpr size_t MIN_ENTRIES = 0x400;
|
||||
|
||||
if (std::distance(payload_start, payload_cursor) + MIN_ENTRIES >= FRAME_PAYLOAD_SIZE) {
|
||||
LOG_WARNING(Render_Vulkan, "Payload overflow, waiting for worker thread");
|
||||
void UpdateDescriptorQueue::Acquire(Scheduler& scheduler, size_t required_entries) {
|
||||
static constexpr size_t DEFAULT_REQUIRED_ENTRIES = 0x400;
|
||||
const size_t reserve = required_entries > 0 ? required_entries : DEFAULT_REQUIRED_ENTRIES;
|
||||
ASSERT_MSG(reserve < FRAME_PAYLOAD_SIZE, "Descriptor reservation {} >= frame capacity {}",
|
||||
reserve, FRAME_PAYLOAD_SIZE);
|
||||
const size_t used = static_cast<size_t>(std::distance(payload_start, payload_cursor));
|
||||
if (used + reserve >= FRAME_PAYLOAD_SIZE) {
|
||||
LOG_WARNING(Render_Vulkan, "Payload overflow (used={}, reserve={}, capacity={})",
|
||||
used, reserve, FRAME_PAYLOAD_SIZE);
|
||||
scheduler.WaitWorker();
|
||||
payload_cursor = payload_start;
|
||||
}
|
||||
|
||||
@@ -34,12 +34,11 @@ class UpdateDescriptorQueue final {
|
||||
static constexpr size_t PAYLOAD_SIZE = FRAME_PAYLOAD_SIZE * FRAMES_IN_FLIGHT;
|
||||
|
||||
public:
|
||||
explicit UpdateDescriptorQueue(const Device& device_, Scheduler& scheduler_);
|
||||
explicit UpdateDescriptorQueue(const Device& device_);
|
||||
~UpdateDescriptorQueue();
|
||||
|
||||
void TickFrame();
|
||||
|
||||
void Acquire();
|
||||
void Acquire(Scheduler& scheduler, size_t required_entries = 0);
|
||||
|
||||
const DescriptorUpdateEntry* UpdateData() const noexcept {
|
||||
return upload_start;
|
||||
@@ -75,8 +74,6 @@ public:
|
||||
|
||||
private:
|
||||
const Device& device;
|
||||
Scheduler& scheduler;
|
||||
|
||||
size_t frame_index{0};
|
||||
DescriptorUpdateEntry* payload_cursor = nullptr;
|
||||
DescriptorUpdateEntry* payload_start = nullptr;
|
||||
|
||||
@@ -255,8 +255,7 @@ std::optional<u64> GenericEnvironment::TryFindSize() {
|
||||
static constexpr u64 SELF_BRANCH_A = 0xE2400FFFFF87000FULL;
|
||||
static constexpr u64 SELF_BRANCH_B = 0xE2400FFFFF07000FULL;
|
||||
|
||||
static constexpr u64 MESA_EXIT_MASK = 0xFFF00000000F001FULL;
|
||||
static constexpr u64 MESA_EXIT_VALUE = (0xE30ULL << 52) | (0x7ULL << 16) | 0xFULL;
|
||||
static constexpr u64 EXIT_VALUE = 0xE30000000007000FULL;
|
||||
|
||||
code.resize(MAXIMUM_SIZE / INST_SIZE);
|
||||
|
||||
@@ -271,7 +270,7 @@ std::optional<u64> GenericEnvironment::TryFindSize() {
|
||||
if (inst == SELF_BRANCH_A || inst == SELF_BRANCH_B) {
|
||||
return offset + index;
|
||||
}
|
||||
if ((inst & MESA_EXIT_MASK) == MESA_EXIT_VALUE) {
|
||||
if (!is_proprietary_driver && inst == EXIT_VALUE) {
|
||||
return offset + index + INST_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -23,24 +26,6 @@ constexpr u32 GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT + GOB_SIZE_Z_
|
||||
constexpr u32 SWIZZLE_X_BITS = 0b100101111;
|
||||
constexpr u32 SWIZZLE_Y_BITS = 0b011010000;
|
||||
|
||||
using SwizzleTable = std::array<std::array<u32, GOB_SIZE_X>, GOB_SIZE_Y>;
|
||||
|
||||
/**
|
||||
* This table represents the internal swizzle of a gob, in format 16 bytes x 2 sector packing.
|
||||
* Calculates the offset of an (x, y) position within a swizzled texture.
|
||||
* Taken from the Tegra X1 Technical Reference Manual. pages 1187-1188
|
||||
*/
|
||||
constexpr SwizzleTable MakeSwizzleTable() {
|
||||
SwizzleTable table{};
|
||||
for (u32 y = 0; y < table.size(); ++y) {
|
||||
for (u32 x = 0; x < table[0].size(); ++x) {
|
||||
table[y][x] = ((x % 64) / 32) * 256 + ((y % 8) / 2) * 64 + ((x % 32) / 16) * 32 +
|
||||
(y % 2) * 16 + (x % 16);
|
||||
}
|
||||
}
|
||||
return table;
|
||||
}
|
||||
|
||||
/// Unswizzles a block linear texture into linear memory.
|
||||
void UnswizzleTexture(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel,
|
||||
u32 width, u32 height, u32 depth, u32 block_height, u32 block_depth,
|
||||
|
||||
@@ -321,32 +321,23 @@ public:
|
||||
return properties.properties.limits.maxPushConstantsSize;
|
||||
}
|
||||
|
||||
/// Returns the maximum size for shared memory.
|
||||
u32 GetMaxComputeSharedMemorySize() const {
|
||||
return properties.properties.limits.maxComputeSharedMemorySize;
|
||||
}
|
||||
|
||||
/// Returns the maximum number of dynamic storage buffer descriptors per set.
|
||||
u32 GetMaxDescriptorSetStorageBuffersDynamic() const {
|
||||
return properties.properties.limits.maxDescriptorSetStorageBuffersDynamic;
|
||||
}
|
||||
|
||||
/// Returns the maximum number of dynamic uniform buffer descriptors per set.
|
||||
u32 GetMaxDescriptorSetUniformBuffersDynamic() const {
|
||||
return properties.properties.limits.maxDescriptorSetUniformBuffersDynamic;
|
||||
}
|
||||
|
||||
u32 GetMaxPerStageDescriptorSampledImages() const {
|
||||
return properties.properties.limits.maxPerStageDescriptorSampledImages;
|
||||
}
|
||||
|
||||
u32 GetMaxPerStageResources() const {
|
||||
return properties.properties.limits.maxPerStageResources;
|
||||
}
|
||||
|
||||
u32 GetMaxDescriptorSetSampledImages() const {
|
||||
return properties.properties.limits.maxDescriptorSetSampledImages;
|
||||
}
|
||||
#define FN_MAX_LIMIT_LIST \
|
||||
FN_MAX_LIMIT_ELEM(ComputeSharedMemorySize) \
|
||||
FN_MAX_LIMIT_ELEM(PerStageDescriptorSampledImages) \
|
||||
FN_MAX_LIMIT_ELEM(PerStageResources) \
|
||||
FN_MAX_LIMIT_ELEM(DescriptorSetSamplers) \
|
||||
FN_MAX_LIMIT_ELEM(DescriptorSetUniformBuffers) \
|
||||
FN_MAX_LIMIT_ELEM(DescriptorSetUniformBuffersDynamic) \
|
||||
FN_MAX_LIMIT_ELEM(DescriptorSetStorageBuffers) \
|
||||
FN_MAX_LIMIT_ELEM(DescriptorSetStorageBuffersDynamic) \
|
||||
FN_MAX_LIMIT_ELEM(DescriptorSetSampledImages) \
|
||||
FN_MAX_LIMIT_ELEM(DescriptorSetStorageImages) \
|
||||
FN_MAX_LIMIT_ELEM(DescriptorSetInputAttachments)
|
||||
#define FN_MAX_LIMIT_ELEM(name) \
|
||||
u32 GetMax##name() const { return properties.properties.limits.max##name; }
|
||||
FN_MAX_LIMIT_LIST
|
||||
#undef FN_MAX_LIMIT_ELEM
|
||||
#undef FN_MAX_LIMIT_LIST
|
||||
|
||||
/// Returns float control properties of the device.
|
||||
const VkPhysicalDeviceFloatControlsPropertiesKHR& FloatControlProperties() const {
|
||||
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
# CPMUtil CMake Test Scripts
|
||||
|
||||
Testing out some CMake scripting functionality.
|
||||
Executable
+341
@@ -0,0 +1,341 @@
|
||||
#!/usr/bin/env -S cmake -P
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright 2026 crueter
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
cmake_minimum_required(VERSION 3.31)
|
||||
|
||||
# TODO: Account for CPMConfig.cmake
|
||||
list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR})
|
||||
list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/CMakeModules)
|
||||
set(CPMUTIL_ROOT ${CMAKE_SOURCE_DIR})
|
||||
|
||||
include(CPMUtil)
|
||||
|
||||
# Parse the JSON object for a given key.
|
||||
macro(parse_key key)
|
||||
get_json_object(${key})
|
||||
set(JSON_NAME ${key})
|
||||
parse_object(${object})
|
||||
endmacro()
|
||||
|
||||
# Get a package's effective URL, for an already parsed object
|
||||
function(get_package_url_object out)
|
||||
if (${url})
|
||||
set(${out} "${url}")
|
||||
else()
|
||||
get_package_url(URL_OUT "${out}"
|
||||
GIT_HOST "${git_host}"
|
||||
REPO "${repo}"
|
||||
VERSION "${version}"
|
||||
ARTIFACT "${artifact}"
|
||||
PACKAGE "${package}")
|
||||
endif()
|
||||
|
||||
return(PROPAGATE ${out})
|
||||
endfunction()
|
||||
|
||||
# Fetch a package from an already-parsed object.
|
||||
function(fetch_package_object)
|
||||
set(optionArgs FORCE)
|
||||
cmake_parse_arguments(ARG "${optionArgs}" "" "" ${ARGN})
|
||||
|
||||
if (${url})
|
||||
set(pkg_url "${url}")
|
||||
else()
|
||||
get_package_url(URL_OUT pkg_url
|
||||
GIT_HOST "${git_host}"
|
||||
REPO "${repo}"
|
||||
VERSION "${version}"
|
||||
ARTIFACT "${artifact}"
|
||||
PACKAGE "${package}")
|
||||
endif()
|
||||
|
||||
if (DEFINED CACHE_PATH_OVERRIDE)
|
||||
set(cache_path ${CACHE_PATH_OVERRIDE})
|
||||
else()
|
||||
get_cache_path(${package} ${version} cache_path)
|
||||
endif()
|
||||
|
||||
set(fetch_args
|
||||
URL "${pkg_url}"
|
||||
HASH "${hash}"
|
||||
PATH "${cache_path}"
|
||||
PATCHES ${patches})
|
||||
|
||||
if(ARG_FORCE)
|
||||
list(APPEND fetch_args FORCE)
|
||||
endif()
|
||||
|
||||
fetch_package(${fetch_args})
|
||||
endfunction()
|
||||
|
||||
# Format the cpmfile. Requires one of: jq, python, perl
|
||||
# If you don't have any of those, sorry not sorry.
|
||||
# Maybe I should make a shell-based alternative.
|
||||
function(format_cpmfile)
|
||||
# jq is the preferred formatter since it's the fastest
|
||||
cpm_find_program(JQ_EXECUTABLE jq)
|
||||
if (JQ_EXECUTABLE)
|
||||
set(command ${JQ_EXECUTABLE} --indent 4 -S .)
|
||||
else()
|
||||
# Python is simple and works
|
||||
find_package(Python 3.5 COMPONENTS Interpreter QUIET)
|
||||
if (Python_FOUND)
|
||||
set(command ${Python_EXECUTABLE} -m json.tool
|
||||
--indent 4 --sort-keys)
|
||||
else()
|
||||
# json_pp (part of perl) also works well
|
||||
cpm_find_program(JSONPP_EXECUTABLE json_pp)
|
||||
if (JSONPP_EXECUTABLE)
|
||||
set(json_opts "indent" "indent_length=4" "canonical"
|
||||
"space_after=1" "space_before=0")
|
||||
string(JOIN "," json_opts_str ${json_opts})
|
||||
|
||||
set(command ${JSONPP_EXECUTABLE} -f json -t json -json_opt
|
||||
"${json_opts_str}")
|
||||
else()
|
||||
fatal("Fatal: could not find one of jq, Python, or perl"
|
||||
"(json_pp). Install one of these packages to use"
|
||||
"CPMUtil's tooling. If they ARE installed, your"
|
||||
"CMake installation is broken.")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
get_cpmfile_path(file)
|
||||
mktempdir(TMP)
|
||||
set(tmp_file ${TMP}/cpmfile.json)
|
||||
|
||||
execute_process(COMMAND ${command}
|
||||
INPUT_FILE ${file}
|
||||
OUTPUT_FILE ${tmp_file})
|
||||
|
||||
# TODO: error handling, mv, cp?
|
||||
file(COPY_FILE ${tmp_file} ${file})
|
||||
|
||||
file(REMOVE_RECURSE ${TMP})
|
||||
endfunction()
|
||||
|
||||
# Computes expected SHA512 hash of a package
|
||||
function(get_package_hash url out)
|
||||
mktempdir(TMP)
|
||||
|
||||
get_filename_component(filename ${url} NAME)
|
||||
set(file ${TMP}/${filename})
|
||||
|
||||
cpm_download("${url}" "${file}")
|
||||
file(SHA512 ${file} ${out})
|
||||
|
||||
file(REMOVE_RECURSE ${TMP})
|
||||
return(PROPAGATE ${out})
|
||||
endfunction()
|
||||
|
||||
# Download and put the content into a variable.
|
||||
function(cpm_download_var url out)
|
||||
mktempdir(TMP)
|
||||
|
||||
set(file ${TMP}/tmp)
|
||||
cpm_download("${url}" "${file}")
|
||||
file(READ ${file} ${out})
|
||||
|
||||
file(REMOVE_RECURSE ${TMP})
|
||||
return(PROPAGATE ${out})
|
||||
endfunction()
|
||||
|
||||
# Check if a URL request succeeds without actually saving anything
|
||||
function(cpm_url_exists url out)
|
||||
foreach(i RANGE 5)
|
||||
file(DOWNLOAD "${url}" STATUS ret LOG log TIMEOUT 10)
|
||||
list(GET ret 0 code)
|
||||
|
||||
if (code EQUAL 0)
|
||||
set(${out} TRUE)
|
||||
break()
|
||||
else()
|
||||
if (log MATCHES "HTTP/[0-9.]+ (429|403)")
|
||||
sleep(5)
|
||||
continue()
|
||||
endif()
|
||||
|
||||
set(${out} FALSE)
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
return(PROPAGATE ${out})
|
||||
endfunction()
|
||||
|
||||
# Get latest tag for a package.
|
||||
# Requires an already-parsed object
|
||||
function(get_latest_tag out)
|
||||
# TODO: Ci packages
|
||||
if (NOT repo OR ci)
|
||||
set(${out} null)
|
||||
return(PROPAGATE ${out})
|
||||
endif()
|
||||
|
||||
# first determine if this is a tag or not
|
||||
if (git_host STREQUAL github.com)
|
||||
set(api "https://api.github.com/repos/${repo}")
|
||||
set(check_endpoint "/git/refs/tags")
|
||||
else()
|
||||
set(api "https://${git_host}/api/v1/repos/${repo}")
|
||||
set(check_endpoint "/tags")
|
||||
endif()
|
||||
|
||||
# artifacts must only check releases
|
||||
if ("${artifact}" STREQUAL "")
|
||||
set(json_key name)
|
||||
set(endpoint "/tags")
|
||||
else()
|
||||
set(json_key tag_name)
|
||||
set(endpoint "/releases")
|
||||
set(check_endpoint "/releases/tags")
|
||||
endif()
|
||||
|
||||
cpm_url_exists("${api}${check_endpoint}/${version}" is_tag)
|
||||
|
||||
if (NOT is_tag)
|
||||
set(${out} null)
|
||||
return(PROPAGATE ${out})
|
||||
endif()
|
||||
|
||||
# strip out prefixes e.g. boost-, openssl-, v
|
||||
# NOTE: subrels e.g. -1 at the end may cause issues.
|
||||
string(REGEX REPLACE "[^0-9.-]" "" t_numeric_version "${version}")
|
||||
string(REGEX REPLACE "-$|^-" "" t_numeric_version "${numeric_version}")
|
||||
|
||||
# now api req
|
||||
cpm_download_var("${api}${endpoint}" tags)
|
||||
|
||||
string(JSON len LENGTH ${tags})
|
||||
math(EXPR last_index "${len} - 1")
|
||||
|
||||
set(greatest_version_numeric ${t_numeric_version})
|
||||
set(greatest_version ${version})
|
||||
|
||||
foreach(i RANGE ${last_index})
|
||||
string(JSON tag_obj GET "${tags}" ${i})
|
||||
string(JSON tag_name GET "${tag_obj}" ${json_key})
|
||||
|
||||
# same as above
|
||||
string(REGEX REPLACE "[^0-9.-]" "" numeric_tag "${tag_name}")
|
||||
string(REGEX REPLACE "-$|^-" "" numeric_tag "${numeric_tag}")
|
||||
|
||||
if (numeric_tag VERSION_GREATER_EQUAL greatest_version_numeric)
|
||||
set(greatest_version_numeric ${numeric_tag})
|
||||
set(greatest_version ${tag_name})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# numeric version replacement
|
||||
if (numeric_version)
|
||||
set(${out} ${greatest_version_numeric})
|
||||
else()
|
||||
set(${out} ${greatest_version})
|
||||
endif()
|
||||
|
||||
return(PROPAGATE ${out})
|
||||
endfunction()
|
||||
|
||||
# TODO(crueter): Combine these
|
||||
|
||||
# Update hash and version of a package
|
||||
# Outputs the updated object
|
||||
function(modify_package object version hash out)
|
||||
string(JSON new_object SET "${object}" hash "\"${hash}\"")
|
||||
string(JSON new_object SET "${new_object}" version "\"${version}\"")
|
||||
|
||||
set(${out} "${new_object}")
|
||||
return(PROPAGATE ${out})
|
||||
endfunction()
|
||||
|
||||
# Update hash and numeric_version of a package
|
||||
# Outputs the updated object
|
||||
function(modify_package_numeric object version hash out)
|
||||
string(JSON new_object SET "${object}" hash "\"${hash}\"")
|
||||
string(JSON new_object SET "${new_object}" numeric_version "\"${version}\"")
|
||||
|
||||
set(${out} "${new_object}")
|
||||
return(PROPAGATE ${out})
|
||||
endfunction()
|
||||
|
||||
|
||||
function(get_cpmfile_keys out)
|
||||
get_cpmfile_content(object)
|
||||
|
||||
string(JSON len LENGTH ${object})
|
||||
|
||||
math(EXPR last_index "${len} - 1")
|
||||
foreach(i RANGE ${last_index})
|
||||
string(JSON key MEMBER ${object} ${i})
|
||||
list(APPEND ${out} ${key})
|
||||
endforeach()
|
||||
|
||||
return(PROPAGATE ${out})
|
||||
endfunction()
|
||||
|
||||
function(parse_script_args out)
|
||||
if(PRINT_USAGE)
|
||||
usage()
|
||||
cmake_language(EXIT 0)
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_SCRIPT_MODE_FILE OR NOT CMAKE_ARGC)
|
||||
set(${out} "" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(found_script FALSE)
|
||||
set(idx 0)
|
||||
|
||||
get_filename_component(script_name "${CMAKE_SCRIPT_MODE_FILE}" NAME)
|
||||
|
||||
while(idx LESS CMAKE_ARGC)
|
||||
if(found_script)
|
||||
set(arg "${CMAKE_ARGV${idx}}")
|
||||
if(NOT arg STREQUAL "--")
|
||||
list(APPEND positional_args ${arg})
|
||||
endif()
|
||||
elseif(CMAKE_ARGV${idx} STREQUAL CMAKE_SCRIPT_MODE_FILE)
|
||||
set(found_script TRUE)
|
||||
else()
|
||||
get_filename_component(arg_name "${CMAKE_ARGV${idx}}" NAME)
|
||||
if(arg_name STREQUAL script_name)
|
||||
set(found_script TRUE)
|
||||
endif()
|
||||
endif()
|
||||
math(EXPR idx "${idx} + 1")
|
||||
endwhile()
|
||||
|
||||
if(ALL_PACKAGES AND NOT NO_ALL)
|
||||
get_cpmfile_keys(all_keys)
|
||||
if(NO_CI)
|
||||
set(filtered_keys)
|
||||
foreach(key ${all_keys})
|
||||
parse_key(${key})
|
||||
if(NOT ci)
|
||||
list(APPEND filtered_keys ${key})
|
||||
endif()
|
||||
endforeach()
|
||||
set(positional_args ${filtered_keys})
|
||||
else()
|
||||
set(positional_args ${all_keys})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(${out} ${positional_args} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Convert a CMake list to a JSON array
|
||||
function(list_to_array list out)
|
||||
set(${out} "[]")
|
||||
set(idx 0)
|
||||
foreach(elem ${list})
|
||||
string(JSON ${out} SET ${${out}} "${idx}" "\"${elem}\"")
|
||||
math(EXPR idx "${idx} + 1")
|
||||
endforeach()
|
||||
|
||||
return(PROPAGATE ${out})
|
||||
endfunction()
|
||||
@@ -1,9 +1,11 @@
|
||||
#!/bin/sh -e
|
||||
#!/usr/bin/env -S cmake -P
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright 2026 crueter
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
jq --indent 4 -S <cpmfile.json >cpmfile.json.new
|
||||
mv cpmfile.json.new cpmfile.json
|
||||
cmake_minimum_required(VERSION 3.31)
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/ScriptUtils.cmake)
|
||||
|
||||
# TODO: Run some sanity checks e.g. patches exist, etc.
|
||||
format_cpmfile()
|
||||
Executable
+24
@@ -0,0 +1,24 @@
|
||||
#!/usr/bin/env -S cmake -P
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright 2026 crueter
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
cmake_minimum_required(VERSION 3.31)
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/ScriptUtils.cmake)
|
||||
|
||||
set(NO_ALL TRUE)
|
||||
|
||||
function(usage)
|
||||
echo([=[
|
||||
Usage: cpmutil.sh ls
|
||||
|
||||
List all packages in the cpmfile.
|
||||
]=])
|
||||
endfunction()
|
||||
|
||||
get_cpmfile_keys(keys)
|
||||
|
||||
foreach(key ${keys})
|
||||
echo("${key}")
|
||||
endforeach()
|
||||
@@ -0,0 +1,104 @@
|
||||
#!/usr/bin/env -S cmake -P
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright 2026 crueter
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
cmake_minimum_required(VERSION 3.31)
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../ScriptUtils.cmake)
|
||||
|
||||
function(usage)
|
||||
echo([=[
|
||||
Usage: add.cmake [OPTIONS...]
|
||||
|
||||
Internal use only.
|
||||
]=])
|
||||
endfunction()
|
||||
|
||||
if (NOT DEFINED KEY)
|
||||
fatal("KEY is required")
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED REPO)
|
||||
fatal("REPO is required")
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED VERSION)
|
||||
fatal("VERSION is required")
|
||||
endif()
|
||||
|
||||
|
||||
if (NOT DEFINED CI)
|
||||
set(CI FALSE)
|
||||
endif()
|
||||
|
||||
# construct json
|
||||
set(object "{}")
|
||||
|
||||
macro(add key val)
|
||||
set(${key} ${val})
|
||||
string(JSON object SET ${object} ${key} "\"${val}\"")
|
||||
endmacro()
|
||||
|
||||
add(repo ${REPO})
|
||||
add(version ${VERSION})
|
||||
|
||||
if (DEFINED GIT_HOST AND NOT "${GIT_HOST}" STREQUAL github.com)
|
||||
add(git_host ${GIT_HOST})
|
||||
else()
|
||||
set(git_host github.com)
|
||||
endif()
|
||||
|
||||
if (DEFINED PACKAGE)
|
||||
add(package ${PACKAGE})
|
||||
else()
|
||||
set(package ${KEY})
|
||||
endif()
|
||||
|
||||
if (DEFINED FIND_ARGS)
|
||||
add(find_args "${FIND_ARGS}")
|
||||
endif()
|
||||
|
||||
if (DEFINED MIN_VERSION)
|
||||
add(min_version ${MIN_VERSION})
|
||||
endif()
|
||||
|
||||
if (DEFINED ARTIFACT)
|
||||
add(artifact ${ARTIFACT})
|
||||
endif()
|
||||
|
||||
if (CI)
|
||||
add(ci true)
|
||||
|
||||
if (DEFINED DISABLED_PLATFORMS)
|
||||
list_to_array(${DISABLED_PLATFORMS} json_disabled)
|
||||
string(JSON object SET "${object}" disabled_platforms "${json_disabled}")
|
||||
endif()
|
||||
else()
|
||||
if (DEFINED OPTIONS)
|
||||
list_to_array(${OPTIONS} json_options)
|
||||
string(JSON object SET "${object}" options "${json_options}")
|
||||
endif()
|
||||
|
||||
# get hash
|
||||
get_package_url_object(pkg_url)
|
||||
get_package_hash("${pkg_url}" pkg_hash)
|
||||
|
||||
add(hash ${pkg_hash})
|
||||
|
||||
endif()
|
||||
|
||||
echo("\"${KEY}\": ${object}")
|
||||
|
||||
# now write
|
||||
get_cpmfile_content(cpmfile)
|
||||
|
||||
# update cached cpmfile content
|
||||
string(JSON cpmfile SET "${cpmfile}" "${KEY}" "${object}")
|
||||
|
||||
# write cached cpmfile
|
||||
get_cpmfile_path(file)
|
||||
file(WRITE ${file} "${cpmfile}")
|
||||
format_cpmfile()
|
||||
|
||||
echo("-- Added ${KEY}")
|
||||
Executable
+33
@@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env -S cmake -P
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright 2026 crueter
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
cmake_minimum_required(VERSION 3.31)
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../ScriptUtils.cmake)
|
||||
|
||||
function(usage)
|
||||
echo([=[
|
||||
Usage: cpmutil.sh package dir [-a|--all] [PACKAGE]...
|
||||
|
||||
Get the local directory for the specified packages.
|
||||
|
||||
Options:
|
||||
-a, --all Operate on all packages in this project.
|
||||
]=])
|
||||
endfunction()
|
||||
|
||||
parse_script_args(args)
|
||||
|
||||
# TODO: CI packages.
|
||||
|
||||
foreach(key ${args})
|
||||
parse_key(${key})
|
||||
|
||||
# Guh.
|
||||
get_cache_path(${package} ${version} cache_path)
|
||||
|
||||
cmake_path(ABSOLUTE_PATH cache_path NORMALIZE OUTPUT_VARIABLE abs_path)
|
||||
echo("${key}: ${abs_path}")
|
||||
endforeach()
|
||||
Executable
+34
@@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env -S cmake -P
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright 2026 crueter
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
cmake_minimum_required(VERSION 3.31)
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../ScriptUtils.cmake)
|
||||
|
||||
function(usage)
|
||||
echo([=[
|
||||
Usage: cpmutil.sh package fetch [-a|--all] [PACKAGE]...
|
||||
|
||||
Fetch the specified package or packages from their defined download locations.
|
||||
If the package is already cached, it will not be re-fetched.
|
||||
|
||||
Options:
|
||||
-a, --all Operate on all packages in this project.
|
||||
]=])
|
||||
endfunction()
|
||||
|
||||
set(NO_CI TRUE)
|
||||
parse_script_args(args)
|
||||
|
||||
foreach(key ${args})
|
||||
if (ci)
|
||||
continue()
|
||||
endif()
|
||||
|
||||
parse_key(${key})
|
||||
echo("-- ${key}")
|
||||
|
||||
fetch_package_object()
|
||||
endforeach()
|
||||
Executable
+59
@@ -0,0 +1,59 @@
|
||||
#!/usr/bin/env -S cmake -P
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright 2026 crueter
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
cmake_minimum_required(VERSION 3.31)
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../ScriptUtils.cmake)
|
||||
|
||||
function(usage)
|
||||
echo([=[
|
||||
Usage: cpmutil.sh package hash [-a|--all] [PACKAGE]...
|
||||
|
||||
Check the hash of a specific package or packages.
|
||||
If a hash mismatch occurs, this script will update the package's hash.
|
||||
|
||||
Options:
|
||||
-a, --all Operate on all packages in this project.
|
||||
|
||||
Note that this procedure will usually take a long time
|
||||
depending on the number and size of dependencies.
|
||||
]=])
|
||||
endfunction()
|
||||
|
||||
set(NO_CI TRUE)
|
||||
parse_script_args(args)
|
||||
|
||||
get_cpmfile_content(cpmfile)
|
||||
foreach(key ${args})
|
||||
if (ci)
|
||||
continue()
|
||||
endif()
|
||||
|
||||
parse_key("${key}")
|
||||
echo("-- ${key}")
|
||||
|
||||
get_package_url_object(pkg_url)
|
||||
get_package_hash("${pkg_url}" pkg_hash)
|
||||
|
||||
if (pkg_hash STREQUAL hash)
|
||||
echo("Hashes match")
|
||||
else()
|
||||
echo_error("Hash mismatch")
|
||||
echo_error("Expected: ${hash}")
|
||||
echo_error("Got: ${pkg_hash}")
|
||||
|
||||
modify_package("${object}" "${version}" "${pkg_hash}" new_object)
|
||||
|
||||
# update cached cpmfile content
|
||||
string(JSON cpmfile SET "${cpmfile}" "${key}" "${new_object}")
|
||||
|
||||
echo("Corrected hash for ${key}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# write cached cpmfile
|
||||
get_cpmfile_path(file)
|
||||
file(WRITE ${file} "${cpmfile}")
|
||||
format_cpmfile()
|
||||
@@ -0,0 +1,155 @@
|
||||
#!/usr/bin/env -S cmake -P
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright 2026 crueter
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
cmake_minimum_required(VERSION 3.31)
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../ScriptUtils.cmake)
|
||||
|
||||
function(usage)
|
||||
echo([=[
|
||||
Usage: cpmutil.sh package patch [PACKAGE]
|
||||
|
||||
Create an in-tree patch for the specified package.
|
||||
]=])
|
||||
endfunction()
|
||||
|
||||
set(NO_ALL TRUE)
|
||||
|
||||
# arg parsing
|
||||
parse_script_args(args)
|
||||
|
||||
list(LENGTH args arg_len)
|
||||
if(arg_len GREATER 0)
|
||||
list(GET args 0 KEY)
|
||||
endif()
|
||||
|
||||
if (NOT KEY)
|
||||
fatal("You must provide a key")
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED DESCRIPTION)
|
||||
fatal("No description provided")
|
||||
endif()
|
||||
|
||||
parse_key(${KEY})
|
||||
get_cache_path(${package} ${version} local_cache)
|
||||
|
||||
if (NOT EXISTS ${local_cache})
|
||||
fatal("${package} is not fetched locally")
|
||||
endif()
|
||||
|
||||
# get last patch number + 1
|
||||
list(LENGTH patches patches_len)
|
||||
math(EXPR last_index "${patches_len} - 1")
|
||||
list(GET patches ${last_index} patch)
|
||||
string(REGEX MATCH "[0-9][0-9][0-9][0-9]" number "${patch}")
|
||||
math(EXPR new_patchnum "${number} + 1")
|
||||
|
||||
# now pad it for filename usage
|
||||
set(padded "0000${new_patchnum}")
|
||||
string(LENGTH ${padded} total_len)
|
||||
math(EXPR start_index "${total_len} - 4")
|
||||
string(SUBSTRING ${padded} ${start_index} 4 padded_patch_number)
|
||||
|
||||
# this requires Git
|
||||
find_package(Git REQUIRED)
|
||||
|
||||
# fetch temporary package
|
||||
mktempdir(TMP)
|
||||
|
||||
set(CACHE_PATH_OVERRIDE ${TMP}/local)
|
||||
fetch_package_object()
|
||||
|
||||
# make patch dir
|
||||
set(patch_dir "${CPMUTIL_PATCH_DIR}/${KEY}")
|
||||
file(MAKE_DIRECTORY "${patch_dir}")
|
||||
|
||||
# git stuff
|
||||
macro(git_cmd out out_status)
|
||||
echo("${GIT_EXECUTABLE} ${ARGN}")
|
||||
# TODO: error handling
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} ${ARGN}
|
||||
WORKING_DIRECTORY ${CACHE_PATH_OVERRIDE}
|
||||
OUTPUT_VARIABLE ${out}
|
||||
RESULT_VARIABLE ${out_status})
|
||||
endmacro()
|
||||
|
||||
# initialize
|
||||
git_cmd(_ _ init)
|
||||
git_cmd(_ _ add -A)
|
||||
git_cmd(_ _ commit -m init)
|
||||
git_cmd(_ _ "--work-tree=${local_cache}" add -A)
|
||||
|
||||
# check for diffs
|
||||
git_cmd(_ diff_status diff --cached --quiet)
|
||||
git_cmd(out _ diff)
|
||||
|
||||
if (diff_status EQUAL 0)
|
||||
echo(${out})
|
||||
echo_error("No differences found between local copy and source")
|
||||
file(REMOVE_RECURSE ${TMP})
|
||||
cmake_language(EXIT 1)
|
||||
endif()
|
||||
|
||||
# prompt for patch description
|
||||
git_cmd(_ commit_status commit -m "${DESCRIPTION}")
|
||||
|
||||
# format patch
|
||||
git_cmd(patch_content _ format-patch -1 HEAD --stdout)
|
||||
|
||||
# now get patch name...
|
||||
|
||||
# strip out existing numeric prefix
|
||||
string(REGEX REPLACE "^[0-9][0-9][0-9][0-9]-" "" name_part "${DESCRIPTION}")
|
||||
# spaces to dashes
|
||||
string(REPLACE " " "-" name_part "${name_part}")
|
||||
# strip out non-alphanumeric or dash characters
|
||||
string(REGEX REPLACE "[^a-zA-Z0-9-]" "" name_part "${name_part}")
|
||||
# collapse consecutive dashes
|
||||
string(REGEX REPLACE "-+" "-" name_part "${name_part}")
|
||||
# strip leading/trailing dashes
|
||||
string(REGEX REPLACE "^-|-$" "" name_part "${name_part}")
|
||||
|
||||
if (NOT name_part)
|
||||
set(name_part "patch")
|
||||
echo_error("Warning: could not determine patch name")
|
||||
endif()
|
||||
|
||||
# Truncate to 49 chars (60 - num prefix - `.patch` suffix)
|
||||
string(SUBSTRING "${name_part}" 0 49 name_part)
|
||||
|
||||
# And remove any trailing dashes
|
||||
string(REGEX REPLACE "-$" "" name_part "${name_part}")
|
||||
|
||||
# now construct and save patch
|
||||
set(patch_name "${padded_patch_number}-${name_part}.patch")
|
||||
|
||||
file(WRITE "${patch_dir}/${patch_name}" "${patch_content}")
|
||||
|
||||
echo("-- Patch created at ${patch_dir}/${patch_name}")
|
||||
file(REMOVE_RECURSE ${TMP})
|
||||
|
||||
# Now add to cpmfile.
|
||||
get_cpmfile_content(content)
|
||||
|
||||
# Build new patches JSON array
|
||||
set(new_patches "[")
|
||||
if(patches)
|
||||
string(JSON existing GET "${content}" "${KEY}" "patches")
|
||||
string(JSON len LENGTH "${existing}")
|
||||
math(EXPR range_end "${len} - 1")
|
||||
foreach(idx RANGE ${range_end})
|
||||
string(JSON val GET "${existing}" ${idx})
|
||||
string(APPEND new_patches "\"${val}\",")
|
||||
endforeach()
|
||||
endif()
|
||||
string(APPEND new_patches "\"${patch_name}\"]")
|
||||
|
||||
# Update the cpmfile
|
||||
string(JSON content SET "${content}" "${KEY}" patches "${new_patches}")
|
||||
get_cpmfile_path(file_path)
|
||||
file(WRITE "${file_path}" "${content}")
|
||||
format_cpmfile()
|
||||
|
||||
Executable
+34
@@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env -S cmake -P
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright 2026 crueter
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
cmake_minimum_required(VERSION 3.31)
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../ScriptUtils.cmake)
|
||||
|
||||
function(usage)
|
||||
echo([=[
|
||||
Usage: cpmutil.sh package reset [-a|--all] [PACKAGE]...
|
||||
|
||||
Reset a locally fetched package to its original state.
|
||||
This is most useful for dropping any changes you've made.
|
||||
|
||||
Options:
|
||||
-a, --all Operate on all packages in this project.
|
||||
]=])
|
||||
endfunction()
|
||||
|
||||
set(NO_CI TRUE)
|
||||
parse_script_args(args)
|
||||
|
||||
foreach(key ${args})
|
||||
if (ci)
|
||||
continue()
|
||||
endif()
|
||||
|
||||
parse_key(${key})
|
||||
echo("-- ${key}")
|
||||
|
||||
fetch_package_object(FORCE)
|
||||
endforeach()
|
||||
Executable
+31
@@ -0,0 +1,31 @@
|
||||
#!/usr/bin/env -S cmake -P
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright 2026 crueter
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
cmake_minimum_required(VERSION 3.31)
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../ScriptUtils.cmake)
|
||||
|
||||
function(usage)
|
||||
echo([=[
|
||||
Usage: cpmutil.sh package rm [PACKAGE]...
|
||||
|
||||
Delete a package or packages' cpmfile definition.
|
||||
]=])
|
||||
endfunction()
|
||||
|
||||
set(NO_ALL TRUE)
|
||||
parse_script_args(args)
|
||||
|
||||
get_cpmfile_content(object)
|
||||
|
||||
# Remove key
|
||||
foreach(key ${args})
|
||||
string(JSON object REMOVE "${object}" ${key})
|
||||
endforeach()
|
||||
|
||||
# write
|
||||
get_cpmfile_path(file)
|
||||
file(WRITE ${file} "${object}")
|
||||
format_cpmfile()
|
||||
@@ -0,0 +1,97 @@
|
||||
#!/usr/bin/env -S cmake -P
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright 2026 crueter
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
cmake_minimum_required(VERSION 3.31)
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../ScriptUtils.cmake)
|
||||
|
||||
function(usage)
|
||||
echo([=[
|
||||
Usage: cpmutil.sh package update [-a|--all] [-c|--commit] [PACKAGE]...
|
||||
|
||||
Check for updates for a package or packages.
|
||||
|
||||
Options:
|
||||
-a, --all Operate on all packages in this project.
|
||||
-c, --commit Automatically generate a commit message
|
||||
]=])
|
||||
endfunction()
|
||||
|
||||
set(NO_CI TRUE)
|
||||
|
||||
parse_script_args(args)
|
||||
|
||||
get_cpmfile_content(cpmfile)
|
||||
set(update_log "")
|
||||
set(changed FALSE)
|
||||
|
||||
foreach(key ${args})
|
||||
parse_key(${key})
|
||||
|
||||
get_latest_tag(tag)
|
||||
|
||||
if (${tag} STREQUAL null)
|
||||
continue()
|
||||
elseif(NOT ${tag} STREQUAL ${version})
|
||||
if (numeric_version)
|
||||
set(old_version ${numeric_version})
|
||||
echo("${key}: ${old_version} -> ${tag}")
|
||||
set(numeric_version ${tag})
|
||||
get_json_element("${object}" version version)
|
||||
else()
|
||||
set(old_version ${version})
|
||||
echo("${key}: ${old_version} -> ${tag}")
|
||||
set(version ${tag})
|
||||
endif()
|
||||
|
||||
get_json_element("${object}" artifact artifact)
|
||||
|
||||
# Redo version replacements
|
||||
process_version_replacements()
|
||||
|
||||
get_package_url_object(pkg_url)
|
||||
get_package_hash("${pkg_url}" pkg_hash)
|
||||
|
||||
if (numeric_version)
|
||||
modify_package_numeric("${object}"
|
||||
"${tag}" "${pkg_hash}" new_object)
|
||||
else()
|
||||
modify_package("${object}" "${tag}" "${pkg_hash}" new_object)
|
||||
endif()
|
||||
|
||||
# update cached cpmfile content
|
||||
string(JSON cpmfile SET "${cpmfile}" "${key}" "${new_object}")
|
||||
|
||||
# used for commit
|
||||
string(APPEND update_log "* ${key}: ${old_version} -> ${tag}\n")
|
||||
set(changed TRUE)
|
||||
else()
|
||||
echo("${key}: Up to date")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
get_cpmfile_path(file)
|
||||
file(WRITE ${file} "${cpmfile}")
|
||||
format_cpmfile()
|
||||
|
||||
if(MAKE_COMMIT AND changed)
|
||||
mktempdir(TMP)
|
||||
|
||||
find_package(Git QUIET)
|
||||
|
||||
if (NOT Git_FOUND)
|
||||
fatal("Git is required to be installed for --commit,"
|
||||
"but it could not be found.")
|
||||
endif()
|
||||
|
||||
set(msg_file ${TMP}/commit_msg.txt)
|
||||
file(WRITE ${msg_file} "Update dependencies\n\n${update_log}")
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} add cpmfile.json
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} commit -F ${msg_file}
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
|
||||
|
||||
file(REMOVE_RECURSE ${TMP})
|
||||
endif()
|
||||
Executable
+28
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env -S cmake -P
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright 2026 crueter
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
cmake_minimum_required(VERSION 3.31)
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../ScriptUtils.cmake)
|
||||
|
||||
function(usage)
|
||||
echo([=[
|
||||
Usage: cpmutil.sh package url [-a|--all] [PACKAGE]...
|
||||
|
||||
Get the download URL for the specified packages.
|
||||
|
||||
Options:
|
||||
-a, --all Operate on all packages in this project.
|
||||
]=])
|
||||
endfunction()
|
||||
|
||||
parse_script_args(args)
|
||||
|
||||
foreach(key ${args})
|
||||
parse_key(${key})
|
||||
|
||||
get_package_url_object(pkg_url)
|
||||
echo("${key}: ${pkg_url}")
|
||||
endforeach()
|
||||
Executable
+75
@@ -0,0 +1,75 @@
|
||||
#!/usr/bin/env -S cmake -P
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright 2026 crueter
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
cmake_minimum_required(VERSION 3.31)
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../ScriptUtils.cmake)
|
||||
|
||||
function(usage)
|
||||
echo([=[
|
||||
Usage: cpmutil.sh package version [PACKAGE] [VERSION]
|
||||
|
||||
Update a package's version. If the package uses a sha, you must provide a sha,
|
||||
and if the package uses a tag, you must provide the fully qualified tag.
|
||||
]=])
|
||||
endfunction()
|
||||
|
||||
set(NO_ALL TRUE)
|
||||
# arg parsing
|
||||
parse_script_args(args)
|
||||
|
||||
list(LENGTH args arg_len)
|
||||
if(arg_len GREATER 0)
|
||||
list(GET args 0 KEY)
|
||||
endif()
|
||||
|
||||
if(arg_len GREATER 1)
|
||||
list(GET args 1 NEW_VERSION)
|
||||
endif()
|
||||
|
||||
# checks
|
||||
if (NOT KEY)
|
||||
fatal("You must provide a key")
|
||||
endif()
|
||||
|
||||
if (NOT NEW_VERSION)
|
||||
fatal("You must provide a version")
|
||||
endif()
|
||||
|
||||
# action
|
||||
get_cpmfile_content(cpmfile)
|
||||
parse_key("${KEY}")
|
||||
|
||||
if (numeric_version)
|
||||
set(numeric_version ${NEW_VERSION})
|
||||
get_json_element("${object}" version version)
|
||||
else()
|
||||
set(version ${NEW_VERSION})
|
||||
endif()
|
||||
|
||||
get_json_element("${object}" artifact artifact)
|
||||
|
||||
# Redo version replacements
|
||||
process_version_replacements()
|
||||
|
||||
get_package_url_object(pkg_url)
|
||||
get_package_hash("${pkg_url}" pkg_hash)
|
||||
|
||||
if (numeric_version)
|
||||
modify_package_numeric("${object}"
|
||||
"${NEW_VERSION}" "${pkg_hash}" new_object)
|
||||
else()
|
||||
modify_package("${object}" "${NEW_VERSION}" "${pkg_hash}" new_object)
|
||||
endif()
|
||||
|
||||
# update cached cpmfile content
|
||||
string(JSON cpmfile SET "${cpmfile}" "${key}" "${new_object}")
|
||||
|
||||
# write cached cpmfile
|
||||
get_cpmfile_path(file)
|
||||
file(WRITE ${file} "${cpmfile}")
|
||||
format_cpmfile()
|
||||
|
||||
echo("-- * Updated")
|
||||
Executable
+36
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env -S cmake -P
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright 2026 crueter
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
cmake_minimum_required(VERSION 3.31)
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../ScriptUtils.cmake)
|
||||
|
||||
function(usage)
|
||||
echo([=[
|
||||
Usage: cpmutil.sh package which [PACKAGE]...
|
||||
|
||||
Check if a package or packages are defined in the cpmfile.
|
||||
]=])
|
||||
endfunction()
|
||||
|
||||
set(NO_ALL TRUE)
|
||||
parse_script_args(args)
|
||||
set(exit 0)
|
||||
|
||||
get_cpmfile_content(object)
|
||||
|
||||
foreach(key ${args})
|
||||
# Check if a key exists
|
||||
string(JSON member ERROR_VARIABLE err GET "${object}" ${key})
|
||||
|
||||
if (NOT err)
|
||||
echo("${key}")
|
||||
else()
|
||||
echo_error("${key} not defined in cpmfile")
|
||||
set(exit 1)
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
cmake_language(EXIT ${exit})
|
||||
Executable
+33
@@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env -S cmake -P
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright 2026 crueter
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
cmake_minimum_required(VERSION 3.31)
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/ScriptUtils.cmake)
|
||||
|
||||
# Update CPMUtil and its tooling/etc
|
||||
set(pwd ${CMAKE_SOURCE_DIR})
|
||||
|
||||
set(host "https://git.crueter.xyz")
|
||||
set(repo "CMake/CPMUtil")
|
||||
set(release "releases/download/continuous")
|
||||
set(filename CPMUtil.tar.zst)
|
||||
|
||||
mktempdir(TMP)
|
||||
|
||||
# Download tarball
|
||||
set(url "${host}/${repo}/${release}/${filename}")
|
||||
set(file ${TMP}/${filename})
|
||||
|
||||
cpm_download(${url} ${file})
|
||||
|
||||
# Extract to current working directory
|
||||
file(ARCHIVE_EXTRACT
|
||||
INPUT ${file}
|
||||
DESTINATION ${pwd})
|
||||
|
||||
# done :)
|
||||
echo("Updated CPMUtil")
|
||||
file(REMOVE_RECURSE ${TMP})
|
||||
@@ -1,45 +0,0 @@
|
||||
#!/bin/sh -e
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright 2026 crueter
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
: "${CPM_SOURCE_CACHE:=$PWD/.cache/cpm}"
|
||||
: "${CPMUTIL_PATCH_DIR:=$PWD/.patch}"
|
||||
|
||||
# TODO: cache cpmfile defs?
|
||||
|
||||
cmd_exists() {
|
||||
command -v "$1" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
must_install() {
|
||||
for cmd in "$@"; do
|
||||
cmd_exists "$cmd" || { echo "-- $cmd must be installed" && exit 1; }
|
||||
done
|
||||
}
|
||||
|
||||
# Random integer between 100000 and 999999
|
||||
_randint() {
|
||||
awk 'BEGIN { srand(); print int(100000 + rand() * 900000) }'
|
||||
}
|
||||
|
||||
# Use mktemp if available, use a local temp dir otherwise
|
||||
make_temp_dir() {
|
||||
if cmd_exists mktemp; then
|
||||
mktemp -d
|
||||
else
|
||||
TMP="$PWD/.cpm/tmp-$(_randint)"
|
||||
mkdir -p "$TMP"
|
||||
echo "$TMP"
|
||||
fi
|
||||
}
|
||||
|
||||
# must_install jq find mktemp tar 7z unzip sha512sum git patch curl
|
||||
|
||||
if [ ! -s cpmfile.json ]; then
|
||||
# TODO: actually make it a no-op
|
||||
echo "-- Warning: cpmfile.json does not exist or is empty, most commands will be no-ops"
|
||||
else
|
||||
LIBS=$(jq -j 'keys_unsorted | join(" ")' cpmfile.json)
|
||||
export LIBS
|
||||
fi
|
||||
+15
-24
@@ -6,14 +6,22 @@
|
||||
SUBMODULES="$(git submodule status --recursive | cut -c2-)"
|
||||
: "${SUBMODULES:?No submodules defined!}"
|
||||
|
||||
tmp=$(mktemp)
|
||||
printf '{}' >"$tmp"
|
||||
|
||||
IFS="
|
||||
"
|
||||
for i in $SUBMODULES; do
|
||||
sha=$(echo "$i" | cut -d" " -f1 | cut -c1-10)
|
||||
ver=$(echo "$i" | cut -d" " -f3 | tr -d '()')
|
||||
commit=$(echo "$i" -d" " -f1 | cut -c1-10)
|
||||
short_commit=$(echo "$i" -d" " -f1 | cut -c1-7)
|
||||
|
||||
ref=$(echo "$i" | cut -d" " -f3 | tr -d '()')
|
||||
|
||||
case "$ref" in
|
||||
# ref == commit, use commit versioning
|
||||
"$short_commit") version="$commit" ;;
|
||||
# ref is a branch, use commit versioning
|
||||
heads/*) version="$commit" ;;
|
||||
# ref is (probably) a tag, use tag versioning
|
||||
*) version="$ref" ;;
|
||||
esac
|
||||
|
||||
path=$(echo "$i" | cut -d" " -f2)
|
||||
name=$(echo "$path" | awk -F/ '{print $NF}')
|
||||
@@ -21,26 +29,9 @@ for i in $SUBMODULES; do
|
||||
remote=$(git -C "$path" remote get-url origin)
|
||||
|
||||
host=$(echo "$remote" | cut -d"/" -f3)
|
||||
[ "$host" != github.com ] || host=
|
||||
|
||||
repo=$(echo "$remote" | cut -d"/" -f4-5 | cut -d'.' -f1)
|
||||
|
||||
entry=$(jq -n --arg name "$name" \
|
||||
--arg sha "$sha" \
|
||||
--arg ver "$ver" \
|
||||
--arg repo "$repo" \
|
||||
--arg host "$host" \
|
||||
'{
|
||||
($name): {
|
||||
sha: $sha,
|
||||
version: $ver,
|
||||
repo: $repo
|
||||
} + (if $host != "" then {git_host: $host} else {} end)
|
||||
}')
|
||||
|
||||
jq --argjson new "$entry" '. + $new' "$tmp" >"${tmp}.new"
|
||||
mv "$tmp.new" "$tmp"
|
||||
cmake -DKEY="$name" -DVERSION="$version" -DREPO="$repo" -DGIT_HOST="$host" \
|
||||
-P "$CMAKE/package/add.cmake"
|
||||
done
|
||||
|
||||
jq '.' "$tmp" >cpmfile.json
|
||||
rm -f "$tmp"
|
||||
|
||||
+50
-13
@@ -12,17 +12,17 @@ Usage: cpmutil.sh package [command]
|
||||
Operate on a package or packages.
|
||||
|
||||
Commands:
|
||||
hash Verify the hash of a package, and update it if needed
|
||||
update Check for updates for a package
|
||||
fetch Fetch a package and place it in the cache
|
||||
add Add a new package
|
||||
rm Remove a package
|
||||
version Change the version of a package
|
||||
which Check if a package is defined
|
||||
download Get the download URL for a package
|
||||
dir Get the local directory for a package
|
||||
reset Reset a fetched package to its original state
|
||||
patch Create an in-tree patch based on local modifications
|
||||
hash Verify the hash of a package, and update it if needed
|
||||
update Check for updates for a package
|
||||
fetch Fetch a package and place it in the cache
|
||||
add Add a new package
|
||||
rm Remove a package
|
||||
version Change the version of a package
|
||||
which Check if a package is defined
|
||||
url Get the download URL for a package
|
||||
dir Get the local directory for a package
|
||||
reset Reset a fetched package to its original state
|
||||
patch Add a patch to a package in the cpmfile
|
||||
|
||||
EOF
|
||||
|
||||
@@ -30,11 +30,48 @@ EOF
|
||||
}
|
||||
|
||||
SCRIPTS=$(CDPATH='' cd -- "$(dirname -- "$0")/package" && pwd)
|
||||
export SCRIPTS
|
||||
CMAKE="$CMAKE/package"
|
||||
|
||||
export SCRIPTS CMAKE
|
||||
|
||||
filter_args() {
|
||||
print_usage=false
|
||||
all_flag=false
|
||||
commit_flag=false
|
||||
filtered=""
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
-h | --help) print_usage=true ;;
|
||||
-a | --all) all_flag=true ;;
|
||||
-c | --commit) commit_flag=true ;;
|
||||
-ac | -ca)
|
||||
commit_flag=true
|
||||
all_flag=true
|
||||
;;
|
||||
*) filtered="$filtered $arg" ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
while :; do
|
||||
case "$1" in
|
||||
hash | update | fetch | add | rm | version | which | download | reset | patch | dir)
|
||||
dir | fetch | hash | reset | rm | url | version | which | update)
|
||||
cmd="$1"
|
||||
shift
|
||||
|
||||
filter_args "$@"
|
||||
cmake_defines=""
|
||||
|
||||
if $print_usage; then cmake_defines="$cmake_defines -DPRINT_USAGE=TRUE"; fi
|
||||
if $all_flag; then cmake_defines="$cmake_defines -DALL_PACKAGES=TRUE"; fi
|
||||
if $commit_flag; then cmake_defines="$cmake_defines -DMAKE_COMMIT=TRUE"; fi
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
cmake $cmake_defines -P "$CMAKE/$cmd.cmake" -- $filtered
|
||||
break
|
||||
;;
|
||||
add | patch)
|
||||
cmd="$1"
|
||||
shift
|
||||
"$SCRIPTS/$cmd".sh "$@"
|
||||
|
||||
+149
-2
@@ -49,6 +49,153 @@ done
|
||||
|
||||
[ -n "$PKG" ] || die "You must specify a package name."
|
||||
|
||||
export PKG
|
||||
# This reads a single-line input from the user and also gives them
|
||||
# help if needed.
|
||||
# $1: The prompt itself, without any trailing spaces or whatever
|
||||
# $2: The help text that gets shown when the user types a question mark
|
||||
# $3: This is set to "required" if it's necessary,
|
||||
# otherwise it can continue without input.
|
||||
# Stores its output in the "reply" variable
|
||||
read_single() {
|
||||
while :; do
|
||||
printf -- "-- %s" "$1"
|
||||
[ -z "$2" ] || printf " (? for help, %s)" "$3"
|
||||
printf ": "
|
||||
if ! IFS= read -r reply; then
|
||||
echo
|
||||
[ "$3" = "required" ] && continue || reply=""
|
||||
fi
|
||||
case "$reply" in
|
||||
"?") echo "$2" ;;
|
||||
"") [ "$3" = "required" ] && continue || return 0 ;;
|
||||
*) return 0 ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
"$SCRIPTS"/util/interactive.sh
|
||||
# read_single, but optional
|
||||
optional() {
|
||||
read_single "$1" "$2" "optional"
|
||||
}
|
||||
|
||||
# a
|
||||
required() {
|
||||
read_single "$1" "$2" "required"
|
||||
}
|
||||
|
||||
# Basically the same as the single line function except multiline,
|
||||
# also it's never "required" so we don't need that handling.
|
||||
multi() {
|
||||
echo "-- $1"
|
||||
if [ -n "$2" ]; then
|
||||
echo "-- (? on first line for help, Ctrl-D to finish)"
|
||||
else
|
||||
echo "-- (Ctrl-D to finish)"
|
||||
fi
|
||||
while :; do
|
||||
reply=$(cat)
|
||||
if [ "$(echo "$reply" | head -n 1)" = "?" ] && [ -n "$2" ]; then
|
||||
echo "$2"
|
||||
continue
|
||||
fi
|
||||
|
||||
# removes trailing EOF and empty lines
|
||||
reply=$(printf '%s\n' "$reply" |
|
||||
sed 's/\x04$//' |
|
||||
sed '/^[[:space:]]*$/d')
|
||||
|
||||
break
|
||||
done
|
||||
}
|
||||
|
||||
# the actual inputs :)
|
||||
|
||||
required "Package repository (owner/repo)" \
|
||||
"The remote repository this is stored on.
|
||||
You shouldn't include the host, just owner/repo is enough."
|
||||
|
||||
REPO="$reply"
|
||||
|
||||
required "Version of the bundled package" \
|
||||
"The tag or commit hash of the bundled package."
|
||||
|
||||
VERSION="$reply"
|
||||
|
||||
optional "Package name for find_package" \
|
||||
"When searching for system packages, this argument will be passed to find_package.
|
||||
For example, using \"Boost\" here will result in CPMUtil internally calling find_package(Boost).
|
||||
If unset, defaults to the JSON key."
|
||||
|
||||
PACKAGE="$reply"
|
||||
|
||||
optional "Minimum required version" \
|
||||
"The minimum required version for this package if it's pulled in by the system."
|
||||
|
||||
MIN_VERSION="$reply"
|
||||
|
||||
optional "Additional find_package arguments, space-separated" \
|
||||
"Extra arguments passed to find_package(), (e.g. CONFIG)"
|
||||
|
||||
FIND_ARGS="$reply"
|
||||
|
||||
optional "Git host (default: github.com)" \
|
||||
"The hostname of the Git server, if not GitHub (e.g. codeberg.org, git.crueter.xyz)"
|
||||
|
||||
GIT_HOST="$reply"
|
||||
|
||||
optional "Is this a CI package? [y/N]" \
|
||||
"Yes if the package is a prebuilt binary distribution (e.g. crueter-ci),
|
||||
no if the package is built from source if it's bundled."
|
||||
|
||||
case "$reply" in
|
||||
[Yy]*) CI=true ;;
|
||||
*) CI=false ;;
|
||||
esac
|
||||
|
||||
if [ "$CI" = "false" ]; then
|
||||
optional "Name of the release artifact to download, if applicable.
|
||||
-- %VERSION% is replaced by the version ($VERSION)" \
|
||||
"Download the specified artifact from the release with the previously specified tag."
|
||||
|
||||
ARTIFACT="$reply"
|
||||
|
||||
multi "Fixed options, one per line (e.g. OPUS_BUILD_TESTING OFF)" \
|
||||
"Fixed options passed to the project's CMakeLists.txt. Variadic options
|
||||
should be set in CMake with AddJsonPackage's OPTIONS parameter."
|
||||
|
||||
OPTIONS="$reply"
|
||||
else
|
||||
required "Name of the CI artifact" \
|
||||
"CI artifacts are stored as <name>-<platform>-<version>.tar.zst. This option controls the name."
|
||||
|
||||
ARTIFACT="$reply"
|
||||
|
||||
multi "Platforms without a package (one per line)" \
|
||||
"Valid platforms:
|
||||
windows-amd64 windows-arm64
|
||||
mingw-amd64 mingw-arm64
|
||||
android-aarch64 android-x86_64
|
||||
linux-amd64 linux-aarch64
|
||||
macos-universal ios-aarch64"
|
||||
|
||||
DISABLED_PLATFORMS="$reply"
|
||||
fi
|
||||
|
||||
# invoke add.cmake for validation
|
||||
set -- "$@" \
|
||||
-DKEY="$PKG" \
|
||||
-DREPO="$REPO" \
|
||||
-DVERSION="$VERSION" \
|
||||
-DCI="$CI"
|
||||
|
||||
[ -z "$PACKAGE" ] || set -- "$@" -DPACKAGE="$PACKAGE"
|
||||
[ -z "$GIT_HOST" ] || set -- "$@" -DGIT_HOST="$GIT_HOST"
|
||||
[ -z "$MIN_VERSION" ] || set -- "$@" -DMIN_VERSION="$MIN_VERSION"
|
||||
[ -z "$FIND_ARGS" ] || set -- "$@" -DFIND_ARGS="$FIND_ARGS"
|
||||
[ -z "$OPTIONS" ] || set -- "$@" -DOPTIONS="$(echo "$OPTIONS" | tr '\n' ';')"
|
||||
[ -z "$ARTIFACT" ] || set -- "$@" -DARTIFACT="$ARTIFACT"
|
||||
[ -z "$DISABLED_PLATFORMS" ] || set -- "$@" -DDISABLED_PLATFORMS="$(echo "$DISABLED_PLATFORMS" | tr '\n' ';')"
|
||||
|
||||
cmake "$@" -P "$CMAKE"/add.cmake
|
||||
|
||||
echo "Added package $PKG to cpmfile.json. Include it in your project with AddJsonPackage($PKG)"
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
#!/bin/sh -e
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright 2026 crueter
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
. "$SCRIPTS"/../common.sh
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: cpmutil.sh package dir [-a|--all] [PACKAGE]...
|
||||
|
||||
Get the local directory for the specified packages.
|
||||
|
||||
Options:
|
||||
-a, --all Operate on all packages in this project.
|
||||
|
||||
EOF
|
||||
|
||||
exit 0
|
||||
}
|
||||
|
||||
while :; do
|
||||
case "$1" in
|
||||
-a | --all) ALL=1 ;;
|
||||
-h | --help) usage ;;
|
||||
"$0") break ;;
|
||||
"") break ;;
|
||||
*) packages="$packages $1" ;;
|
||||
esac
|
||||
|
||||
shift
|
||||
done
|
||||
|
||||
[ "$ALL" != 1 ] || packages="${LIBS:-$packages}"
|
||||
[ -n "$packages" ] || usage
|
||||
|
||||
for pkg in $packages; do
|
||||
unset JSON
|
||||
export PACKAGE="$pkg"
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
. "$SCRIPTS"/vars.sh
|
||||
|
||||
# TODO: common get dir func
|
||||
if [ "$CI" = true ]; then
|
||||
dir="${CPM_SOURCE_CACHE}/${LOWER_PACKAGE}"
|
||||
else
|
||||
dir="${CPM_SOURCE_CACHE}/${LOWER_PACKAGE}/${KEY}"
|
||||
fi
|
||||
|
||||
echo "-- $pkg: $dir"
|
||||
|
||||
if [ ! -d "$dir" ]; then
|
||||
echo "-- * Warning: directory does not exist. Use fetch or reset to create it"
|
||||
fi
|
||||
done
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user