mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-17 21:57:52 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| be89eed7ad | |||
| 66b073ab8d |
@@ -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.
|
||||
|
||||
Vendored
-5
@@ -59,11 +59,6 @@ endif()
|
||||
if (PLATFORM_PS4 OR PLATFORM_MANAGARM)
|
||||
# Doesn't support VA-API, don't go thru the embarrassment of trying to enable it
|
||||
list(APPEND FFmpeg_HWACCEL_FLAGS --disable-vaapi)
|
||||
elseif (ANDROID)
|
||||
list(APPEND FFmpeg_HWACCEL_FLAGS
|
||||
--enable-mediacodec
|
||||
--enable-jni
|
||||
)
|
||||
elseif (UNIX AND NOT DEFINED FFmpeg_IS_CROSS_COMPILING AND NOT ANDROID)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(LIBVA libva)
|
||||
|
||||
@@ -27,7 +27,7 @@ if (ARCHITECTURE_arm64)
|
||||
target_link_libraries(yuzu-android PRIVATE adrenotools)
|
||||
endif()
|
||||
|
||||
target_link_libraries(yuzu-android PRIVATE ${FFmpeg_LIBRARIES} OpenSSL::SSL cpp-jwt::cpp-jwt)
|
||||
target_link_libraries(yuzu-android PRIVATE OpenSSL::SSL cpp-jwt::cpp-jwt)
|
||||
if (ENABLE_UPDATE_CHECKER)
|
||||
target_compile_definitions(yuzu-android PUBLIC ENABLE_UPDATE_CHECKER)
|
||||
endif()
|
||||
|
||||
@@ -36,10 +36,6 @@
|
||||
#include <frontend_common/content_manager.h>
|
||||
#include <jni.h>
|
||||
|
||||
extern "C" {
|
||||
#include <libavcodec/jni.h>
|
||||
}
|
||||
|
||||
#include "common/android/multiplayer/multiplayer.h"
|
||||
#include "common/android/android_common.h"
|
||||
#include "common/android/id_cache.h"
|
||||
@@ -385,6 +381,23 @@ void EmulationSession::HaltEmulation() {
|
||||
m_cv.notify_one();
|
||||
}
|
||||
|
||||
void EmulationSession::ReloadInputDevices() {
|
||||
std::scoped_lock lock(m_mutex);
|
||||
if (!m_is_running) {
|
||||
return;
|
||||
}
|
||||
m_system.HIDCore().ReloadInputDevices();
|
||||
}
|
||||
|
||||
void EmulationSession::ApplySettings() {
|
||||
std::scoped_lock lock(m_mutex);
|
||||
if (!m_is_running) {
|
||||
return;
|
||||
}
|
||||
m_system.ApplySettings();
|
||||
m_system.HIDCore().ReloadInputDevices();
|
||||
}
|
||||
|
||||
void EmulationSession::RunEmulation() {
|
||||
{
|
||||
std::scoped_lock lock(m_mutex);
|
||||
@@ -684,13 +697,6 @@ const char* fallback_cpu_detection() {
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" {
|
||||
jint InitFFmpegOnLoad(JavaVM* vm) {
|
||||
av_jni_set_java_vm(vm, nullptr);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceChanged(JNIEnv* env, jobject instance,
|
||||
@@ -1176,8 +1182,7 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getGpuModel(JNIEnv* env, jobject j
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_applySettings(JNIEnv* env, jobject jobj) {
|
||||
EmulationSession::GetInstance().System().ApplySettings();
|
||||
EmulationSession::GetInstance().System().HIDCore().ReloadInputDevices();
|
||||
EmulationSession::GetInstance().ApplySettings();
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_logSettings(JNIEnv* env, jobject jobj) {
|
||||
|
||||
@@ -45,6 +45,8 @@ public:
|
||||
void HaltEmulation();
|
||||
void RunEmulation();
|
||||
void ShutdownEmulation();
|
||||
void ReloadInputDevices();
|
||||
void ApplySettings();
|
||||
|
||||
const Core::PerfStatsResults& PerfStats();
|
||||
int ShadersBuilding();
|
||||
|
||||
@@ -282,7 +282,7 @@ void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onDeviceMotionEvent(
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_reloadInputDevices(JNIEnv* env,
|
||||
jobject j_obj) {
|
||||
EmulationSession::GetInstance().System().HIDCore().ReloadInputDevices();
|
||||
EmulationSession::GetInstance().ReloadInputDevices();
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_registerController(JNIEnv* env,
|
||||
|
||||
@@ -427,11 +427,8 @@ namespace Common::Android {
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
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)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
@@ -403,9 +403,6 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct
|
||||
if (info.uses_sampled_1d) {
|
||||
ctx.AddCapability(spv::Capability::Sampled1D);
|
||||
}
|
||||
if (info.uses_image_1d) {
|
||||
ctx.AddCapability(spv::Capability::Image1D);
|
||||
}
|
||||
if (info.uses_sparse_residency) {
|
||||
ctx.AddCapability(spv::Capability::SparseResidency);
|
||||
}
|
||||
@@ -435,7 +432,7 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct
|
||||
}
|
||||
if ((info.uses_subgroup_vote || info.uses_subgroup_invocation_id ||
|
||||
info.uses_subgroup_shuffles) &&
|
||||
profile.support_vote && profile.SupportsSubgroupStage(ctx.stage)) {
|
||||
profile.support_vote) {
|
||||
ctx.AddCapability(spv::Capability::GroupNonUniformBallot);
|
||||
ctx.AddCapability(spv::Capability::GroupNonUniformShuffle);
|
||||
if (!profile.warp_size_potentially_larger_than_guest) {
|
||||
|
||||
@@ -202,8 +202,7 @@ void EmitGetIndirectBranchVariable(EmitContext&) {
|
||||
}
|
||||
|
||||
Id EmitGetCbufU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
|
||||
if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int8 &&
|
||||
ctx.profile.support_uniform_and_storage_buffer_8bit) {
|
||||
if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int8) {
|
||||
const Id load{GetCbuf(ctx, ctx.U8, &UniformDefinitions::U8, sizeof(u8), binding, offset,
|
||||
ctx.load_const_func_u8)};
|
||||
return ctx.OpUConvert(ctx.U32[1], load);
|
||||
@@ -220,8 +219,7 @@ Id EmitGetCbufU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& of
|
||||
}
|
||||
|
||||
Id EmitGetCbufS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
|
||||
if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int8 &&
|
||||
ctx.profile.support_uniform_and_storage_buffer_8bit) {
|
||||
if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int8) {
|
||||
const Id load{GetCbuf(ctx, ctx.S8, &UniformDefinitions::S8, sizeof(s8), binding, offset,
|
||||
ctx.load_const_func_u8)};
|
||||
return ctx.OpSConvert(ctx.U32[1], load);
|
||||
@@ -238,8 +236,7 @@ Id EmitGetCbufS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& of
|
||||
}
|
||||
|
||||
Id EmitGetCbufU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
|
||||
if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int16 &&
|
||||
ctx.profile.support_uniform_and_storage_buffer_16bit) {
|
||||
if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int16) {
|
||||
const Id load{GetCbuf(ctx, ctx.U16, &UniformDefinitions::U16, sizeof(u16), binding, offset,
|
||||
ctx.load_const_func_u16)};
|
||||
return ctx.OpUConvert(ctx.U32[1], load);
|
||||
@@ -256,8 +253,7 @@ Id EmitGetCbufU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& o
|
||||
}
|
||||
|
||||
Id EmitGetCbufS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
|
||||
if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int16 &&
|
||||
ctx.profile.support_uniform_and_storage_buffer_16bit) {
|
||||
if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int16) {
|
||||
const Id load{GetCbuf(ctx, ctx.S16, &UniformDefinitions::S16, sizeof(s16), binding, offset,
|
||||
ctx.load_const_func_u16)};
|
||||
return ctx.OpSConvert(ctx.U32[1], load);
|
||||
|
||||
@@ -260,13 +260,6 @@ bool IsTextureMsaa(EmitContext& ctx, const IR::TextureInstInfo& info) {
|
||||
return ctx.textures.at(info.descriptor_index).is_multisample;
|
||||
}
|
||||
|
||||
bool IsTextureInteger(EmitContext& ctx, const IR::TextureInstInfo& info) {
|
||||
if (info.type == TextureType::Buffer) {
|
||||
return false;
|
||||
}
|
||||
return ctx.textures.at(info.descriptor_index).is_integer;
|
||||
}
|
||||
|
||||
Id Decorate(EmitContext& ctx, IR::Inst* inst, Id sample) {
|
||||
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
||||
if (info.relaxed_precision != 0) {
|
||||
@@ -487,14 +480,11 @@ Id EmitBoundImageWrite(EmitContext&) {
|
||||
Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id bias_lc, const IR::Value& offset) {
|
||||
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
||||
const bool is_integer{IsTextureInteger(ctx, info)};
|
||||
const Id result_type{is_integer ? ctx.U32[4] : ctx.F32[4]};
|
||||
Id color;
|
||||
if (ctx.stage == Stage::Fragment) {
|
||||
const ImageOperands operands(ctx, info.has_bias != 0, false, info.has_lod_clamp != 0,
|
||||
bias_lc, offset);
|
||||
color = Emit(&EmitContext::OpImageSparseSampleImplicitLod,
|
||||
&EmitContext::OpImageSampleImplicitLod, ctx, inst, result_type,
|
||||
return Emit(&EmitContext::OpImageSparseSampleImplicitLod,
|
||||
&EmitContext::OpImageSampleImplicitLod, ctx, inst, ctx.F32[4],
|
||||
Texture(ctx, info, index), coords, operands.MaskOptional(), operands.Span());
|
||||
} else {
|
||||
// We can't use implicit lods on non-fragment stages on SPIR-V. Maxwell hardware behaves as
|
||||
@@ -502,29 +492,26 @@ Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value&
|
||||
// derivatives
|
||||
const Id lod{ctx.Const(0.0f)};
|
||||
const ImageOperands operands(ctx, false, true, info.has_lod_clamp != 0, lod, offset);
|
||||
color = Emit(&EmitContext::OpImageSparseSampleExplicitLod,
|
||||
&EmitContext::OpImageSampleExplicitLod, ctx, inst, result_type,
|
||||
return Emit(&EmitContext::OpImageSparseSampleExplicitLod,
|
||||
&EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4],
|
||||
Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
|
||||
}
|
||||
return is_integer ? ctx.OpBitcast(ctx.F32[4], color) : color;
|
||||
}
|
||||
|
||||
Id EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id lod, const IR::Value& offset) {
|
||||
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
||||
const bool is_integer{IsTextureInteger(ctx, info)};
|
||||
const Id result_type{is_integer ? ctx.U32[4] : ctx.F32[4]};
|
||||
const ImageOperands operands(ctx, false, true, false, lod, offset);
|
||||
|
||||
Id result = Emit(&EmitContext::OpImageSparseSampleExplicitLod,
|
||||
&EmitContext::OpImageSampleExplicitLod, ctx, inst, result_type,
|
||||
&EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4],
|
||||
Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
|
||||
#ifdef __ANDROID__
|
||||
if (!is_integer && Settings::values.fix_bloom_effects.GetValue()) {
|
||||
if (Settings::values.fix_bloom_effects.GetValue()) {
|
||||
result = ctx.OpVectorTimesScalar(ctx.F32[4], result, ctx.Const(0.98f));
|
||||
}
|
||||
#endif
|
||||
return is_integer ? ctx.OpBitcast(ctx.F32[4], result) : result;
|
||||
return result;
|
||||
}
|
||||
|
||||
Id EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
|
||||
@@ -560,39 +547,30 @@ Id EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Va
|
||||
Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
const IR::Value& offset, const IR::Value& offset2) {
|
||||
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
||||
const bool is_integer{IsTextureInteger(ctx, info)};
|
||||
const Id result_type{is_integer ? ctx.U32[4] : ctx.F32[4]};
|
||||
const ImageOperands operands(ctx, offset, offset2);
|
||||
if (ctx.profile.need_gather_subpixel_offset) {
|
||||
coords = ImageGatherSubpixelOffset(ctx, info, TextureImage(ctx, info, index), coords);
|
||||
}
|
||||
const Id color{Emit(&EmitContext::OpImageSparseGather, &EmitContext::OpImageGather, ctx, inst,
|
||||
result_type, Texture(ctx, info, index), coords,
|
||||
ctx.Const(info.gather_component), operands.MaskOptional(),
|
||||
operands.Span())};
|
||||
return is_integer ? ctx.OpBitcast(ctx.F32[4], color) : color;
|
||||
return Emit(&EmitContext::OpImageSparseGather, &EmitContext::OpImageGather, ctx, inst,
|
||||
ctx.F32[4], Texture(ctx, info, index), coords, ctx.Const(info.gather_component),
|
||||
operands.MaskOptional(), operands.Span());
|
||||
}
|
||||
|
||||
Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
const IR::Value& offset, const IR::Value& offset2, Id dref) {
|
||||
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
||||
const bool is_integer{IsTextureInteger(ctx, info)};
|
||||
const Id result_type{is_integer ? ctx.U32[4] : ctx.F32[4]};
|
||||
const ImageOperands operands(ctx, offset, offset2);
|
||||
if (ctx.profile.need_gather_subpixel_offset) {
|
||||
coords = ImageGatherSubpixelOffset(ctx, info, TextureImage(ctx, info, index), coords);
|
||||
}
|
||||
const Id color{Emit(&EmitContext::OpImageSparseDrefGather, &EmitContext::OpImageDrefGather,
|
||||
ctx, inst, result_type, Texture(ctx, info, index), coords, dref,
|
||||
operands.MaskOptional(), operands.Span())};
|
||||
return is_integer ? ctx.OpBitcast(ctx.F32[4], color) : color;
|
||||
return Emit(&EmitContext::OpImageSparseDrefGather, &EmitContext::OpImageDrefGather, ctx, inst,
|
||||
ctx.F32[4], Texture(ctx, info, index), coords, dref, operands.MaskOptional(),
|
||||
operands.Span());
|
||||
}
|
||||
|
||||
Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset,
|
||||
Id lod, Id ms) {
|
||||
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
||||
const bool is_integer{IsTextureInteger(ctx, info)};
|
||||
const Id result_type{is_integer ? ctx.U32[4] : ctx.F32[4]};
|
||||
AddOffsetToCoordinates(ctx, info, coords, offset);
|
||||
if (info.type == TextureType::Buffer) {
|
||||
lod = Id{};
|
||||
@@ -602,10 +580,8 @@ Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id c
|
||||
lod = Id{};
|
||||
}
|
||||
const ImageOperands operands(lod, ms);
|
||||
const Id color{Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst,
|
||||
result_type, TextureImage(ctx, info, index), coords,
|
||||
operands.MaskOptional(), operands.Span())};
|
||||
return is_integer ? ctx.OpBitcast(ctx.F32[4], color) : color;
|
||||
return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4],
|
||||
TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span());
|
||||
}
|
||||
|
||||
Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod,
|
||||
@@ -650,17 +626,14 @@ Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, I
|
||||
Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id derivatives, const IR::Value& offset, Id lod_clamp) {
|
||||
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
||||
const bool is_integer{IsTextureInteger(ctx, info)};
|
||||
const Id result_type{is_integer ? ctx.U32[4] : ctx.F32[4]};
|
||||
const auto operands = info.num_derivatives == 3
|
||||
? ImageOperands(ctx, info.has_lod_clamp != 0, derivatives,
|
||||
ctx.Def(offset), {}, lod_clamp)
|
||||
: ImageOperands(ctx, info.has_lod_clamp != 0, derivatives,
|
||||
info.num_derivatives, offset, lod_clamp);
|
||||
const Id color{Emit(&EmitContext::OpImageSparseSampleExplicitLod,
|
||||
&EmitContext::OpImageSampleExplicitLod, ctx, inst, result_type,
|
||||
Texture(ctx, info, index), coords, operands.Mask(), operands.Span())};
|
||||
return is_integer ? ctx.OpBitcast(ctx.F32[4], color) : color;
|
||||
return Emit(&EmitContext::OpImageSparseSampleExplicitLod,
|
||||
&EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4],
|
||||
Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
|
||||
}
|
||||
|
||||
Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords) {
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -13,14 +10,7 @@ Id SubgroupScope(EmitContext& ctx) {
|
||||
return ctx.Const(static_cast<u32>(spv::Scope::Subgroup));
|
||||
}
|
||||
|
||||
bool StageSupportsSubgroups(EmitContext& ctx) {
|
||||
return ctx.profile.SupportsSubgroupStage(ctx.stage);
|
||||
}
|
||||
|
||||
Id GetThreadId(EmitContext& ctx) {
|
||||
if (!StageSupportsSubgroups(ctx)) {
|
||||
return ctx.u32_zero_value;
|
||||
}
|
||||
return ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id);
|
||||
}
|
||||
|
||||
@@ -78,9 +68,6 @@ Id GetMaxThreadId(EmitContext& ctx, Id thread_id, Id clamp, Id segmentation_mask
|
||||
}
|
||||
|
||||
Id SelectValue(EmitContext& ctx, Id in_range, Id value, Id src_thread_id) {
|
||||
if (!StageSupportsSubgroups(ctx)) {
|
||||
return value;
|
||||
}
|
||||
return ctx.OpSelect(
|
||||
ctx.U32[1], in_range,
|
||||
ctx.OpGroupNonUniformShuffle(ctx.U32[1], SubgroupScope(ctx), value, src_thread_id), value);
|
||||
@@ -102,9 +89,6 @@ Id EmitLaneId(EmitContext& ctx) {
|
||||
}
|
||||
|
||||
Id EmitVoteAll(EmitContext& ctx, Id pred) {
|
||||
if (!StageSupportsSubgroups(ctx)) {
|
||||
return pred;
|
||||
}
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
return ctx.OpGroupNonUniformAll(ctx.U1, SubgroupScope(ctx), pred);
|
||||
}
|
||||
@@ -118,9 +102,6 @@ Id EmitVoteAll(EmitContext& ctx, Id pred) {
|
||||
}
|
||||
|
||||
Id EmitVoteAny(EmitContext& ctx, Id pred) {
|
||||
if (!StageSupportsSubgroups(ctx)) {
|
||||
return pred;
|
||||
}
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
return ctx.OpGroupNonUniformAny(ctx.U1, SubgroupScope(ctx), pred);
|
||||
}
|
||||
@@ -134,9 +115,6 @@ Id EmitVoteAny(EmitContext& ctx, Id pred) {
|
||||
}
|
||||
|
||||
Id EmitVoteEqual(EmitContext& ctx, Id pred) {
|
||||
if (!StageSupportsSubgroups(ctx)) {
|
||||
return ctx.true_value;
|
||||
}
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
return ctx.OpGroupNonUniformAllEqual(ctx.U1, SubgroupScope(ctx), pred);
|
||||
}
|
||||
@@ -151,9 +129,6 @@ Id EmitVoteEqual(EmitContext& ctx, Id pred) {
|
||||
}
|
||||
|
||||
Id EmitSubgroupBallot(EmitContext& ctx, Id pred) {
|
||||
if (!StageSupportsSubgroups(ctx)) {
|
||||
return ctx.OpSelect(ctx.U32[1], pred, ctx.Const(1u), ctx.u32_zero_value);
|
||||
}
|
||||
const Id ballot{ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), pred)};
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
return ctx.OpCompositeExtract(ctx.U32[1], ballot, 0U);
|
||||
@@ -162,37 +137,22 @@ Id EmitSubgroupBallot(EmitContext& ctx, Id pred) {
|
||||
}
|
||||
|
||||
Id EmitSubgroupEqMask(EmitContext& ctx) {
|
||||
if (!StageSupportsSubgroups(ctx)) {
|
||||
return ctx.Const(1u);
|
||||
}
|
||||
return LoadMask(ctx, ctx.subgroup_mask_eq);
|
||||
}
|
||||
|
||||
Id EmitSubgroupLtMask(EmitContext& ctx) {
|
||||
if (!StageSupportsSubgroups(ctx)) {
|
||||
return ctx.u32_zero_value;
|
||||
}
|
||||
return LoadMask(ctx, ctx.subgroup_mask_lt);
|
||||
}
|
||||
|
||||
Id EmitSubgroupLeMask(EmitContext& ctx) {
|
||||
if (!StageSupportsSubgroups(ctx)) {
|
||||
return ctx.Const(1u);
|
||||
}
|
||||
return LoadMask(ctx, ctx.subgroup_mask_le);
|
||||
}
|
||||
|
||||
Id EmitSubgroupGtMask(EmitContext& ctx) {
|
||||
if (!StageSupportsSubgroups(ctx)) {
|
||||
return ctx.u32_zero_value;
|
||||
}
|
||||
return LoadMask(ctx, ctx.subgroup_mask_gt);
|
||||
}
|
||||
|
||||
Id EmitSubgroupGeMask(EmitContext& ctx) {
|
||||
if (!StageSupportsSubgroups(ctx)) {
|
||||
return ctx.Const(1u);
|
||||
}
|
||||
return LoadMask(ctx, ctx.subgroup_mask_ge);
|
||||
}
|
||||
|
||||
@@ -262,7 +222,7 @@ Id EmitShuffleButterfly(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id
|
||||
|
||||
Id EmitFSwizzleAdd(EmitContext& ctx, Id op_a, Id op_b, Id swizzle) {
|
||||
const Id three{ctx.Const(3U)};
|
||||
Id mask{GetThreadId(ctx)};
|
||||
Id mask{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)};
|
||||
mask = ctx.OpBitwiseAnd(ctx.U32[1], mask, three);
|
||||
mask = ctx.OpShiftLeftLogical(ctx.U32[1], mask, ctx.Const(1U));
|
||||
mask = ctx.OpShiftRightLogical(ctx.U32[1], swizzle, mask);
|
||||
|
||||
@@ -30,7 +30,7 @@ enum class Operation {
|
||||
|
||||
Id ImageType(EmitContext& ctx, const TextureDescriptor& desc) {
|
||||
const spv::ImageFormat format{spv::ImageFormat::Unknown};
|
||||
const Id type{desc.is_integer ? ctx.U32[1] : ctx.F32[1]};
|
||||
const Id type{ctx.F32[1]};
|
||||
const bool depth{desc.is_depth};
|
||||
const bool ms{desc.is_multisample};
|
||||
switch (desc.type) {
|
||||
@@ -1126,7 +1126,7 @@ void EmitContext::DefineConstantBuffers(const Info& info, u32& binding) {
|
||||
}
|
||||
IR::Type types{info.used_constant_buffer_types | info.used_indirect_cbuf_types};
|
||||
if (True(types & IR::Type::U8)) {
|
||||
if (profile.support_int8 && profile.support_uniform_and_storage_buffer_8bit) {
|
||||
if (profile.support_int8) {
|
||||
DefineConstBuffers(*this, info, &UniformDefinitions::U8, binding, U8, 'u', sizeof(u8));
|
||||
DefineConstBuffers(*this, info, &UniformDefinitions::S8, binding, S8, 's', sizeof(s8));
|
||||
} else {
|
||||
@@ -1134,7 +1134,7 @@ void EmitContext::DefineConstantBuffers(const Info& info, u32& binding) {
|
||||
}
|
||||
}
|
||||
if (True(types & IR::Type::U16)) {
|
||||
if (profile.support_int16 && profile.support_uniform_and_storage_buffer_16bit) {
|
||||
if (profile.support_int16) {
|
||||
DefineConstBuffers(*this, info, &UniformDefinitions::U16, binding, U16, 'u',
|
||||
sizeof(u16));
|
||||
DefineConstBuffers(*this, info, &UniformDefinitions::S16, binding, S16, 's',
|
||||
@@ -1196,18 +1196,10 @@ void EmitContext::DefineConstantBufferIndirectFunctions(const Info& info) {
|
||||
IR::Type types{info.used_indirect_cbuf_types};
|
||||
bool supports_aliasing = profile.support_descriptor_aliasing;
|
||||
if (supports_aliasing && True(types & IR::Type::U8)) {
|
||||
if (profile.support_int8 && profile.support_uniform_and_storage_buffer_8bit) {
|
||||
load_const_func_u8 = make_accessor(U8, &UniformDefinitions::U8);
|
||||
} else {
|
||||
types |= IR::Type::U32;
|
||||
}
|
||||
load_const_func_u8 = make_accessor(U8, &UniformDefinitions::U8);
|
||||
}
|
||||
if (supports_aliasing && True(types & IR::Type::U16)) {
|
||||
if (profile.support_int16 && profile.support_uniform_and_storage_buffer_16bit) {
|
||||
load_const_func_u16 = make_accessor(U16, &UniformDefinitions::U16);
|
||||
} else {
|
||||
types |= IR::Type::U32;
|
||||
}
|
||||
load_const_func_u16 = make_accessor(U16, &UniformDefinitions::U16);
|
||||
}
|
||||
if (supports_aliasing && True(types & IR::Type::F32)) {
|
||||
load_const_func_f32 = make_accessor(F32[1], &UniformDefinitions::F32);
|
||||
@@ -1383,7 +1375,6 @@ void EmitContext::DefineTextures(const Info& info, u32& binding, u32& scaling_in
|
||||
.image_type = image_type,
|
||||
.count = desc.count,
|
||||
.is_multisample = desc.is_multisample,
|
||||
.is_integer = desc.is_integer,
|
||||
});
|
||||
if (profile.supported_spirv >= 0x00010400) {
|
||||
interfaces.push_back(id);
|
||||
@@ -1447,7 +1438,7 @@ void EmitContext::DefineInputs(const IR::Program& program) {
|
||||
if (info.uses_is_helper_invocation) {
|
||||
is_helper_invocation = DefineInput(*this, U1, false, spv::BuiltIn::HelperInvocation);
|
||||
}
|
||||
if (info.uses_subgroup_mask && profile.SupportsSubgroupStage(stage)) {
|
||||
if (info.uses_subgroup_mask) {
|
||||
subgroup_mask_eq = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupEqMaskKHR);
|
||||
subgroup_mask_lt = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupLtMaskKHR);
|
||||
subgroup_mask_le = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupLeMaskKHR);
|
||||
@@ -1461,10 +1452,9 @@ void EmitContext::DefineInputs(const IR::Program& program) {
|
||||
Decorate(subgroup_mask_ge, spv::Decoration::Flat);
|
||||
}
|
||||
}
|
||||
if ((info.uses_fswzadd || info.uses_subgroup_invocation_id || info.uses_subgroup_shuffles ||
|
||||
(profile.warp_size_potentially_larger_than_guest &&
|
||||
(info.uses_subgroup_vote || info.uses_subgroup_mask))) &&
|
||||
profile.SupportsSubgroupStage(stage)) {
|
||||
if (info.uses_fswzadd || info.uses_subgroup_invocation_id || info.uses_subgroup_shuffles ||
|
||||
(profile.warp_size_potentially_larger_than_guest &&
|
||||
(info.uses_subgroup_vote || info.uses_subgroup_mask))) {
|
||||
AddCapability(spv::Capability::GroupNonUniform);
|
||||
subgroup_local_invocation_id =
|
||||
DefineInput(*this, U32[1], false, spv::BuiltIn::SubgroupLocalInvocationId);
|
||||
|
||||
@@ -42,7 +42,6 @@ struct TextureDefinition {
|
||||
Id image_type;
|
||||
u32 count;
|
||||
bool is_multisample;
|
||||
bool is_integer;
|
||||
};
|
||||
|
||||
struct TextureBufferDefinition {
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// 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
|
||||
|
||||
@@ -46,10 +43,6 @@ union TextureInstInfo {
|
||||
BitField<25, 2, u32> num_derivatives;
|
||||
BitField<27, 3, ImageFormat> image_format;
|
||||
BitField<30, 1, u32> ndv_is_active;
|
||||
/// Only meaningful for the sampled-texture family (ImageSampleImplicitLod, ImageFetch,
|
||||
/// ImageGather, etc.); unused/zero for storage-image opcodes, which carry their own
|
||||
/// is_integer via ImageDescriptor/ImageBufferDescriptor instead.
|
||||
BitField<31, 1, u32> is_integer;
|
||||
};
|
||||
static_assert(sizeof(TextureInstInfo) <= sizeof(u32));
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
@@ -569,8 +569,6 @@ void VisitUsages(Info& info, IR::Inst& inst) {
|
||||
case IR::Opcode::ImageRead: {
|
||||
const auto flags{inst.Flags<IR::TextureInstInfo>()};
|
||||
info.uses_typeless_image_reads |= flags.image_format == ImageFormat::Typeless;
|
||||
info.uses_image_1d |=
|
||||
flags.type == TextureType::Color1D || flags.type == TextureType::ColorArray1D;
|
||||
info.uses_sparse_residency |=
|
||||
inst.GetAssociatedPseudoOperation(IR::Opcode::GetSparseFromOp) != nullptr;
|
||||
break;
|
||||
@@ -579,8 +577,6 @@ void VisitUsages(Info& info, IR::Inst& inst) {
|
||||
const auto flags{inst.Flags<IR::TextureInstInfo>()};
|
||||
info.uses_typeless_image_writes |= flags.image_format == ImageFormat::Typeless;
|
||||
info.uses_image_buffers |= flags.type == TextureType::Buffer;
|
||||
info.uses_image_1d |=
|
||||
flags.type == TextureType::Color1D || flags.type == TextureType::ColorArray1D;
|
||||
break;
|
||||
}
|
||||
case IR::Opcode::SubgroupEqMask:
|
||||
@@ -765,13 +761,9 @@ void VisitUsages(Info& info, IR::Inst& inst) {
|
||||
case IR::Opcode::ImageAtomicAnd32:
|
||||
case IR::Opcode::ImageAtomicOr32:
|
||||
case IR::Opcode::ImageAtomicXor32:
|
||||
case IR::Opcode::ImageAtomicExchange32: {
|
||||
const auto flags{inst.Flags<IR::TextureInstInfo>()};
|
||||
case IR::Opcode::ImageAtomicExchange32:
|
||||
info.uses_atomic_image_u32 = true;
|
||||
info.uses_image_1d |=
|
||||
flags.type == TextureType::Color1D || flags.type == TextureType::ColorArray1D;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -562,7 +562,6 @@ public:
|
||||
})};
|
||||
// TODO: Read this from TIC
|
||||
texture_descriptors[index].is_multisample |= desc.is_multisample;
|
||||
texture_descriptors[index].is_integer |= desc.is_integer;
|
||||
return index;
|
||||
}
|
||||
|
||||
@@ -686,21 +685,6 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
|
||||
program.info.image_descriptors,
|
||||
};
|
||||
const u32 sampled_dynamic_cap = DynamicSampledTextureCap(program.info, host_info, DynamicSampledTextureArrayCount(to_replace));
|
||||
bool has_last_is_integer{false};
|
||||
u32 last_cbuf_index{};
|
||||
u32 last_cbuf_offset{};
|
||||
bool last_is_integer{false};
|
||||
const auto is_texture_pixel_format_integer{[&](const ConstBufferAddr& cbuf_addr) {
|
||||
if (has_last_is_integer && last_cbuf_index == cbuf_addr.index &&
|
||||
last_cbuf_offset == cbuf_addr.offset) {
|
||||
return last_is_integer;
|
||||
}
|
||||
last_is_integer = IsTexturePixelFormatIntegerCached(env, cbuf_addr);
|
||||
last_cbuf_index = cbuf_addr.index;
|
||||
last_cbuf_offset = cbuf_addr.offset;
|
||||
has_last_is_integer = true;
|
||||
return last_is_integer;
|
||||
}};
|
||||
for (TextureInst& texture_inst : to_replace) {
|
||||
// TODO: Handle arrays
|
||||
IR::Inst* const inst{texture_inst.inst};
|
||||
@@ -765,7 +749,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
|
||||
}
|
||||
const bool is_written{inst->GetOpcode() != IR::Opcode::ImageRead};
|
||||
const bool is_read{inst->GetOpcode() != IR::Opcode::ImageWrite};
|
||||
const bool is_integer{is_texture_pixel_format_integer(cbuf)};
|
||||
const bool is_integer{IsTexturePixelFormatIntegerCached(env, cbuf)};
|
||||
if (flags.type == TextureType::Buffer) {
|
||||
index = descriptors.Add(ImageBufferDescriptor{
|
||||
.format = flags.image_format,
|
||||
@@ -807,12 +791,10 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
|
||||
});
|
||||
} else {
|
||||
count = std::min(count, sampled_dynamic_cap);
|
||||
const bool is_integer{is_texture_pixel_format_integer(cbuf)};
|
||||
index = descriptors.Add(TextureDescriptor{
|
||||
.type = flags.type,
|
||||
.is_depth = flags.is_depth != 0,
|
||||
.is_multisample = is_multisample,
|
||||
.is_integer = is_integer,
|
||||
.has_secondary = cbuf.has_secondary,
|
||||
.cbuf_index = cbuf.index,
|
||||
.cbuf_offset = cbuf.offset,
|
||||
@@ -823,7 +805,6 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
|
||||
.count = count,
|
||||
.size_shift = size_shift,
|
||||
});
|
||||
flags.is_integer.Assign(is_integer ? 1 : 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -10,16 +10,12 @@
|
||||
|
||||
namespace Shader {
|
||||
|
||||
enum class Stage : u32;
|
||||
|
||||
struct Profile {
|
||||
u32 supported_spirv{0x00010000};
|
||||
bool unified_descriptor_binding{};
|
||||
bool support_descriptor_aliasing{};
|
||||
bool support_int8{};
|
||||
bool support_uniform_and_storage_buffer_8bit{};
|
||||
bool support_int16{};
|
||||
bool support_uniform_and_storage_buffer_16bit{};
|
||||
bool support_int64{};
|
||||
bool support_vertex_instance_id{};
|
||||
bool support_float_controls{};
|
||||
@@ -34,7 +30,6 @@ struct Profile {
|
||||
bool support_fp64_signed_zero_nan_preserve{};
|
||||
bool support_explicit_workgroup_layout{};
|
||||
bool support_vote{};
|
||||
u32 supported_subgroup_stages{0x7F};
|
||||
bool support_viewport_index_layer_non_geometry{};
|
||||
bool support_viewport_mask{};
|
||||
bool support_typeless_image_loads{};
|
||||
@@ -98,10 +93,6 @@ struct Profile {
|
||||
|
||||
u64 min_ssbo_alignment{};
|
||||
u32 max_user_clip_distances{};
|
||||
|
||||
bool SupportsSubgroupStage(Stage stage) const {
|
||||
return (supported_subgroup_stages & (1u << static_cast<u32>(stage))) != 0;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Shader
|
||||
|
||||
@@ -209,7 +209,6 @@ struct TextureDescriptor {
|
||||
TextureType type;
|
||||
bool is_depth;
|
||||
bool is_multisample;
|
||||
bool is_integer;
|
||||
bool has_secondary;
|
||||
u32 cbuf_index;
|
||||
u32 cbuf_offset;
|
||||
|
||||
@@ -20,54 +20,58 @@ 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();
|
||||
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 {
|
||||
std::tie(offsets.luma, std::ignore) = GetProgressiveOffsets();
|
||||
}
|
||||
|
||||
// Send assembled bitstream to decoder.
|
||||
if (!decode_api.SendPacket(packet_data, offsets, GetFrameDimensions())) {
|
||||
if (!decode_api.SendPacket(packet_data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto push = [&](u64 luma, std::shared_ptr<FFmpeg::Frame> frame) {
|
||||
if (UsingDecodeOrder()) {
|
||||
host1x.frame_queue.PushDecodeOrder(id, luma, std::move(frame));
|
||||
} else {
|
||||
host1x.frame_queue.PushPresentOrder(id, luma, std::move(frame));
|
||||
}
|
||||
};
|
||||
// Only receive/store visible frames.
|
||||
if (vp9_hidden_frame) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (auto result = decode_api.ReceiveFrame()) {
|
||||
auto& [frame, frame_offsets] = *result;
|
||||
if (!frame) {
|
||||
continue;
|
||||
// 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 (frame_offsets.interlaced) {
|
||||
auto frame_copy = frame;
|
||||
push(frame_offsets.luma, std::move(frame));
|
||||
push(frame_offsets.luma_bottom, std::move(frame_copy));
|
||||
|
||||
if (UsingDecodeOrder()) {
|
||||
host1x.frame_queue.PushDecodeOrder(id, luma_top, std::move(frame));
|
||||
host1x.frame_queue.PushDecodeOrder(id, luma_bottom, std::move(frame_copy));
|
||||
} else {
|
||||
push(frame_offsets.luma, std::move(frame));
|
||||
host1x.frame_queue.PushPresentOrder(id, luma_top, std::move(frame));
|
||||
host1x.frame_queue.PushPresentOrder(id, luma_bottom, std::move(frame_copy));
|
||||
}
|
||||
} else {
|
||||
auto [luma_offset, chroma_offset] = GetProgressiveOffsets();
|
||||
|
||||
if (!frame.get()) {
|
||||
LOG_ERROR(HW_GPU, "Nvdec {} failed to decode progressive frame for luma {:#X}", id,
|
||||
luma_offset);
|
||||
}
|
||||
|
||||
if (UsingDecodeOrder()) {
|
||||
host1x.frame_queue.PushDecodeOrder(id, luma_offset, std::move(frame));
|
||||
} else {
|
||||
host1x.frame_queue.PushPresentOrder(id, luma_offset, std::move(frame));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <queue>
|
||||
|
||||
@@ -47,19 +46,12 @@ 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,10 +52,6 @@ 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,7 +35,6 @@ 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,7 +841,6 @@ 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,10 +4,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cstring>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging.h"
|
||||
#include "common/scope_exit.h"
|
||||
@@ -88,52 +84,6 @@ std::string AVError(int errnum) {
|
||||
return errbuf;
|
||||
}
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -168,24 +118,7 @@ Decoder::Decoder(Tegra::Host1x::NvdecCommon::VideoCodec codec) {
|
||||
return AV_CODEC_ID_NONE;
|
||||
}
|
||||
}();
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
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);
|
||||
}
|
||||
m_codec = avcodec_find_decoder(av_codec);
|
||||
}
|
||||
|
||||
bool Decoder::SupportsDecodingOnDevice(AVPixelFormat* out_pix_fmt, AVHWDeviceType type) const {
|
||||
@@ -281,10 +214,6 @@ 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;
|
||||
#if defined(__ANDROID__)
|
||||
m_codec_context->flags |= AV_CODEC_FLAG_LOW_DELAY;
|
||||
m_codec_context->flags2 |= AV_CODEC_FLAG2_FAST;
|
||||
#endif
|
||||
}
|
||||
|
||||
DecoderContext::~DecoderContext() {
|
||||
@@ -298,25 +227,15 @@ void DecoderContext::InitializeHardwareDecoder(const HardwareContext& context, A
|
||||
m_codec_context->pix_fmt = hw_pix_fmt;
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
bool DecoderContext::OpenContext(const Decoder& decoder) {
|
||||
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;
|
||||
}
|
||||
|
||||
LOG_INFO(HW_GPU, "Using decoder {}", decoder.GetCodec()->name);
|
||||
if (!m_codec_context->hw_device_ctx) {
|
||||
LOG_INFO(HW_GPU, "Using FFmpeg CPU decoder");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -362,13 +281,6 @@ 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) {
|
||||
@@ -376,90 +288,29 @@ bool DecodeApi::Initialize(Tegra::Host1x::NvdecCommon::VideoCodec codec) {
|
||||
m_decoder.emplace(codec);
|
||||
m_decoder_context.emplace(*m_decoder);
|
||||
|
||||
bool is_mediacodec = false;
|
||||
#if defined(__ANDROID__)
|
||||
const std::string_view decoder_name = m_decoder->GetCodec() ? m_decoder->GetCodec()->name : "";
|
||||
is_mediacodec = decoder_name == "h264_mediacodec" ||
|
||||
decoder_name == "vp8_mediacodec" ||
|
||||
decoder_name == "vp9_mediacodec";
|
||||
#endif
|
||||
|
||||
// Enable GPU decoding if requested.
|
||||
if (!is_mediacodec &&
|
||||
Settings::values.nvdec_emulation.GetValue() == Settings::NvdecEmulation::Gpu) {
|
||||
if (Settings::values.nvdec_emulation.GetValue() == Settings::NvdecEmulation::Gpu) {
|
||||
m_hardware_context.emplace();
|
||||
m_hardware_context->InitializeForDecoder(*m_decoder_context, *m_decoder);
|
||||
}
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
m_defer_android_mediacodec_open = is_mediacodec;
|
||||
m_needs_h264_extradata = decoder_name == "h264_mediacodec";
|
||||
if (m_defer_android_mediacodec_open) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Open the decoder context.
|
||||
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, const FrameOffsets& offsets,
|
||||
std::optional<FrameDimensions> dimensions) {
|
||||
if (!m_opened) {
|
||||
std::vector<u8> extradata;
|
||||
#if defined(__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;
|
||||
}
|
||||
if (!offsets.hidden) {
|
||||
m_pending_offsets.push(offsets);
|
||||
}
|
||||
bool DecodeApi::SendPacket(std::span<const u8> packet_data) {
|
||||
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::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};
|
||||
std::shared_ptr<Frame> DecodeApi::ReceiveFrame() {
|
||||
// Receive raw frame from decoder.
|
||||
return m_decoder_context->ReceiveFrame();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -179,7 +179,7 @@ public:
|
||||
~DecoderContext();
|
||||
|
||||
void InitializeHardwareDecoder(const HardwareContext& context, AVPixelFormat hw_pix_fmt);
|
||||
bool OpenContext(const Decoder& decoder, std::span<const u8> extradata = {});
|
||||
bool OpenContext(const Decoder& decoder);
|
||||
bool SendPacket(const Packet& packet);
|
||||
std::shared_ptr<Frame> ReceiveFrame();
|
||||
|
||||
@@ -198,18 +198,6 @@ 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);
|
||||
@@ -225,24 +213,13 @@ public:
|
||||
return m_decoder_context->UsingDecodeOrder();
|
||||
}
|
||||
|
||||
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();
|
||||
bool SendPacket(std::span<const u8> packet_data);
|
||||
std::shared_ptr<Frame> 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,7 +31,6 @@ 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,7 +118,6 @@ 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();
|
||||
@@ -137,17 +136,11 @@ void Vic::Execute() noexcept {
|
||||
break;
|
||||
}
|
||||
Blend(config, slot_config, config.output_surface_config.out_pixel_format);
|
||||
decoded_frame = true;
|
||||
} else {
|
||||
LOG_TRACE(HW_GPU, "Vic {} failed to get frame with offset {:#X}", id, luma_offset);
|
||||
LOG_ERROR(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,7 +628,6 @@ private:
|
||||
|
||||
s32 id;
|
||||
s32 nvdec_id{-1};
|
||||
bool has_decoded_frame{};
|
||||
u32 syncpoint;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// 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
|
||||
|
||||
@@ -43,11 +40,7 @@ layout(binding = BINDING_INPUT_BUFFER, std430) readonly restrict buffer InputBuf
|
||||
uvec4 astc_data[];
|
||||
};
|
||||
|
||||
#ifdef VULKAN
|
||||
layout(binding = BINDING_OUTPUT_IMAGE) uniform writeonly restrict image2DArray dest_image;
|
||||
#else
|
||||
layout(binding = BINDING_OUTPUT_IMAGE, rgba8) uniform writeonly restrict image2DArray dest_image;
|
||||
#endif
|
||||
|
||||
const uint GOB_SIZE_X_SHIFT = 6;
|
||||
const uint GOB_SIZE_Y_SHIFT = 3;
|
||||
@@ -596,163 +589,6 @@ ivec4 BlueContract(int a, int r, int g, int b) {
|
||||
return ivec4(a, (r + b) >> 1, (g + b) >> 1, b);
|
||||
}
|
||||
|
||||
bool IsHDRColorEndpointMode(uint cem) {
|
||||
return cem == 2u || cem == 3u || cem == 7u || cem == 11u || cem == 14u || cem == 15u;
|
||||
}
|
||||
|
||||
// Sign-extends the low nbits of value (a 2's complement field packed into the bottom of an
|
||||
// otherwise-unsigned integer), per C.2.15's HDR endpoint bitfield unpacking.
|
||||
int SignExtend(int value, uint nbits) {
|
||||
int sign_bit = 1 << (nbits - 1u);
|
||||
return (value ^ sign_bit) - sign_bit;
|
||||
}
|
||||
|
||||
// HDR Endpoint Mode 7 (C.2.15): base RGB + scale factor.
|
||||
void DecodeHDREndpointMode7(uint v0, uint v1, uint v2, uint v3, out ivec3 e0, out ivec3 e1) {
|
||||
uint modeval = ((v0 & 0xC0u) >> 6u) | ((v1 & 0x80u) >> 5u) | ((v2 & 0x80u) >> 4u);
|
||||
|
||||
uint majcomp;
|
||||
uint mode;
|
||||
if ((modeval & 0xCu) != 0xCu) {
|
||||
majcomp = modeval >> 2u;
|
||||
mode = modeval & 3u;
|
||||
} else if (modeval != 0xFu) {
|
||||
majcomp = modeval & 3u;
|
||||
mode = 4u;
|
||||
} else {
|
||||
majcomp = 0u;
|
||||
mode = 5u;
|
||||
}
|
||||
|
||||
int red = int(v0 & 0x3Fu);
|
||||
int green = int(v1 & 0x1Fu);
|
||||
int blue = int(v2 & 0x1Fu);
|
||||
int scale = int(v3 & 0x1Fu);
|
||||
|
||||
uint x0 = (v1 >> 6u) & 1u;
|
||||
uint x1 = (v1 >> 5u) & 1u;
|
||||
uint x2 = (v2 >> 6u) & 1u;
|
||||
uint x3 = (v2 >> 5u) & 1u;
|
||||
uint x4 = (v3 >> 7u) & 1u;
|
||||
uint x5 = (v3 >> 6u) & 1u;
|
||||
uint x6 = (v3 >> 5u) & 1u;
|
||||
|
||||
uint ohm = 1u << mode;
|
||||
if ((ohm & 0x30u) != 0u) green |= int(x0 << 6u);
|
||||
if ((ohm & 0x3Au) != 0u) green |= int(x1 << 5u);
|
||||
if ((ohm & 0x30u) != 0u) blue |= int(x2 << 6u);
|
||||
if ((ohm & 0x3Au) != 0u) blue |= int(x3 << 5u);
|
||||
if ((ohm & 0x3Du) != 0u) scale |= int(x6 << 5u);
|
||||
if ((ohm & 0x2Du) != 0u) scale |= int(x5 << 6u);
|
||||
if ((ohm & 0x04u) != 0u) scale |= int(x4 << 7u);
|
||||
if ((ohm & 0x3Bu) != 0u) red |= int(x4 << 6u);
|
||||
if ((ohm & 0x04u) != 0u) red |= int(x3 << 6u);
|
||||
if ((ohm & 0x10u) != 0u) red |= int(x5 << 7u);
|
||||
if ((ohm & 0x0Fu) != 0u) red |= int(x2 << 7u);
|
||||
if ((ohm & 0x05u) != 0u) red |= int(x1 << 8u);
|
||||
if ((ohm & 0x0Au) != 0u) red |= int(x0 << 8u);
|
||||
if ((ohm & 0x05u) != 0u) red |= int(x0 << 9u);
|
||||
if ((ohm & 0x02u) != 0u) red |= int(x6 << 9u);
|
||||
if ((ohm & 0x01u) != 0u) red |= int(x3 << 10u);
|
||||
if ((ohm & 0x02u) != 0u) red |= int(x5 << 10u);
|
||||
|
||||
int shamts[6] = int[](1, 1, 2, 3, 4, 5);
|
||||
int shamt = shamts[mode];
|
||||
red <<= shamt;
|
||||
green <<= shamt;
|
||||
blue <<= shamt;
|
||||
scale <<= shamt;
|
||||
|
||||
if (mode != 5u) {
|
||||
green = red - green;
|
||||
blue = red - blue;
|
||||
}
|
||||
|
||||
if (majcomp == 1u) {
|
||||
int t = red; red = green; green = t;
|
||||
}
|
||||
if (majcomp == 2u) {
|
||||
int t = red; red = blue; blue = t;
|
||||
}
|
||||
|
||||
e1 = ivec3(clamp(red, 0, 0xFFF), clamp(green, 0, 0xFFF), clamp(blue, 0, 0xFFF));
|
||||
e0 = ivec3(clamp(red - scale, 0, 0xFFF), clamp(green - scale, 0, 0xFFF),
|
||||
clamp(blue - scale, 0, 0xFFF));
|
||||
}
|
||||
|
||||
// HDR Endpoint Mode 11 (C.2.15): direct RGB pair. Shared by modes 11, 14 and 15, which all
|
||||
// decode their RGB the same way and only differ in how alpha is filled in.
|
||||
void DecodeHDREndpointMode11(uint v0, uint v1, uint v2, uint v3, uint v4, uint v5, out ivec3 e0,
|
||||
out ivec3 e1) {
|
||||
uint majcomp = ((v4 & 0x80u) >> 7u) | ((v5 & 0x80u) >> 6u);
|
||||
if (majcomp == 3u) {
|
||||
e0 = ivec3(int(v0 << 4u), int(v2 << 4u), int((v4 & 0x7Fu) << 5u));
|
||||
e1 = ivec3(int(v1 << 4u), int(v3 << 4u), int((v5 & 0x7Fu) << 5u));
|
||||
return;
|
||||
}
|
||||
|
||||
uint mode = ((v1 & 0x80u) >> 7u) | ((v2 & 0x80u) >> 6u) | ((v3 & 0x80u) >> 5u);
|
||||
int va = int(v0 | ((v1 & 0x40u) << 2u));
|
||||
int vb0 = int(v2 & 0x3Fu);
|
||||
int vb1 = int(v3 & 0x3Fu);
|
||||
int vc = int(v1 & 0x3Fu);
|
||||
int vd0 = int(v4 & 0x7Fu);
|
||||
int vd1 = int(v5 & 0x7Fu);
|
||||
|
||||
int dbitstab[8] = int[](7, 6, 7, 6, 5, 6, 5, 6);
|
||||
vd0 = SignExtend(vd0, uint(dbitstab[mode]));
|
||||
vd1 = SignExtend(vd1, uint(dbitstab[mode]));
|
||||
|
||||
uint x0 = (v2 >> 6u) & 1u;
|
||||
uint x1 = (v3 >> 6u) & 1u;
|
||||
uint x2 = (v4 >> 6u) & 1u;
|
||||
uint x3 = (v5 >> 6u) & 1u;
|
||||
uint x4 = (v4 >> 5u) & 1u;
|
||||
uint x5 = (v5 >> 5u) & 1u;
|
||||
|
||||
uint ohm = 1u << mode;
|
||||
if ((ohm & 0xA4u) != 0u) va |= int(x0 << 9u);
|
||||
if ((ohm & 0x08u) != 0u) va |= int(x2 << 9u);
|
||||
if ((ohm & 0x50u) != 0u) va |= int(x4 << 9u);
|
||||
if ((ohm & 0x50u) != 0u) va |= int(x5 << 10u);
|
||||
if ((ohm & 0xA0u) != 0u) va |= int(x1 << 10u);
|
||||
if ((ohm & 0xC0u) != 0u) va |= int(x2 << 11u);
|
||||
if ((ohm & 0x04u) != 0u) vc |= int(x1 << 6u);
|
||||
if ((ohm & 0xE8u) != 0u) vc |= int(x3 << 6u);
|
||||
if ((ohm & 0x20u) != 0u) vc |= int(x2 << 7u);
|
||||
if ((ohm & 0x5Bu) != 0u) vb0 |= int(x0 << 6u);
|
||||
if ((ohm & 0x5Bu) != 0u) vb1 |= int(x1 << 6u);
|
||||
if ((ohm & 0x12u) != 0u) vb0 |= int(x2 << 7u);
|
||||
if ((ohm & 0x12u) != 0u) vb1 |= int(x3 << 7u);
|
||||
|
||||
int shamt = (int(mode) >> 1) ^ 3;
|
||||
va <<= shamt;
|
||||
vb0 <<= shamt;
|
||||
vb1 <<= shamt;
|
||||
vc <<= shamt;
|
||||
vd0 <<= shamt;
|
||||
vd1 <<= shamt;
|
||||
|
||||
int r1 = clamp(va, 0, 0xFFF);
|
||||
int g1 = clamp(va - vb0, 0, 0xFFF);
|
||||
int b1 = clamp(va - vb1, 0, 0xFFF);
|
||||
int r0 = clamp(va - vc, 0, 0xFFF);
|
||||
int g0 = clamp(va - vb0 - vc - vd0, 0, 0xFFF);
|
||||
int b0 = clamp(va - vb1 - vc - vd1, 0, 0xFFF);
|
||||
|
||||
if (majcomp == 1u) {
|
||||
int t;
|
||||
t = r0; r0 = g0; g0 = t;
|
||||
t = r1; r1 = g1; g1 = t;
|
||||
} else if (majcomp == 2u) {
|
||||
int t;
|
||||
t = r0; r0 = b0; b0 = t;
|
||||
t = r1; r1 = b1; b1 = t;
|
||||
}
|
||||
e0 = ivec3(r0, g0, b0);
|
||||
e1 = ivec3(r1, g1, b1);
|
||||
}
|
||||
|
||||
void ComputeEndpoints(out uvec4 ep1, out uvec4 ep2, uint color_endpoint_mode, uint color_values[32],
|
||||
inout uint colvals_index) {
|
||||
#define READ_UINT_VALUES(N) \
|
||||
@@ -879,88 +715,8 @@ void ComputeEndpoints(out uvec4 ep1, out uvec4 ep2, uint color_endpoint_mode, ui
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
READ_UINT_VALUES(2)
|
||||
uint y0, y1;
|
||||
if (V[0].y >= V[0].x) {
|
||||
y0 = V[0].x << 4u;
|
||||
y1 = V[0].y << 4u;
|
||||
} else {
|
||||
y0 = (V[0].y << 4u) + 8u;
|
||||
y1 = (V[0].x << 4u) - 8u;
|
||||
}
|
||||
ep1 = uvec4(0x780u, y0, y0, y0);
|
||||
ep2 = uvec4(0x780u, y1, y1, y1);
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
READ_UINT_VALUES(2)
|
||||
uint y0, d;
|
||||
if ((V[0].x & 0x80u) != 0u) {
|
||||
y0 = ((V[0].y & 0xE0u) << 4u) | ((V[0].x & 0x7Fu) << 2u);
|
||||
d = (V[0].y & 0x1Fu) << 2u;
|
||||
} else {
|
||||
y0 = ((V[0].y & 0xF0u) << 4u) | ((V[0].x & 0x7Fu) << 1u);
|
||||
d = (V[0].y & 0x0Fu) << 1u;
|
||||
}
|
||||
const uint y1 = min(y0 + d, 0xFFFu);
|
||||
ep1 = uvec4(0x780u, y0, y0, y0);
|
||||
ep2 = uvec4(0x780u, y1, y1, y1);
|
||||
break;
|
||||
}
|
||||
case 7: {
|
||||
READ_UINT_VALUES(4)
|
||||
ivec3 e0, e1;
|
||||
DecodeHDREndpointMode7(V[0].x, V[0].y, V[0].z, V[0].w, e0, e1);
|
||||
ep1 = uvec4(0x780u, uint(e0.x), uint(e0.y), uint(e0.z));
|
||||
ep2 = uvec4(0x780u, uint(e1.x), uint(e1.y), uint(e1.z));
|
||||
break;
|
||||
}
|
||||
case 11: {
|
||||
READ_UINT_VALUES(6)
|
||||
ivec3 e0, e1;
|
||||
DecodeHDREndpointMode11(V[0].x, V[0].y, V[0].z, V[0].w, V[1].x, V[1].y, e0, e1);
|
||||
ep1 = uvec4(0x780u, uint(e0.x), uint(e0.y), uint(e0.z));
|
||||
ep2 = uvec4(0x780u, uint(e1.x), uint(e1.y), uint(e1.z));
|
||||
break;
|
||||
}
|
||||
case 14: {
|
||||
READ_UINT_VALUES(8)
|
||||
ivec3 e0, e1;
|
||||
DecodeHDREndpointMode11(V[0].x, V[0].y, V[0].z, V[0].w, V[1].x, V[1].y, e0, e1);
|
||||
// Only HDR mode with LDR (8-bit UNORM)-interpreted alpha; left as-is (0-255).
|
||||
ep1 = uvec4(V[1].z, uint(e0.x), uint(e0.y), uint(e0.z));
|
||||
ep2 = uvec4(V[1].w, uint(e1.x), uint(e1.y), uint(e1.z));
|
||||
break;
|
||||
}
|
||||
case 15: {
|
||||
READ_UINT_VALUES(8)
|
||||
ivec3 e0, e1;
|
||||
DecodeHDREndpointMode11(V[0].x, V[0].y, V[0].z, V[0].w, V[1].x, V[1].y, e0, e1);
|
||||
const uint mode = ((V[1].z >> 7u) & 1u) | ((V[1].w >> 6u) & 2u);
|
||||
int a6 = int(V[1].z & 0x7Fu);
|
||||
int a7 = int(V[1].w & 0x7Fu);
|
||||
int alpha0, alpha1;
|
||||
if (mode == 3u) {
|
||||
alpha0 = a6 << 5;
|
||||
alpha1 = a7 << 5;
|
||||
} else {
|
||||
a6 |= (a7 << int(mode + 1u)) & 0x780;
|
||||
a7 &= int(0x3Fu >> mode);
|
||||
a7 ^= int(0x20u >> mode);
|
||||
a7 -= int(0x20u >> mode);
|
||||
a6 <<= int(4u - mode);
|
||||
a7 <<= int(4u - mode);
|
||||
a7 += a6;
|
||||
alpha0 = a6;
|
||||
alpha1 = clamp(a7, 0, 0xFFF);
|
||||
}
|
||||
ep1 = uvec4(uint(alpha0), uint(e0.x), uint(e0.y), uint(e0.z));
|
||||
ep2 = uvec4(uint(alpha1), uint(e1.x), uint(e1.y), uint(e1.z));
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
// Not a valid CEM at all (all 16 values are now handled above).
|
||||
// HDR mode, or more likely a bug computing the color_endpoint_mode
|
||||
ep1 = uvec4(0xFF, 0xFF, 0, 0);
|
||||
ep2 = uvec4(0xFF, 0xFF, 0, 0);
|
||||
break;
|
||||
@@ -1356,51 +1112,13 @@ void DecompressBlock(ivec3 coord) {
|
||||
if (num_partitions > 1) {
|
||||
local_partition = Select2DPartition(partition_index, i, j, num_partitions);
|
||||
}
|
||||
const uint local_cem = color_endpoint_mode[local_partition];
|
||||
const uvec4 C0 = ReplicateByteTo16(endpoints0[local_partition]);
|
||||
const uvec4 C1 = ReplicateByteTo16(endpoints1[local_partition]);
|
||||
const uvec4 weight_vec = GetUnquantizedWeightVector(j, i, size_params, plane_index, dual_plane);
|
||||
|
||||
vec4 p;
|
||||
if (IsHDRColorEndpointMode(local_cem)) {
|
||||
// Endpoints are raw 12-bit pseudo-logarithmic values; shift left 4 bits to
|
||||
// become 16-bit before interpolating, per C.2.19.
|
||||
const uvec4 C0 = endpoints0[local_partition] << 4u;
|
||||
const uvec4 C1 = endpoints1[local_partition] << 4u;
|
||||
const uvec4 C = (C0 * (uvec4(64) - weight_vec) + C1 * weight_vec + uvec4(32)) / 64u;
|
||||
const uvec4 E = (C & uvec4(0xF800u)) >> 11u;
|
||||
const uvec4 M = C & uvec4(0x7FFu);
|
||||
const uvec4 Mt_lo = 3u * M;
|
||||
const uvec4 Mt_mid = 4u * M - 512u;
|
||||
const uvec4 Mt_hi = 5u * M - 2048u;
|
||||
const uvec4 Mt =
|
||||
mix(Mt_lo, mix(Mt_mid, Mt_hi, greaterThanEqual(M, uvec4(1536u))),
|
||||
greaterThanEqual(M, uvec4(512u)));
|
||||
const uvec4 Cf = (E << 10u) + (Mt >> 3u);
|
||||
// +Inf/NaN clamps to the largest finite FP16 value (0x7BFF).
|
||||
const uvec4 half_bits = mix(Cf, uvec4(0x7BFFu), greaterThanEqual(Cf, uvec4(0x7C00u)));
|
||||
p = vec4(unpackHalf2x16(half_bits.x).x, unpackHalf2x16(half_bits.y).x,
|
||||
unpackHalf2x16(half_bits.z).x, unpackHalf2x16(half_bits.w).x);
|
||||
|
||||
// Mode 14 keeps an LDR (8-bit UNORM)-interpreted alpha; component 0 here (A,
|
||||
// see the ep1/ep2 layout used throughout this file).
|
||||
if (local_cem == 14u) {
|
||||
const uint a0 = ReplicateByteTo16(uvec4(endpoints0[local_partition].x)).x;
|
||||
const uint a1 = ReplicateByteTo16(uvec4(endpoints1[local_partition].x)).x;
|
||||
const uint Ca = (a0 * (64u - weight_vec.x) + a1 * weight_vec.x + 32u) / 64u;
|
||||
p.x = float(Ca) / 65535.0f;
|
||||
}
|
||||
} else {
|
||||
const uvec4 C0 = ReplicateByteTo16(endpoints0[local_partition]);
|
||||
const uvec4 C1 = ReplicateByteTo16(endpoints1[local_partition]);
|
||||
const vec4 Cf =
|
||||
vec4((C0 * (uvec4(64) - weight_vec) + C1 * weight_vec + uvec4(32)) / 64);
|
||||
p = Cf / 65535.0f;
|
||||
}
|
||||
|
||||
#ifdef VULKAN
|
||||
const vec4 Cf =
|
||||
vec4((C0 * (uvec4(64) - weight_vec) + C1 * weight_vec + uvec4(32)) / 64);
|
||||
const vec4 p = (Cf / 65535.0f);
|
||||
imageStore(dest_image, coord + ivec3(i, j, 0), p.gbar);
|
||||
#else
|
||||
imageStore(dest_image, coord + ivec3(i, j, 0), clamp(p, 0.0f, 1.0f).gbar);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -8,11 +5,11 @@
|
||||
#extension GL_ARB_shader_stencil_export : require
|
||||
|
||||
layout(binding = 0) uniform sampler2D depth_tex;
|
||||
layout(binding = 1) uniform usampler2D stencil_tex;
|
||||
layout(binding = 1) uniform isampler2D stencil_tex;
|
||||
|
||||
layout(location = 0) in vec2 texcoord;
|
||||
|
||||
void main() {
|
||||
gl_FragDepth = textureLod(depth_tex, texcoord, 0).r;
|
||||
gl_FragStencilRefARB = int(textureLod(stencil_tex, texcoord, 0).r);
|
||||
gl_FragStencilRefARB = textureLod(stencil_tex, texcoord, 0).r;
|
||||
}
|
||||
|
||||
@@ -39,55 +39,6 @@ constexpr std::array POLYGON_OFFSET_ENABLE_LUT = {
|
||||
POLYGON, // Patches
|
||||
};
|
||||
|
||||
constexpr std::array TOPOLOGY_CLASS_REPRESENTATIVE_LUT = {
|
||||
Maxwell::PrimitiveTopology::Points, // Points
|
||||
Maxwell::PrimitiveTopology::Lines, // Lines
|
||||
Maxwell::PrimitiveTopology::LineLoop, // LineLoop
|
||||
Maxwell::PrimitiveTopology::LineStrip, // LineStrip
|
||||
Maxwell::PrimitiveTopology::Triangles, // Triangles
|
||||
Maxwell::PrimitiveTopology::Triangles, // TriangleStrip
|
||||
Maxwell::PrimitiveTopology::Triangles, // TriangleFan
|
||||
Maxwell::PrimitiveTopology::Triangles, // Quads
|
||||
Maxwell::PrimitiveTopology::Triangles, // QuadStrip
|
||||
Maxwell::PrimitiveTopology::Triangles, // Polygon
|
||||
Maxwell::PrimitiveTopology::LinesAdjacency, // LinesAdjacency
|
||||
Maxwell::PrimitiveTopology::LinesAdjacency, // LineStripAdjacency
|
||||
Maxwell::PrimitiveTopology::TrianglesAdjacency, // TrianglesAdjacency
|
||||
Maxwell::PrimitiveTopology::TrianglesAdjacency, // TriangleStripAdjacency
|
||||
Maxwell::PrimitiveTopology::Patches, // Patches
|
||||
};
|
||||
|
||||
bool IsDualSourceBlendFactor(Maxwell::Blend::Factor factor) {
|
||||
using F = Maxwell::Blend::Factor;
|
||||
switch (factor) {
|
||||
case F::Source1Color_D3D:
|
||||
case F::OneMinusSource1Color_D3D:
|
||||
case F::Source1Alpha_D3D:
|
||||
case F::OneMinusSource1Alpha_D3D:
|
||||
case F::Source1Color_GL:
|
||||
case F::OneMinusSource1Color_GL:
|
||||
case F::Source1Alpha_GL:
|
||||
case F::OneMinusSource1Alpha_GL:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ComputeAttachment0DualSourceBlend(const Maxwell& regs) {
|
||||
if (!regs.blend.enable[0]) {
|
||||
return false;
|
||||
}
|
||||
const auto uses_dual_source = [](const auto& blend) {
|
||||
return IsDualSourceBlendFactor(blend.color_source) ||
|
||||
IsDualSourceBlendFactor(blend.color_dest) ||
|
||||
IsDualSourceBlendFactor(blend.alpha_source) ||
|
||||
IsDualSourceBlendFactor(blend.alpha_dest);
|
||||
};
|
||||
return regs.blend_per_target_enabled ? uses_dual_source(regs.blend_per_target[0])
|
||||
: uses_dual_source(regs.blend);
|
||||
}
|
||||
|
||||
void RefreshXfbState(VideoCommon::TransformFeedbackState& state, const Maxwell& regs) {
|
||||
std::ranges::transform(regs.transform_feedback.controls, state.layouts.begin(),
|
||||
[](const auto& layout) {
|
||||
@@ -111,7 +62,6 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe
|
||||
extended_dynamic_state_2_logic_op.Assign(features.has_extended_dynamic_state_2_logic_op ? 1 : 0);
|
||||
extended_dynamic_state_3_blend.Assign(features.has_extended_dynamic_state_3_blend ? 1 : 0);
|
||||
extended_dynamic_state_3_enables.Assign(features.has_extended_dynamic_state_3_enables ? 1 : 0);
|
||||
color_write_enable_dynamic.Assign(features.has_color_write_enable ? 1 : 0);
|
||||
dynamic_vertex_input.Assign(features.has_dynamic_vertex_input ? 1 : 0);
|
||||
xfb_enabled.Assign(regs.transform_feedback_enabled != 0);
|
||||
ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0);
|
||||
@@ -121,13 +71,8 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe
|
||||
tessellation_clockwise.Assign(regs.tessellation.params.output_primitives.Value() ==
|
||||
Maxwell::Tessellation::OutputPrimitives::Triangles_CW);
|
||||
patch_control_points_minus_one.Assign(regs.patch_vertices - 1);
|
||||
const bool can_collapse_topology_class =
|
||||
features.has_extended_dynamic_state && features.has_extended_dynamic_state_2;
|
||||
topology.Assign(can_collapse_topology_class
|
||||
? TOPOLOGY_CLASS_REPRESENTATIVE_LUT[static_cast<size_t>(topology_)]
|
||||
: topology_);
|
||||
topology.Assign(topology_);
|
||||
msaa_mode.Assign(regs.anti_alias_samples_mode);
|
||||
attachment0_dual_source_blend.Assign(ComputeAttachment0DualSourceBlend(regs) ? 1 : 0);
|
||||
|
||||
raw2 = 0;
|
||||
|
||||
@@ -242,15 +187,6 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe
|
||||
maxwell3d.dirty.flags[Dirty::Blending] = false;
|
||||
for (size_t index = 0; index < attachments.size(); ++index) {
|
||||
attachments[index].Refresh(regs, index);
|
||||
auto& attachment = attachments[index];
|
||||
if (color_write_enable_dynamic && attachment.mask_r == 0 &&
|
||||
attachment.mask_g == 0 && attachment.mask_b == 0 &&
|
||||
attachment.mask_a == 0) {
|
||||
attachment.mask_r.Assign(1);
|
||||
attachment.mask_g.Assign(1);
|
||||
attachment.mask_b.Assign(1);
|
||||
attachment.mask_a.Assign(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,6 @@ struct DynamicFeatures {
|
||||
bool has_dynamic_state3_logic_op_enable;
|
||||
bool has_dynamic_state3_line_stipple_enable;
|
||||
bool has_dynamic_vertex_input;
|
||||
bool has_color_write_enable;
|
||||
bool has_provoking_vertex;
|
||||
bool has_provoking_vertex_first_mode;
|
||||
bool has_provoking_vertex_last_mode;
|
||||
@@ -209,9 +208,6 @@ struct FixedPipelineState {
|
||||
BitField<12, 2, u32> tessellation_spacing;
|
||||
BitField<14, 1, u32> tessellation_clockwise;
|
||||
BitField<15, 5, u32> patch_control_points_minus_one;
|
||||
BitField<20, 1, u32> color_write_enable_dynamic;
|
||||
|
||||
BitField<21, 1, u32> attachment0_dual_source_blend;
|
||||
|
||||
BitField<24, 4, Maxwell::PrimitiveTopology> topology;
|
||||
BitField<28, 4, Tegra::Texture::MsaaMode> msaa_mode;
|
||||
|
||||
@@ -223,10 +223,6 @@ FormatInfo SurfaceFormat(const Device& device, FormatType format_type, bool with
|
||||
SURFACE_FORMAT_ELEM(VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK, 0, ETC2_RGB_SRGB) \
|
||||
SURFACE_FORMAT_ELEM(VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK, 0, ETC2_RGBA_SRGB) \
|
||||
SURFACE_FORMAT_ELEM(VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK, 0, ETC2_RGB_PTA_SRGB) \
|
||||
SURFACE_FORMAT_ELEM(VK_FORMAT_EAC_R11_UNORM_BLOCK, 0, EAC_R11_UNORM) \
|
||||
SURFACE_FORMAT_ELEM(VK_FORMAT_EAC_R11_SNORM_BLOCK, 0, EAC_R11_SNORM) \
|
||||
SURFACE_FORMAT_ELEM(VK_FORMAT_EAC_R11G11_UNORM_BLOCK, 0, EAC_R11G11_UNORM) \
|
||||
SURFACE_FORMAT_ELEM(VK_FORMAT_EAC_R11G11_SNORM_BLOCK, 0, EAC_R11G11_SNORM) \
|
||||
/* Depth formats */ \
|
||||
SURFACE_FORMAT_ELEM(VK_FORMAT_D32_SFLOAT, usage_attachable, D32_FLOAT) \
|
||||
SURFACE_FORMAT_ELEM(VK_FORMAT_D16_UNORM, usage_attachable, D16_UNORM) \
|
||||
@@ -282,17 +278,7 @@ FormatInfo SurfaceFormat(const Device& device, FormatType format_type, bool with
|
||||
}
|
||||
} else if (!device.IsOptimalEtc2Supported() && VideoCore::Surface::IsPixelFormatETC2(pixel_format)) {
|
||||
// Transcode on hardware that doesn't support ETC2 natively
|
||||
if (pixel_format == PixelFormat::EAC_R11_SNORM) {
|
||||
tuple.format = VK_FORMAT_R8_SNORM;
|
||||
} else if (pixel_format == PixelFormat::EAC_R11_UNORM) {
|
||||
tuple.format = VK_FORMAT_R8_UNORM;
|
||||
} else if (pixel_format == PixelFormat::EAC_R11G11_SNORM) {
|
||||
tuple.format = VK_FORMAT_R8G8_SNORM;
|
||||
} else if (pixel_format == PixelFormat::EAC_R11G11_UNORM) {
|
||||
tuple.format = VK_FORMAT_R8G8_UNORM;
|
||||
} else {
|
||||
tuple.format = is_srgb ? VK_FORMAT_A8B8G8R8_SRGB_PACK32 : VK_FORMAT_A8B8G8R8_UNORM_PACK32;
|
||||
}
|
||||
tuple.format = is_srgb ? VK_FORMAT_A8B8G8R8_SRGB_PACK32 : VK_FORMAT_A8B8G8R8_UNORM_PACK32;
|
||||
}
|
||||
bool const attachable = (tuple.usage & usage_attachable) != 0;
|
||||
bool const storage = (tuple.usage & usage_storage) != 0;
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <optional>
|
||||
|
||||
#include <boost/container/small_vector.hpp>
|
||||
|
||||
@@ -16,7 +15,6 @@
|
||||
#include "shader_recompiler/shader_info.h"
|
||||
#include "video_core/renderer_vulkan/vk_texture_cache.h"
|
||||
#include "video_core/renderer_vulkan/vk_update_descriptor.h"
|
||||
#include "video_core/surface.h"
|
||||
#include "video_core/texture_cache/types.h"
|
||||
#include "video_core/vulkan_common/vulkan_device.h"
|
||||
|
||||
@@ -24,29 +22,6 @@ namespace Vulkan {
|
||||
|
||||
using Shader::Backend::SPIRV::NUM_TEXTURE_AND_IMAGE_SCALING_WORDS;
|
||||
|
||||
[[nodiscard]] inline std::optional<PixelFormat> PixelFormatFromImageFormat(
|
||||
Shader::ImageFormat format) {
|
||||
switch (format) {
|
||||
case Shader::ImageFormat::Typeless:
|
||||
return std::nullopt;
|
||||
case Shader::ImageFormat::R8_UINT:
|
||||
return PixelFormat::R8_UINT;
|
||||
case Shader::ImageFormat::R8_SINT:
|
||||
return PixelFormat::R8_SINT;
|
||||
case Shader::ImageFormat::R16_UINT:
|
||||
return PixelFormat::R16_UINT;
|
||||
case Shader::ImageFormat::R16_SINT:
|
||||
return PixelFormat::R16_SINT;
|
||||
case Shader::ImageFormat::R32_UINT:
|
||||
return PixelFormat::R32_UINT;
|
||||
case Shader::ImageFormat::R32G32_UINT:
|
||||
return PixelFormat::R32G32_UINT;
|
||||
case Shader::ImageFormat::R32G32B32A32_UINT:
|
||||
return PixelFormat::R32G32B32A32_UINT;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline u32 NumDescriptorEntries(const Shader::Info& info) {
|
||||
return Shader::NumDescriptors(info.constant_buffer_descriptors) +
|
||||
Shader::NumDescriptors(info.storage_buffers_descriptors) +
|
||||
@@ -236,12 +211,8 @@ inline void PushImageDescriptors(TextureCache& texture_cache,
|
||||
const Sampler& sampler{texture_cache.GetSampler(sampler_id)};
|
||||
const bool use_fallback_sampler{sampler.HasAddedAnisotropy() &&
|
||||
!image_view.SupportsAnisotropy()};
|
||||
VkSampler vk_sampler{use_fallback_sampler ? sampler.HandleWithDefaultAnisotropy()
|
||||
: sampler.Handle()};
|
||||
if (sampler.HasLinearFiltering() &&
|
||||
VideoCore::Surface::IsPixelFormatInteger(image_view.format)) {
|
||||
vk_sampler = sampler.HandleWithNearestFilter();
|
||||
}
|
||||
const VkSampler vk_sampler{use_fallback_sampler ? sampler.HandleWithDefaultAnisotropy()
|
||||
: sampler.Handle()};
|
||||
guest_descriptor_queue.AddSampledImage(vk_image_view, vk_sampler);
|
||||
const bool element_rescaled{texture_cache.IsRescaling(image_view)};
|
||||
is_rescaled |= element_rescaled;
|
||||
|
||||
@@ -84,7 +84,7 @@ vk::Buffer CreateBuffer(const Device& device, const MemoryAllocator& memory_allo
|
||||
} // Anonymous namespace
|
||||
|
||||
Buffer::Buffer(BufferCacheRuntime& runtime, VideoCommon::NullBufferParams null_params)
|
||||
: VideoCommon::BufferBase(null_params), scheduler{&runtime.scheduler}, tracker{4096} {
|
||||
: VideoCommon::BufferBase(null_params), tracker{4096} {
|
||||
if (runtime.device.HasNullDescriptor()) {
|
||||
return;
|
||||
}
|
||||
@@ -95,18 +95,12 @@ Buffer::Buffer(BufferCacheRuntime& runtime, VideoCommon::NullBufferParams null_p
|
||||
|
||||
Buffer::Buffer(BufferCacheRuntime& runtime, DAddr cpu_addr_, u64 size_bytes_)
|
||||
: VideoCommon::BufferBase(cpu_addr_, size_bytes_), device{&runtime.device},
|
||||
scheduler{&runtime.scheduler},
|
||||
buffer{CreateBuffer(*device, runtime.memory_allocator, SizeBytes())}, tracker{SizeBytes()} {
|
||||
if (runtime.device.HasDebuggingToolAttached()) {
|
||||
buffer.SetObjectNameEXT(fmt::format("Buffer 0x{:x}", CpuAddr()).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void Buffer::MarkUsage(u64 offset, u64 size) noexcept {
|
||||
tracker.Track(offset, size);
|
||||
last_usage_tick = scheduler->CurrentTick();
|
||||
}
|
||||
|
||||
VkBufferView Buffer::View(u32 offset, u32 size, VideoCore::Surface::PixelFormat format) {
|
||||
if (!device) {
|
||||
// Null buffer supported, return a null descriptor
|
||||
@@ -390,9 +384,7 @@ u32 BufferCacheRuntime::GetStorageBufferAlignment() const {
|
||||
|
||||
void BufferCacheRuntime::TickFrame(Common::SlotVector<Buffer>& slot_buffers) noexcept {
|
||||
for (auto it = slot_buffers.begin(); it != slot_buffers.end(); it++) {
|
||||
if (scheduler.IsFree(it->LastUsageTick())) {
|
||||
it->ResetUsageTracking();
|
||||
}
|
||||
it->ResetUsageTracking();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -564,7 +556,7 @@ void BufferCacheRuntime::BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset
|
||||
if (index >= device.GetMaxVertexInputBindings()) {
|
||||
return;
|
||||
}
|
||||
if (device.IsExtExtendedDynamicStateSupported() && !vertex_input_dynamic_state_active) {
|
||||
if (device.IsExtExtendedDynamicStateSupported()) {
|
||||
scheduler.Record([index, buffer, offset, size, stride](vk::CommandBuffer cmdbuf) {
|
||||
const VkDeviceSize vk_offset = buffer != VK_NULL_HANDLE ? offset : 0;
|
||||
const VkDeviceSize vk_size = buffer != VK_NULL_HANDLE ? size : VK_WHOLE_SIZE;
|
||||
@@ -604,7 +596,7 @@ void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bi
|
||||
if (binding_count == 0) {
|
||||
return;
|
||||
}
|
||||
if (device.IsExtExtendedDynamicStateSupported() && !vertex_input_dynamic_state_active) {
|
||||
if (device.IsExtExtendedDynamicStateSupported()) {
|
||||
scheduler.Record([bindings_ = std::move(bindings), buffer_handles_ = std::move(buffer_handles), binding_count](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.BindVertexBuffers2EXT(bindings_.min_index, binding_count, buffer_handles_.data(), bindings_.offsets.data(), bindings_.sizes.data(), bindings_.strides.data());
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
@@ -43,16 +43,14 @@ public:
|
||||
return tracker.IsUsed(offset, size);
|
||||
}
|
||||
|
||||
void MarkUsage(u64 offset, u64 size) noexcept;
|
||||
void MarkUsage(u64 offset, u64 size) noexcept {
|
||||
tracker.Track(offset, size);
|
||||
}
|
||||
|
||||
void ResetUsageTracking() noexcept {
|
||||
tracker.Reset();
|
||||
}
|
||||
|
||||
[[nodiscard]] u64 LastUsageTick() const noexcept {
|
||||
return last_usage_tick;
|
||||
}
|
||||
|
||||
operator VkBuffer() const noexcept {
|
||||
return *buffer;
|
||||
}
|
||||
@@ -66,11 +64,9 @@ private:
|
||||
};
|
||||
|
||||
const Device* device{};
|
||||
Scheduler* scheduler{};
|
||||
vk::Buffer buffer;
|
||||
std::vector<BufferView> views;
|
||||
VideoCommon::UsageTracker tracker;
|
||||
u64 last_usage_tick{};
|
||||
bool is_null{};
|
||||
};
|
||||
|
||||
@@ -131,10 +127,6 @@ public:
|
||||
|
||||
void BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings);
|
||||
|
||||
void SetVertexInputDynamicState(bool is_active) {
|
||||
vertex_input_dynamic_state_active = is_active;
|
||||
}
|
||||
|
||||
void BindTransformFeedbackBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size);
|
||||
|
||||
void BindTransformFeedbackBuffers(VideoCommon::HostBindings<Buffer>& bindings);
|
||||
@@ -171,11 +163,7 @@ public:
|
||||
|
||||
private:
|
||||
void BindBuffer(VkBuffer buffer, u32 offset, u32 size) {
|
||||
if (buffer == VK_NULL_HANDLE) {
|
||||
guest_descriptor_queue.AddBuffer(buffer, 0, VK_WHOLE_SIZE);
|
||||
} else {
|
||||
guest_descriptor_queue.AddBuffer(buffer, offset, size);
|
||||
}
|
||||
guest_descriptor_queue.AddBuffer(buffer, offset, size);
|
||||
}
|
||||
|
||||
void ReserveNullBuffer();
|
||||
@@ -197,8 +185,6 @@ private:
|
||||
|
||||
bool limit_dynamic_storage_buffers = false;
|
||||
u32 max_dynamic_storage_buffers = (std::numeric_limits<u32>::max)();
|
||||
|
||||
bool vertex_input_dynamic_state_active = false;
|
||||
};
|
||||
|
||||
struct BufferCacheParams {
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
#include "video_core/host_shaders/vulkan_uint8_comp_spv.h"
|
||||
#include "video_core/host_shaders/block_linear_unswizzle_3d_bcn_comp_spv.h"
|
||||
#include "video_core/renderer_vulkan/vk_compute_pass.h"
|
||||
#include "video_core/surface.h"
|
||||
#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
|
||||
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
||||
#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
|
||||
@@ -746,8 +745,10 @@ void BlockLinearUnswizzle3DPass::Unswizzle(
|
||||
|
||||
const u32 MAX_BATCH_SLICES = (std::min)(z_count, image.info.size.depth);
|
||||
|
||||
// Allocate or grow to cover this batch's slice count
|
||||
image.AllocateComputeUnswizzleBuffer(MAX_BATCH_SLICES);
|
||||
if (!image.has_compute_unswizzle_buffer) {
|
||||
// Allocate exactly what this batch needs
|
||||
image.AllocateComputeUnswizzleBuffer(MAX_BATCH_SLICES);
|
||||
}
|
||||
|
||||
ASSERT(swizzles.size() == 1);
|
||||
const auto& sw = swizzles[0];
|
||||
|
||||
@@ -193,14 +193,8 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute,
|
||||
is_written = desc.is_written;
|
||||
}
|
||||
ImageView& image_view = texture_cache.GetImageView(views[index].id);
|
||||
PixelFormat format{image_view.format};
|
||||
if constexpr (is_image) {
|
||||
if (const auto explicit_format{PixelFormatFromImageFormat(desc.format)}) {
|
||||
format = *explicit_format;
|
||||
}
|
||||
}
|
||||
buffer_cache.BindComputeTextureBuffer(index, image_view.GpuAddr(),
|
||||
image_view.BufferSize(), format,
|
||||
image_view.BufferSize(), image_view.format,
|
||||
is_written, is_image);
|
||||
++index;
|
||||
}
|
||||
|
||||
@@ -426,14 +426,8 @@ bool GraphicsPipeline::ConfigureImpl(bool is_indexed) {
|
||||
is_written = desc.is_written;
|
||||
}
|
||||
ImageView& image_view{texture_cache.GetImageView(texture_buffer_it->id)};
|
||||
PixelFormat format{image_view.format};
|
||||
if constexpr (is_image) {
|
||||
if (const auto explicit_format{PixelFormatFromImageFormat(desc.format)}) {
|
||||
format = *explicit_format;
|
||||
}
|
||||
}
|
||||
buffer_cache.BindGraphicsTextureBuffer(stage, index, image_view.GpuAddr(),
|
||||
image_view.BufferSize(), format,
|
||||
image_view.BufferSize(), image_view.format,
|
||||
is_written, is_image);
|
||||
++index;
|
||||
++texture_buffer_it;
|
||||
@@ -478,7 +472,6 @@ bool GraphicsPipeline::ConfigureImpl(bool is_indexed) {
|
||||
}
|
||||
|
||||
buffer_cache.UpdateGraphicsBuffers(is_indexed);
|
||||
buffer_cache.runtime.SetVertexInputDynamicState(HasDynamicVertexInput());
|
||||
buffer_cache.BindHostGeometryBuffers(is_indexed);
|
||||
|
||||
guest_descriptor_queue.Acquire(scheduler, num_descriptor_entries);
|
||||
@@ -872,17 +865,18 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
|
||||
VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE_EXT,
|
||||
VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE_EXT,
|
||||
VK_DYNAMIC_STATE_STENCIL_OP_EXT,
|
||||
VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY_EXT,
|
||||
};
|
||||
dynamic_states.insert(dynamic_states.end(), extended.begin(), extended.end());
|
||||
|
||||
// VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT
|
||||
// VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT is part of EDS1
|
||||
// Only use it if VIDS is not active (VIDS replaces it with full vertex input control)
|
||||
if (!key.state.dynamic_vertex_input) {
|
||||
dynamic_states.push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT);
|
||||
}
|
||||
}
|
||||
|
||||
// VK_DYNAMIC_STATE_VERTEX_INPUT_EXT
|
||||
// VK_DYNAMIC_STATE_VERTEX_INPUT_EXT (VIDS) - Independent from EDS
|
||||
// Provides full dynamic vertex input control, replaces VERTEX_INPUT_BINDING_STRIDE
|
||||
if (key.state.dynamic_vertex_input) {
|
||||
dynamic_states.push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_EXT);
|
||||
}
|
||||
@@ -912,11 +906,6 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
|
||||
dynamic_states.insert(dynamic_states.end(), extended3.begin(), extended3.end());
|
||||
}
|
||||
|
||||
// VK_EXT_color_write_enable fallback for fully on/off render targets when EDS3 blending is not available.
|
||||
if (!key.state.extended_dynamic_state_3_blend && key.state.color_write_enable_dynamic) {
|
||||
dynamic_states.push_back(VK_DYNAMIC_STATE_COLOR_WRITE_ENABLE_EXT);
|
||||
}
|
||||
|
||||
// EDS3 - Enables (composite: per-feature)
|
||||
if (key.state.extended_dynamic_state_3_enables) {
|
||||
if (device.SupportsDynamicState3DepthClampEnable()) {
|
||||
|
||||
@@ -130,70 +130,6 @@ VkResult MasterSemaphore::SubmitQueueTimeline(vk::CommandBuffer& cmdbuf,
|
||||
VkSemaphore wait_semaphore, u64 host_tick) {
|
||||
const VkSemaphore timeline_semaphore = *semaphore;
|
||||
|
||||
if (device.HasSynchronization2()) {
|
||||
const std::array<VkCommandBufferSubmitInfo, 2> cmdbuffer_infos{{
|
||||
{
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO,
|
||||
.pNext = nullptr,
|
||||
.commandBuffer = *upload_cmdbuf,
|
||||
.deviceMask = 0,
|
||||
},
|
||||
{
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO,
|
||||
.pNext = nullptr,
|
||||
.commandBuffer = *cmdbuf,
|
||||
.deviceMask = 0,
|
||||
},
|
||||
}};
|
||||
|
||||
std::array<VkSemaphoreSubmitInfo, 2> signal_infos{{
|
||||
{
|
||||
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO,
|
||||
.pNext = nullptr,
|
||||
.semaphore = timeline_semaphore,
|
||||
.value = host_tick,
|
||||
.stageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
|
||||
.deviceIndex = 0,
|
||||
},
|
||||
{},
|
||||
}};
|
||||
u32 num_signal_semaphores = 1;
|
||||
if (signal_semaphore) {
|
||||
signal_infos[1] = VkSemaphoreSubmitInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO,
|
||||
.pNext = nullptr,
|
||||
.semaphore = signal_semaphore,
|
||||
.value = 0,
|
||||
.stageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
|
||||
.deviceIndex = 0,
|
||||
};
|
||||
num_signal_semaphores = 2;
|
||||
}
|
||||
|
||||
const u32 num_wait_semaphores = wait_semaphore ? 1 : 0;
|
||||
const VkSemaphoreSubmitInfo wait_info{
|
||||
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO,
|
||||
.pNext = nullptr,
|
||||
.semaphore = wait_semaphore,
|
||||
.value = 0,
|
||||
.stageMask = static_cast<VkPipelineStageFlags2>(wait_stage_mask),
|
||||
.deviceIndex = 0,
|
||||
};
|
||||
|
||||
const VkSubmitInfo2 submit_info2{
|
||||
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.waitSemaphoreInfoCount = num_wait_semaphores,
|
||||
.pWaitSemaphoreInfos = num_wait_semaphores ? &wait_info : nullptr,
|
||||
.commandBufferInfoCount = static_cast<u32>(cmdbuffer_infos.size()),
|
||||
.pCommandBufferInfos = cmdbuffer_infos.data(),
|
||||
.signalSemaphoreInfoCount = num_signal_semaphores,
|
||||
.pSignalSemaphoreInfos = signal_infos.data(),
|
||||
};
|
||||
return device.GetGraphicsQueue().Submit2(submit_info2);
|
||||
}
|
||||
|
||||
const u32 num_signal_semaphores = signal_semaphore ? 2 : 1;
|
||||
const std::array signal_values{host_tick, u64(0)};
|
||||
const std::array signal_semaphores{timeline_semaphore, signal_semaphore};
|
||||
@@ -236,66 +172,6 @@ VkResult MasterSemaphore::SubmitQueueFence(vk::CommandBuffer& cmdbuf,
|
||||
vk::CommandBuffer& upload_cmdbuf,
|
||||
VkSemaphore signal_semaphore, VkSemaphore wait_semaphore,
|
||||
u64 host_tick) {
|
||||
if (device.HasSynchronization2()) {
|
||||
const std::array<VkCommandBufferSubmitInfo, 2> cmdbuffer_infos{{
|
||||
{
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO,
|
||||
.pNext = nullptr,
|
||||
.commandBuffer = *upload_cmdbuf,
|
||||
.deviceMask = 0,
|
||||
},
|
||||
{
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO,
|
||||
.pNext = nullptr,
|
||||
.commandBuffer = *cmdbuf,
|
||||
.deviceMask = 0,
|
||||
},
|
||||
}};
|
||||
|
||||
const u32 num_signal_semaphores = signal_semaphore ? 1 : 0;
|
||||
const VkSemaphoreSubmitInfo signal_info{
|
||||
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO,
|
||||
.pNext = nullptr,
|
||||
.semaphore = signal_semaphore,
|
||||
.value = 0,
|
||||
.stageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
|
||||
.deviceIndex = 0,
|
||||
};
|
||||
|
||||
const u32 num_wait_semaphores = wait_semaphore ? 1 : 0;
|
||||
const VkSemaphoreSubmitInfo wait_info{
|
||||
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO,
|
||||
.pNext = nullptr,
|
||||
.semaphore = wait_semaphore,
|
||||
.value = 0,
|
||||
.stageMask = static_cast<VkPipelineStageFlags2>(wait_stage_mask),
|
||||
.deviceIndex = 0,
|
||||
};
|
||||
|
||||
const VkSubmitInfo2 submit_info2{
|
||||
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.waitSemaphoreInfoCount = num_wait_semaphores,
|
||||
.pWaitSemaphoreInfos = num_wait_semaphores ? &wait_info : nullptr,
|
||||
.commandBufferInfoCount = static_cast<u32>(cmdbuffer_infos.size()),
|
||||
.pCommandBufferInfos = cmdbuffer_infos.data(),
|
||||
.signalSemaphoreInfoCount = num_signal_semaphores,
|
||||
.pSignalSemaphoreInfos = num_signal_semaphores ? &signal_info : nullptr,
|
||||
};
|
||||
|
||||
auto fence = GetFreeFence();
|
||||
auto result = device.GetGraphicsQueue().Submit2(submit_info2, *fence);
|
||||
|
||||
if (result == VK_SUCCESS) {
|
||||
std::scoped_lock lock{wait_mutex};
|
||||
wait_queue.emplace(host_tick, std::move(fence));
|
||||
wait_cv.notify_one();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const u32 num_signal_semaphores = signal_semaphore ? 1 : 0;
|
||||
const u32 num_wait_semaphores = wait_semaphore ? 1 : 0;
|
||||
|
||||
|
||||
@@ -246,7 +246,32 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> program
|
||||
key.state.UnpackComparisonOp(key.state.alpha_test_func.Value()));
|
||||
info.alpha_test_reference = std::bit_cast<float>(key.state.alpha_test_ref);
|
||||
|
||||
info.dual_source_blend = key.state.attachment0_dual_source_blend != 0;
|
||||
// Check for dual source blending
|
||||
const auto& blend0 = key.state.attachments[0];
|
||||
if (blend0.enable != 0) {
|
||||
using F = Maxwell::Blend::Factor;
|
||||
const auto src_rgb = blend0.SourceRGBFactor();
|
||||
const auto dst_rgb = blend0.DestRGBFactor();
|
||||
const auto src_a = blend0.SourceAlphaFactor();
|
||||
const auto dst_a = blend0.DestAlphaFactor();
|
||||
info.dual_source_blend =
|
||||
src_rgb == F::Source1Color_D3D || src_rgb == F::OneMinusSource1Color_D3D ||
|
||||
src_rgb == F::Source1Alpha_D3D || src_rgb == F::OneMinusSource1Alpha_D3D ||
|
||||
src_rgb == F::Source1Color_GL || src_rgb == F::OneMinusSource1Color_GL ||
|
||||
src_rgb == F::Source1Alpha_GL || src_rgb == F::OneMinusSource1Alpha_GL ||
|
||||
dst_rgb == F::Source1Color_D3D || dst_rgb == F::OneMinusSource1Color_D3D ||
|
||||
dst_rgb == F::Source1Alpha_D3D || dst_rgb == F::OneMinusSource1Alpha_D3D ||
|
||||
dst_rgb == F::Source1Color_GL || dst_rgb == F::OneMinusSource1Color_GL ||
|
||||
dst_rgb == F::Source1Alpha_GL || dst_rgb == F::OneMinusSource1Alpha_GL ||
|
||||
src_a == F::Source1Color_D3D || src_a == F::OneMinusSource1Color_D3D ||
|
||||
src_a == F::Source1Alpha_D3D || src_a == F::OneMinusSource1Alpha_D3D ||
|
||||
src_a == F::Source1Color_GL || src_a == F::OneMinusSource1Color_GL ||
|
||||
src_a == F::Source1Alpha_GL || src_a == F::OneMinusSource1Alpha_GL ||
|
||||
dst_a == F::Source1Color_D3D || dst_a == F::OneMinusSource1Color_D3D ||
|
||||
dst_a == F::Source1Alpha_D3D || dst_a == F::OneMinusSource1Alpha_D3D ||
|
||||
dst_a == F::Source1Color_GL || dst_a == F::OneMinusSource1Color_GL ||
|
||||
dst_a == F::Source1Alpha_GL || dst_a == F::OneMinusSource1Alpha_GL;
|
||||
}
|
||||
|
||||
if (device.IsMoltenVK()) {
|
||||
for (size_t i = 0; i < 8; ++i) {
|
||||
@@ -353,30 +378,12 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
|
||||
serialization_thread(1, "VkPipelineSerialization") {
|
||||
const auto& float_control{device.FloatControlProperties()};
|
||||
const VkDriverId driver_id{device.GetDriverID()};
|
||||
const VkShaderStageFlags subgroup_stages{device.GetSubgroupSupportedStages()};
|
||||
const auto subgroup_stage_bit{[subgroup_stages](VkShaderStageFlags flag, Shader::Stage stage) {
|
||||
return (subgroup_stages & flag) != 0 ? (1u << static_cast<u32>(stage)) : 0u;
|
||||
}};
|
||||
const u32 supported_subgroup_stages{
|
||||
subgroup_stage_bit(VK_SHADER_STAGE_VERTEX_BIT, Shader::Stage::VertexA) |
|
||||
subgroup_stage_bit(VK_SHADER_STAGE_VERTEX_BIT, Shader::Stage::VertexB) |
|
||||
subgroup_stage_bit(VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,
|
||||
Shader::Stage::TessellationControl) |
|
||||
subgroup_stage_bit(VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT,
|
||||
Shader::Stage::TessellationEval) |
|
||||
subgroup_stage_bit(VK_SHADER_STAGE_GEOMETRY_BIT, Shader::Stage::Geometry) |
|
||||
subgroup_stage_bit(VK_SHADER_STAGE_FRAGMENT_BIT, Shader::Stage::Fragment) |
|
||||
subgroup_stage_bit(VK_SHADER_STAGE_COMPUTE_BIT, Shader::Stage::Compute)};
|
||||
profile = Shader::Profile{
|
||||
.supported_spirv = device.SupportedSpirvVersion(),
|
||||
.unified_descriptor_binding = true,
|
||||
.support_descriptor_aliasing = device.IsDescriptorAliasingSupported(),
|
||||
.support_int8 = device.IsInt8Supported(),
|
||||
.support_uniform_and_storage_buffer_8bit =
|
||||
device.IsUniformAndStorageBuffer8BitAccessSupported(),
|
||||
.support_int16 = device.IsShaderInt16Supported(),
|
||||
.support_uniform_and_storage_buffer_16bit =
|
||||
device.IsUniformAndStorageBuffer16BitAccessSupported(),
|
||||
.support_int64 = device.IsShaderInt64Supported(),
|
||||
.support_vertex_instance_id = false,
|
||||
.support_float_controls = device.IsKhrShaderFloatControlsSupported(),
|
||||
@@ -396,7 +403,6 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
|
||||
float_control.shaderSignedZeroInfNanPreserveFloat64 != VK_FALSE,
|
||||
.support_explicit_workgroup_layout = device.IsKhrWorkgroupMemoryExplicitLayoutSupported(),
|
||||
.support_vote = device.IsSubgroupFeatureSupported(VK_SUBGROUP_FEATURE_VOTE_BIT),
|
||||
.supported_subgroup_stages = supported_subgroup_stages,
|
||||
.support_viewport_index_layer_non_geometry =
|
||||
device.IsExtShaderViewportIndexLayerSupported(),
|
||||
.support_viewport_mask = device.IsNvViewportArray2Supported(),
|
||||
@@ -494,8 +500,6 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
|
||||
device.IsExtExtendedDynamicState3BlendingSupported();
|
||||
dynamic_features.has_extended_dynamic_state_3_enables =
|
||||
device.IsExtExtendedDynamicState3EnablesSupported();
|
||||
dynamic_features.has_color_write_enable =
|
||||
device.IsExtColorWriteEnableSupported();
|
||||
dynamic_features.has_dynamic_state3_depth_clamp_enable =
|
||||
dynamic_features.has_extended_dynamic_state_3_enables &&
|
||||
device.SupportsDynamicState3DepthClampEnable();
|
||||
@@ -628,10 +632,7 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
|
||||
dynamic_features.has_extended_dynamic_state_3_blend ||
|
||||
(key.state.extended_dynamic_state_3_enables != 0) !=
|
||||
dynamic_features.has_extended_dynamic_state_3_enables ||
|
||||
(key.state.color_write_enable_dynamic != 0) !=
|
||||
dynamic_features.has_color_write_enable ||
|
||||
(key.state.dynamic_vertex_input != 0) !=
|
||||
dynamic_features.has_dynamic_vertex_input) {
|
||||
(key.state.dynamic_vertex_input != 0) != dynamic_features.has_dynamic_vertex_input) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -41,8 +41,8 @@ class SamplesQueryBank : public VideoCommon::BankBase {
|
||||
public:
|
||||
static constexpr size_t BANK_SIZE = 256;
|
||||
static constexpr size_t QUERY_SIZE = 8;
|
||||
explicit SamplesQueryBank(const Device& device_, Scheduler& scheduler_, size_t index_)
|
||||
: BankBase(BANK_SIZE), device{device_}, scheduler{scheduler_}, index{index_} {
|
||||
explicit SamplesQueryBank(const Device& device_, size_t index_)
|
||||
: BankBase(BANK_SIZE), device{device_}, index{index_} {
|
||||
const auto& dev = device.GetLogical();
|
||||
query_pool = dev.CreateQueryPool({
|
||||
.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO,
|
||||
@@ -60,28 +60,12 @@ public:
|
||||
void Reset() override {
|
||||
ASSERT(references == 0);
|
||||
VideoCommon::BankBase::Reset();
|
||||
if (device.IsHostQueryResetSupported()) {
|
||||
const auto& dev = device.GetLogical();
|
||||
dev.ResetQueryPool(*query_pool, 0, BANK_SIZE);
|
||||
} else {
|
||||
scheduler.RequestOutsideRenderPassOperationContext();
|
||||
scheduler.Record([pool = *query_pool](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.ResetQueryPool(pool, 0, BANK_SIZE);
|
||||
});
|
||||
}
|
||||
const auto& dev = device.GetLogical();
|
||||
dev.ResetQueryPool(*query_pool, 0, BANK_SIZE);
|
||||
host_results.fill(0ULL);
|
||||
next_bank = 0;
|
||||
}
|
||||
|
||||
void AddReference(size_t how_many = 1) {
|
||||
BankBase::AddReference(how_many);
|
||||
last_used_tick = scheduler.CurrentTick();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsDead() const {
|
||||
return BankBase::IsDead() && scheduler.IsFree(last_used_tick);
|
||||
}
|
||||
|
||||
void Sync(size_t start, size_t size) {
|
||||
const auto& dev = device.GetLogical();
|
||||
const VkResult query_result = dev.GetQueryResults(
|
||||
@@ -114,11 +98,9 @@ public:
|
||||
|
||||
private:
|
||||
const Device& device;
|
||||
Scheduler& scheduler;
|
||||
const size_t index;
|
||||
vk::QueryPool query_pool;
|
||||
std::array<u64, BANK_SIZE> host_results;
|
||||
u64 last_used_tick{};
|
||||
};
|
||||
|
||||
using BaseStreamer = VideoCommon::SimpleStreamer<VideoCommon::HostQueryBase>;
|
||||
@@ -236,8 +218,7 @@ public:
|
||||
}
|
||||
PauseCounter();
|
||||
const auto driver_id = device.GetDriverID();
|
||||
if (driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY ||
|
||||
driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) {
|
||||
if (driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) {
|
||||
pending_sync.clear();
|
||||
sync_values_stash.clear();
|
||||
return;
|
||||
@@ -450,7 +431,7 @@ private:
|
||||
void ReserveBank() {
|
||||
current_bank_id =
|
||||
bank_pool.ReserveBank([this](std::deque<SamplesQueryBank>& queue, size_t index) {
|
||||
queue.emplace_back(device, scheduler, index);
|
||||
queue.emplace_back(device, index);
|
||||
});
|
||||
if (current_bank) {
|
||||
current_bank->next_bank = current_bank_id + 1;
|
||||
@@ -639,15 +620,6 @@ public:
|
||||
VideoCommon::BankBase::Reset();
|
||||
}
|
||||
|
||||
void AddReference(size_t how_many = 1) {
|
||||
BankBase::AddReference(how_many);
|
||||
last_used_tick = scheduler.CurrentTick();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsDead() const {
|
||||
return BankBase::IsDead() && scheduler.IsFree(last_used_tick);
|
||||
}
|
||||
|
||||
void Sync(StagingBufferRef& stagging_buffer, size_t extra_offset, size_t start, size_t size) {
|
||||
scheduler.RequestOutsideRenderPassOperationContext();
|
||||
scheduler.Record([this, dst_buffer = stagging_buffer.buffer, extra_offset, start,
|
||||
@@ -673,7 +645,6 @@ private:
|
||||
Scheduler& scheduler;
|
||||
const size_t index;
|
||||
vk::Buffer buffer;
|
||||
u64 last_used_tick{};
|
||||
};
|
||||
|
||||
class PrimitivesSucceededStreamer;
|
||||
@@ -951,7 +922,7 @@ private:
|
||||
return;
|
||||
}
|
||||
has_flushed_end_pending = false;
|
||||
|
||||
|
||||
// Refresh buffer state before ending transform feedback to ensure counters_count is up-to-date.
|
||||
UpdateBuffers();
|
||||
if (buffers_count == 0) {
|
||||
@@ -1521,7 +1492,7 @@ bool QueryCacheRuntime::HostConditionalRenderingCompareValues(VideoCommon::Looku
|
||||
auto driver_id = impl->device.GetDriverID();
|
||||
const bool is_gpu_high = Settings::IsGPULevelHigh();
|
||||
|
||||
if ((!is_gpu_high && driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) || driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) {
|
||||
if ((!is_gpu_high && driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) || driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) {
|
||||
EndHostConditionalRendering();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -223,10 +223,7 @@ RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra
|
||||
scheduler.SetQueryCache(query_cache);
|
||||
}
|
||||
|
||||
RasterizerVulkan::~RasterizerVulkan() {
|
||||
scheduler.WaitWorker();
|
||||
scheduler.Finish();
|
||||
}
|
||||
RasterizerVulkan::~RasterizerVulkan() = default;
|
||||
|
||||
template <typename Func>
|
||||
void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) {
|
||||
@@ -1014,12 +1011,12 @@ void RasterizerVulkan::UpdateDynamicStates() {
|
||||
auto& regs = maxwell3d->regs;
|
||||
auto& flags = maxwell3d->dirty.flags;
|
||||
const auto topology = maxwell3d->draw_manager.draw_state.topology;
|
||||
const bool topology_changed = state_tracker.ChangePrimitiveTopology(topology);
|
||||
if (topology_changed) {
|
||||
if (state_tracker.ChangePrimitiveTopology(topology)) {
|
||||
flags[Dirty::DepthBiasEnable] = true;
|
||||
flags[Dirty::PrimitiveRestartEnable] = true;
|
||||
}
|
||||
|
||||
// Core Dynamic States (Vulkan 1.0) - Always active regardless of dyna_state setting
|
||||
UpdateViewportsState(regs);
|
||||
UpdateScissorsState(regs);
|
||||
UpdateDepthBias(regs);
|
||||
@@ -1028,6 +1025,7 @@ void RasterizerVulkan::UpdateDynamicStates() {
|
||||
UpdateStencilFaces(regs);
|
||||
UpdateLineWidth(regs);
|
||||
|
||||
// EDS1: CullMode, DepthCompare, FrontFace, StencilOp, DepthBoundsTest, DepthTest, DepthWrite, StencilTest
|
||||
if (device.IsExtExtendedDynamicStateSupported()) {
|
||||
UpdateCullMode(regs);
|
||||
UpdateDepthCompareOp(regs);
|
||||
@@ -1039,24 +1037,21 @@ void RasterizerVulkan::UpdateDynamicStates() {
|
||||
UpdateDepthWriteEnable(regs);
|
||||
UpdateStencilTestEnable(regs);
|
||||
}
|
||||
if (topology_changed) {
|
||||
scheduler.Record([topology_vk = MaxwellToVK::PrimitiveTopology(device, topology)](
|
||||
vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.SetPrimitiveTopologyEXT(topology_vk);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// EDS2: PrimitiveRestart, RasterizerDiscard, DepthBias enable/disable
|
||||
if (device.IsExtExtendedDynamicState2Supported()) {
|
||||
UpdatePrimitiveRestartEnable(regs);
|
||||
UpdateRasterizerDiscardEnable(regs);
|
||||
UpdateDepthBiasEnable(regs);
|
||||
}
|
||||
|
||||
// EDS2 Extras: LogicOp operation selection
|
||||
if (device.IsExtExtendedDynamicState2ExtrasSupported()) {
|
||||
UpdateLogicOp(regs);
|
||||
}
|
||||
|
||||
// EDS3 Enables: LogicOpEnable, DepthClamp, LineStipple, ConservativeRaster
|
||||
if (device.IsExtExtendedDynamicState3EnablesSupported()) {
|
||||
using namespace Tegra::Engines;
|
||||
// AMD Workaround: LogicOp incompatible with float render targets
|
||||
@@ -1081,12 +1076,12 @@ void RasterizerVulkan::UpdateDynamicStates() {
|
||||
UpdateAlphaToOneEnable(regs);
|
||||
}
|
||||
|
||||
// EDS3 Blending: ColorBlendEnable, ColorBlendEquation, ColorWriteMask
|
||||
if (device.IsExtExtendedDynamicState3BlendingSupported()) {
|
||||
UpdateBlending(regs);
|
||||
} else if (device.IsExtColorWriteEnableSupported()) {
|
||||
UpdateColorWriteEnable(regs);
|
||||
}
|
||||
|
||||
// Vertex Input Dynamic State: Independent from EDS levels
|
||||
if (device.IsExtVertexInputDynamicStateSupported()) {
|
||||
if (auto* gp = pipeline_cache.CurrentGraphicsPipeline(); gp && gp->HasDynamicVertexInput()) {
|
||||
UpdateVertexInput(regs);
|
||||
@@ -1099,6 +1094,7 @@ void RasterizerVulkan::HandleTransformFeedback() {
|
||||
|
||||
const auto& regs = maxwell3d->regs;
|
||||
if (!device.IsExtTransformFeedbackSupported()) {
|
||||
// If the guest enabled transform feedback, warn once that the device lacks support.
|
||||
if (regs.transform_feedback_enabled != 0) {
|
||||
std::call_once(warn_unsupported, [&] {
|
||||
LOG_WARNING(Render_Vulkan, "Transform feedback requested by guest but VK_EXT_transform_feedback is unavailable; queries disabled");
|
||||
@@ -1727,16 +1723,9 @@ void RasterizerVulkan::UpdateBlending(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
|
||||
if (state_tracker.TouchBlendEnable()) {
|
||||
std::array<VkBool32, Maxwell::NumRenderTargets> setup_enables{};
|
||||
for (size_t index = 0; index < Maxwell::NumRenderTargets; index++) {
|
||||
bool is_integer = false;
|
||||
if (regs.rt[index].format != Tegra::RenderTargetFormat::NONE) {
|
||||
const auto format =
|
||||
VideoCore::Surface::PixelFormatFromRenderTargetFormat(regs.rt[index].format);
|
||||
is_integer = IsPixelFormatInteger(format);
|
||||
}
|
||||
setup_enables[index] =
|
||||
(!is_integer && regs.blend.enable[index] != 0) ? VK_TRUE : VK_FALSE;
|
||||
}
|
||||
std::ranges::transform(
|
||||
regs.blend.enable, setup_enables.begin(),
|
||||
[&](const auto& is_enabled) { return is_enabled != 0 ? VK_TRUE : VK_FALSE; });
|
||||
scheduler.Record([setup_enables](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.SetColorBlendEnableEXT(0, setup_enables);
|
||||
});
|
||||
@@ -1785,20 +1774,6 @@ void RasterizerVulkan::UpdateBlending(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerVulkan::UpdateColorWriteEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
if (!state_tracker.TouchColorMask()) {
|
||||
return;
|
||||
}
|
||||
std::array<VkBool32, Maxwell::NumRenderTargets> setup_enables{};
|
||||
for (size_t index = 0; index < Maxwell::NumRenderTargets; index++) {
|
||||
const auto& mask = regs.color_mask[regs.color_mask_common ? 0 : index];
|
||||
setup_enables[index] = (mask.R || mask.G || mask.B || mask.A) ? VK_TRUE : VK_FALSE;
|
||||
}
|
||||
scheduler.Record([setup_enables](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.SetColorWriteEnableEXT(setup_enables);
|
||||
});
|
||||
}
|
||||
|
||||
void RasterizerVulkan::UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
if (!state_tracker.TouchStencilTestEnable()) {
|
||||
return;
|
||||
|
||||
@@ -191,7 +191,6 @@ private:
|
||||
void UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs);
|
||||
void UpdateLogicOp(Tegra::Engines::Maxwell3D::Regs& regs);
|
||||
void UpdateBlending(Tegra::Engines::Maxwell3D::Regs& regs);
|
||||
void UpdateColorWriteEnable(Tegra::Engines::Maxwell3D::Regs& regs);
|
||||
|
||||
void UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs);
|
||||
|
||||
|
||||
@@ -344,9 +344,7 @@ void Scheduler::EndRenderPass()
|
||||
|
||||
Record([num_images = num_renderpass_images,
|
||||
images = renderpass_images,
|
||||
ranges = renderpass_image_ranges,
|
||||
has_transform_feedback = device.IsExtTransformFeedbackSupported()](
|
||||
vk::CommandBuffer cmdbuf) {
|
||||
ranges = renderpass_image_ranges](vk::CommandBuffer cmdbuf) {
|
||||
std::array<VkImageMemoryBarrier, 9> barriers;
|
||||
for (size_t i = 0; i < num_images; ++i) {
|
||||
const VkImageSubresourceRange& range = ranges[i];
|
||||
@@ -386,17 +384,6 @@ void Scheduler::EndRenderPass()
|
||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE,
|
||||
0, nullptr, nullptr, vk::Span(barriers.data(), num_images));
|
||||
if (has_transform_feedback) {
|
||||
static constexpr VkMemoryBarrier XFB_OUTPUT_BARRIER{
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = VK_ACCESS_TRANSFORM_FEEDBACK_WRITE_BIT_EXT,
|
||||
.dstAccessMask = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT,
|
||||
};
|
||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT,
|
||||
VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
0, XFB_OUTPUT_BARRIER);
|
||||
}
|
||||
});
|
||||
|
||||
state.renderpass = VkRenderPass{};
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
@@ -129,32 +128,9 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
|
||||
return usage;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool WillUseAcceleratedAstcDecode(const Device& device, const ImageInfo& info) {
|
||||
if (!IsPixelFormatASTC(info.format) || device.IsOptimalAstcSupported()) {
|
||||
return false;
|
||||
}
|
||||
if (Settings::values.accelerate_astc.GetValue() != Settings::AstcDecodeMode::Gpu) {
|
||||
return false;
|
||||
}
|
||||
return Settings::values.astc_recompression.GetValue() ==
|
||||
Settings::AstcRecompression::Uncompressed &&
|
||||
info.size.depth == 1;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool WillUseWidenedAstcFormat(const Device& device, const ImageInfo& info) {
|
||||
return WillUseAcceleratedAstcDecode(device, info) &&
|
||||
!VideoCore::Surface::IsPixelFormatSRGB(info.format);
|
||||
}
|
||||
|
||||
[[nodiscard]] VkImageCreateInfo MakeImageCreateInfo(const Device& device, const ImageInfo& info,
|
||||
std::optional<VkFormat> format_override = {}) {
|
||||
auto format_info =
|
||||
[[nodiscard]] VkImageCreateInfo MakeImageCreateInfo(const Device& device, const ImageInfo& info) {
|
||||
const auto format_info =
|
||||
MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, false, info.format);
|
||||
if (format_override) {
|
||||
format_info.format = *format_override;
|
||||
format_info.attachable = false;
|
||||
format_info.storage = true;
|
||||
}
|
||||
VkImageCreateFlags flags{};
|
||||
if (info.type == ImageType::e2D && info.resources.layers >= 6 &&
|
||||
info.size.width == info.size.height && !device.HasBrokenCubeImageCompatibility()) {
|
||||
@@ -188,12 +164,11 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
|
||||
}
|
||||
|
||||
[[nodiscard]] vk::Image MakeImage(const Device& device, const MemoryAllocator& allocator,
|
||||
const ImageInfo& info, std::span<const VkFormat> view_formats,
|
||||
std::optional<VkFormat> format_override = {}) {
|
||||
const ImageInfo& info, std::span<const VkFormat> view_formats) {
|
||||
if (info.type == ImageType::Buffer) {
|
||||
return vk::Image{};
|
||||
}
|
||||
VkImageCreateInfo image_ci = MakeImageCreateInfo(device, info, format_override);
|
||||
VkImageCreateInfo image_ci = MakeImageCreateInfo(device, info);
|
||||
const VkImageFormatListCreateInfo image_format_list = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
@@ -1324,9 +1299,7 @@ void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, Im
|
||||
case PixelFormat::R32G32_FLOAT:
|
||||
case PixelFormat::R32G32_SINT:
|
||||
case PixelFormat::R32_FLOAT:
|
||||
if (src_view.format == PixelFormat::D32_FLOAT &&
|
||||
(dst_view.format == PixelFormat::B5G6R5_UNORM ||
|
||||
Settings::values.fix_bloom_effects.GetValue())) {
|
||||
if ((src_view.format == PixelFormat::D32_FLOAT) && Settings::values.fix_bloom_effects.GetValue()) {
|
||||
const Region2D region{
|
||||
.start = {0, 0},
|
||||
.end = {static_cast<s32>(dst->RenderArea().width),
|
||||
@@ -1582,19 +1555,15 @@ void TextureCacheRuntime::TickFrame() {}
|
||||
Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu_addr_,
|
||||
VAddr cpu_addr_)
|
||||
: VideoCommon::ImageBase(info_, gpu_addr_, cpu_addr_), scheduler{&runtime_.scheduler},
|
||||
runtime{&runtime_},
|
||||
original_image(MakeImage(runtime_.device, runtime_.memory_allocator, info,
|
||||
WillUseWidenedAstcFormat(runtime_.device, info)
|
||||
? std::span<const VkFormat>{}
|
||||
: runtime->ViewFormats(info.format),
|
||||
WillUseWidenedAstcFormat(runtime_.device, info)
|
||||
? std::make_optional(VK_FORMAT_R32G32B32A32_SFLOAT)
|
||||
: std::nullopt)),
|
||||
runtime{&runtime_}, original_image(MakeImage(runtime_.device, runtime_.memory_allocator, info,
|
||||
runtime->ViewFormats(info.format))),
|
||||
aspect_mask(ImageAspectMask(info.format)) {
|
||||
if (IsPixelFormatASTC(info.format) && !runtime->device.IsOptimalAstcSupported()) {
|
||||
switch (Settings::values.accelerate_astc.GetValue()) {
|
||||
case Settings::AstcDecodeMode::Gpu:
|
||||
if (WillUseAcceleratedAstcDecode(runtime->device, info)) {
|
||||
if (Settings::values.astc_recompression.GetValue() ==
|
||||
Settings::AstcRecompression::Uncompressed &&
|
||||
info.size.depth == 1) {
|
||||
flags |= VideoCommon::ImageFlagBits::AcceleratedUpload;
|
||||
}
|
||||
break;
|
||||
@@ -1620,12 +1589,9 @@ Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu
|
||||
Settings::values.astc_recompression.GetValue() ==
|
||||
Settings::AstcRecompression::Uncompressed) {
|
||||
const auto& device = runtime->device.GetLogical();
|
||||
const VkFormat storage_format = WillUseWidenedAstcFormat(runtime->device, info)
|
||||
? VK_FORMAT_R32G32B32A32_SFLOAT
|
||||
: VK_FORMAT_A8B8G8R8_UNORM_PACK32;
|
||||
for (s32 level = 0; level < info.resources.levels; ++level) {
|
||||
storage_image_views[level] =
|
||||
MakeStorageView(device, level, *original_image, storage_format);
|
||||
MakeStorageView(device, level, *original_image, VK_FORMAT_A8B8G8R8_UNORM_PACK32);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1635,6 +1601,9 @@ Image::Image(const VideoCommon::NullImageParams& params) : VideoCommon::ImageBas
|
||||
Image::~Image() = default;
|
||||
|
||||
void Image::AllocateComputeUnswizzleBuffer(u32 max_slices) {
|
||||
if (has_compute_unswizzle_buffer)
|
||||
return;
|
||||
|
||||
using VideoCore::Surface::BytesPerBlock;
|
||||
|
||||
const u32 block_bytes = BytesPerBlock(info.format); // 8 for BC1, 16 for BC6H
|
||||
@@ -1651,12 +1620,7 @@ void Image::AllocateComputeUnswizzleBuffer(u32 max_slices) {
|
||||
static_cast<u64>(blocks_y) *
|
||||
static_cast<u64>(blocks_z);
|
||||
|
||||
const VkDeviceSize required_size = block_count * block_bytes;
|
||||
if (has_compute_unswizzle_buffer && required_size <= compute_unswizzle_buffer_size) {
|
||||
return;
|
||||
}
|
||||
|
||||
compute_unswizzle_buffer_size = required_size;
|
||||
compute_unswizzle_buffer_size = block_count * block_bytes;
|
||||
|
||||
VkBufferCreateInfo ci{
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
||||
@@ -1976,13 +1940,8 @@ void Image::DownloadMemory(const StagingBufferRef& map, std::span<const BufferIm
|
||||
VkImageView Image::StorageImageView(s32 level) noexcept {
|
||||
auto& view = storage_image_views[level];
|
||||
if (!view) {
|
||||
auto format_info =
|
||||
const auto format_info =
|
||||
MaxwellToVK::SurfaceFormat(runtime->device, FormatType::Optimal, true, info.format);
|
||||
if (WillUseAcceleratedAstcDecode(runtime->device, info)) {
|
||||
format_info.format = WillUseWidenedAstcFormat(runtime->device, info)
|
||||
? VK_FORMAT_R32G32B32A32_SFLOAT
|
||||
: VK_FORMAT_A8B8G8R8_UNORM_PACK32;
|
||||
}
|
||||
view = MakeStorageView(runtime->device.GetLogical(), level, *(this->*current_image),
|
||||
format_info.format);
|
||||
}
|
||||
@@ -2149,11 +2108,7 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
|
||||
SanitizeDepthStencilSwizzle(swizzle, device->SupportsDepthStencilSwizzleOne());
|
||||
}
|
||||
}
|
||||
uses_widened_astc_format = WillUseWidenedAstcFormat(*device, image.info);
|
||||
auto format_info = MaxwellToVK::SurfaceFormat(*device, FormatType::Optimal, true, format);
|
||||
if (uses_widened_astc_format) {
|
||||
format_info.format = VK_FORMAT_R32G32B32A32_SFLOAT;
|
||||
}
|
||||
const auto format_info = MaxwellToVK::SurfaceFormat(*device, FormatType::Optimal, true, format);
|
||||
const VkImageUsageFlags requested_view_usage = ImageUsageFlags(format_info, format);
|
||||
const VkImageUsageFlags image_usage = image.UsageFlags();
|
||||
const VkImageUsageFlags clamped_view_usage = requested_view_usage & image_usage;
|
||||
@@ -2287,14 +2242,7 @@ VkImageView ImageView::StorageView(Shader::TextureType texture_type,
|
||||
Shader::ImageFormat image_format) {
|
||||
if (image_handle) {
|
||||
if (image_format == Shader::ImageFormat::Typeless) {
|
||||
if (!typeless_storage_view) {
|
||||
auto info = MaxwellToVK::SurfaceFormat(*device, FormatType::Optimal, true, format);
|
||||
if (uses_widened_astc_format) {
|
||||
info.format = VK_FORMAT_R32G32B32A32_SFLOAT;
|
||||
}
|
||||
typeless_storage_view = MakeView(info.format, VK_IMAGE_ASPECT_COLOR_BIT, texture_type);
|
||||
}
|
||||
return *typeless_storage_view;
|
||||
return Handle(texture_type);
|
||||
}
|
||||
const bool is_signed = image_format == Shader::ImageFormat::R8_SINT
|
||||
|| image_format == Shader::ImageFormat::R16_SINT;
|
||||
@@ -2303,7 +2251,7 @@ VkImageView ImageView::StorageView(Shader::TextureType texture_type,
|
||||
auto& views{is_signed ? storage_views->signeds : storage_views->unsigneds};
|
||||
auto& view{views[size_t(texture_type)]};
|
||||
if (!view)
|
||||
view = MakeView(Format(image_format), VK_IMAGE_ASPECT_COLOR_BIT, texture_type);
|
||||
view = MakeView(Format(image_format), VK_IMAGE_ASPECT_COLOR_BIT);
|
||||
return *view;
|
||||
}
|
||||
return VK_NULL_HANDLE;
|
||||
@@ -2313,28 +2261,13 @@ bool ImageView::IsRescaled() const noexcept {
|
||||
return (*slot_images)[image_id].IsRescaled();
|
||||
}
|
||||
|
||||
vk::ImageView ImageView::MakeView(VkFormat vk_format, VkImageAspectFlags aspect_mask,
|
||||
std::optional<Shader::TextureType> texture_type) {
|
||||
VkImageViewType view_type = ImageViewType(type);
|
||||
VkImageSubresourceRange subresource_range = MakeSubresourceRange(aspect_mask, range);
|
||||
if (texture_type) {
|
||||
view_type = ImageViewType(*texture_type);
|
||||
switch (view_type) {
|
||||
case VK_IMAGE_VIEW_TYPE_1D_ARRAY:
|
||||
case VK_IMAGE_VIEW_TYPE_2D_ARRAY:
|
||||
case VK_IMAGE_VIEW_TYPE_CUBE_ARRAY:
|
||||
break;
|
||||
default:
|
||||
subresource_range.layerCount = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
vk::ImageView ImageView::MakeView(VkFormat vk_format, VkImageAspectFlags aspect_mask) {
|
||||
return device->GetLogical().CreateImageView({
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.image = image_handle,
|
||||
.viewType = view_type,
|
||||
.viewType = ImageViewType(type),
|
||||
.format = vk_format,
|
||||
.components{
|
||||
.r = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
@@ -2342,7 +2275,7 @@ vk::ImageView ImageView::MakeView(VkFormat vk_format, VkImageAspectFlags aspect_
|
||||
.b = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
.a = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
},
|
||||
.subresourceRange = subresource_range,
|
||||
.subresourceRange = MakeSubresourceRange(aspect_mask, range),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2364,15 +2297,12 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t
|
||||
const void* pnext = nullptr;
|
||||
if (has_custom_border_colors) {
|
||||
pnext = &border_ci;
|
||||
// Log extension usage for custom border color
|
||||
if (GPU::Logging::IsActive()) {
|
||||
GPU::Logging::GPULogger::GetInstance().LogExtensionUsage(
|
||||
"VK_EXT_custom_border_color", "Sampler::Sampler");
|
||||
}
|
||||
}
|
||||
if (device.IsExtBorderColorSwizzleSupported() && GPU::Logging::IsActive()) {
|
||||
GPU::Logging::GPULogger::GetInstance().LogExtensionUsage(
|
||||
"VK_EXT_border_color_swizzle", "Sampler::Sampler");
|
||||
}
|
||||
const VkSamplerReductionModeCreateInfoEXT reduction_ci{
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_REDUCTION_MODE_CREATE_INFO_EXT,
|
||||
.pNext = pnext,
|
||||
@@ -2386,28 +2316,20 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t
|
||||
// Some games have samplers with garbage. Sanitize them here.
|
||||
const f32 max_anisotropy = std::clamp(tsc.MaxAnisotropy(), 1.0f, 16.0f);
|
||||
|
||||
const VkFilter mag_filter{MaxwellToVK::Sampler::Filter(tsc.mag_filter)};
|
||||
const VkFilter min_filter{MaxwellToVK::Sampler::Filter(tsc.min_filter)};
|
||||
const VkSamplerMipmapMode mipmap_mode{MaxwellToVK::Sampler::MipmapMode(tsc.mipmap_filter)};
|
||||
const bool has_linear_filtering{mag_filter == VK_FILTER_LINEAR ||
|
||||
min_filter == VK_FILTER_LINEAR ||
|
||||
mipmap_mode == VK_SAMPLER_MIPMAP_MODE_LINEAR};
|
||||
|
||||
const auto create_sampler = [&](const f32 anisotropy, bool force_nearest) {
|
||||
const auto create_sampler = [&](const f32 anisotropy) {
|
||||
return device.GetLogical().CreateSampler(VkSamplerCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
||||
.pNext = pnext,
|
||||
.flags = 0,
|
||||
.magFilter = force_nearest ? VK_FILTER_NEAREST : mag_filter,
|
||||
.minFilter = force_nearest ? VK_FILTER_NEAREST : min_filter,
|
||||
.mipmapMode = force_nearest ? VK_SAMPLER_MIPMAP_MODE_NEAREST : mipmap_mode,
|
||||
.magFilter = MaxwellToVK::Sampler::Filter(tsc.mag_filter),
|
||||
.minFilter = MaxwellToVK::Sampler::Filter(tsc.min_filter),
|
||||
.mipmapMode = MaxwellToVK::Sampler::MipmapMode(tsc.mipmap_filter),
|
||||
.addressModeU = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_u, tsc.mag_filter),
|
||||
.addressModeV = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_v, tsc.mag_filter),
|
||||
.addressModeW = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_p, tsc.mag_filter),
|
||||
.mipLodBias = tsc.LodBias(),
|
||||
.anisotropyEnable =
|
||||
static_cast<VkBool32>(!force_nearest && anisotropy > 1.0f ? VK_TRUE : VK_FALSE),
|
||||
.maxAnisotropy = force_nearest ? 1.0f : anisotropy,
|
||||
.anisotropyEnable = static_cast<VkBool32>(anisotropy > 1.0f ? VK_TRUE : VK_FALSE),
|
||||
.maxAnisotropy = anisotropy,
|
||||
.compareEnable = tsc.depth_compare_enabled,
|
||||
.compareOp = MaxwellToVK::Sampler::DepthCompareFunction(tsc.depth_compare_func),
|
||||
.minLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.0f : tsc.MinLod(),
|
||||
@@ -2418,18 +2340,11 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t
|
||||
});
|
||||
};
|
||||
|
||||
sampler = create_sampler(max_anisotropy, false);
|
||||
sampler = create_sampler(max_anisotropy);
|
||||
|
||||
const f32 max_anisotropy_default = static_cast<f32>(1U << tsc.max_anisotropy);
|
||||
if (max_anisotropy > max_anisotropy_default) {
|
||||
sampler_default_anisotropy = create_sampler(max_anisotropy_default, false);
|
||||
}
|
||||
if (has_linear_filtering) {
|
||||
// Integer-format image views can never be linearly filtered
|
||||
// (VUID-vkCmdDraw*-magFilter-04553); this sampler is cached purely from the guest's TSC,
|
||||
// decoupled from whichever ImageView it ends up paired with, so build a nearest-forced
|
||||
// fallback here for callers to swap to when the paired view turns out to be integer.
|
||||
sampler_nearest = create_sampler(1.0f, true);
|
||||
sampler_default_anisotropy = create_sampler(max_anisotropy_default);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -370,15 +370,13 @@ private:
|
||||
std::array<vk::ImageView, Shader::NUM_TEXTURE_TYPES> unsigneds;
|
||||
};
|
||||
|
||||
[[nodiscard]] vk::ImageView MakeView(VkFormat vk_format, VkImageAspectFlags aspect_mask,
|
||||
std::optional<Shader::TextureType> texture_type = std::nullopt);
|
||||
[[nodiscard]] vk::ImageView MakeView(VkFormat vk_format, VkImageAspectFlags aspect_mask);
|
||||
|
||||
const Device* device = nullptr;
|
||||
const SlotVector<Image>* slot_images = nullptr;
|
||||
|
||||
std::array<vk::ImageView, Shader::NUM_TEXTURE_TYPES> image_views;
|
||||
std::optional<StorageViews> storage_views;
|
||||
vk::ImageView typeless_storage_view;
|
||||
vk::ImageView depth_view;
|
||||
vk::ImageView stencil_view;
|
||||
vk::ImageView color_view;
|
||||
@@ -387,8 +385,6 @@ private:
|
||||
VkImageView render_target = VK_NULL_HANDLE;
|
||||
VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
u32 buffer_size = 0;
|
||||
|
||||
bool uses_widened_astc_format = false;
|
||||
};
|
||||
|
||||
class ImageAlloc : public VideoCommon::ImageAllocBase {};
|
||||
@@ -409,18 +405,9 @@ public:
|
||||
return static_cast<bool>(sampler_default_anisotropy);
|
||||
}
|
||||
|
||||
[[nodiscard]] VkSampler HandleWithNearestFilter() const noexcept {
|
||||
return *sampler_nearest;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool HasLinearFiltering() const noexcept {
|
||||
return static_cast<bool>(sampler_nearest);
|
||||
}
|
||||
|
||||
private:
|
||||
vk::Sampler sampler;
|
||||
vk::Sampler sampler_default_anisotropy;
|
||||
vk::Sampler sampler_nearest;
|
||||
};
|
||||
|
||||
struct TextureCacheParams {
|
||||
|
||||
@@ -344,10 +344,6 @@ bool IsPixelFormatETC2(PixelFormat format) {
|
||||
case PixelFormat::ETC2_RGB_SRGB:
|
||||
case PixelFormat::ETC2_RGBA_SRGB:
|
||||
case PixelFormat::ETC2_RGB_PTA_SRGB:
|
||||
case PixelFormat::EAC_R11_UNORM:
|
||||
case PixelFormat::EAC_R11_SNORM:
|
||||
case PixelFormat::EAC_R11G11_UNORM:
|
||||
case PixelFormat::EAC_R11G11_SNORM:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
||||
@@ -119,10 +119,6 @@ namespace VideoCore::Surface {
|
||||
PIXEL_FORMAT_ELEM(ETC2_RGB_SRGB, 4, 4, 64) \
|
||||
PIXEL_FORMAT_ELEM(ETC2_RGBA_SRGB, 4, 4, 128) \
|
||||
PIXEL_FORMAT_ELEM(ETC2_RGB_PTA_SRGB, 4, 4, 64) \
|
||||
PIXEL_FORMAT_ELEM(EAC_R11_UNORM, 4, 4, 64) \
|
||||
PIXEL_FORMAT_ELEM(EAC_R11_SNORM, 4, 4, 64) \
|
||||
PIXEL_FORMAT_ELEM(EAC_R11G11_UNORM, 4, 4, 128) \
|
||||
PIXEL_FORMAT_ELEM(EAC_R11G11_SNORM, 4, 4, 128) \
|
||||
/* Depth formats */ \
|
||||
PIXEL_FORMAT_ELEM(D32_FLOAT, 1, 1, 32) \
|
||||
PIXEL_FORMAT_ELEM(D16_UNORM, 1, 1, 16) \
|
||||
|
||||
@@ -204,15 +204,6 @@ PixelFormat PixelFormatFromTextureInfo(TextureFormat format, ComponentType red,
|
||||
return PixelFormat::ETC2_RGB_PTA_SRGB;
|
||||
case Hash(TextureFormat::ETC2_RGBA, UNORM, SRGB):
|
||||
return PixelFormat::ETC2_RGBA_SRGB;
|
||||
/* EAC */
|
||||
case Hash(TextureFormat::EAC, UNORM):
|
||||
return PixelFormat::EAC_R11_UNORM;
|
||||
case Hash(TextureFormat::EAC, SNORM):
|
||||
return PixelFormat::EAC_R11_SNORM;
|
||||
case Hash(TextureFormat::EACX2, UNORM):
|
||||
return PixelFormat::EAC_R11G11_UNORM;
|
||||
case Hash(TextureFormat::EACX2, SNORM):
|
||||
return PixelFormat::EAC_R11G11_SNORM;
|
||||
/* ASTC */
|
||||
case Hash(TextureFormat::ASTC_2D_4X4, UNORM, LINEAR):
|
||||
return PixelFormat::ASTC_2D_4X4_UNORM;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2016 The University of North Carolina at Chapel Hill
|
||||
@@ -1269,220 +1269,6 @@ static inline u32 Select2DPartition(s32 seed, s32 x, s32 y, s32 partitionCount,
|
||||
return SelectPartition(seed, x, y, 0, partitionCount, smallBlock);
|
||||
}
|
||||
|
||||
static constexpr bool IsHDRColorEndpointMode(u32 cem) {
|
||||
switch (cem) {
|
||||
case 2:
|
||||
case 3:
|
||||
case 7:
|
||||
case 11:
|
||||
case 14:
|
||||
case 15:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Sign-extends the low nbits of value (a 2's complement field packed into
|
||||
// the bottom of an otherwise-unsigned integer), per C.2.15's HDR endpoint
|
||||
// bitfield unpacking.
|
||||
static constexpr s32 SignExtend(s32 value, u32 nbits) {
|
||||
const s32 sign_bit = 1 << (nbits - 1);
|
||||
return (value ^ sign_bit) - sign_bit;
|
||||
}
|
||||
|
||||
struct HDREndpointRGB {
|
||||
s32 r0, g0, b0;
|
||||
s32 r1, g1, b1;
|
||||
};
|
||||
|
||||
// HDR Endpoint Mode 7 (C.2.15): base RGB + scale factor.
|
||||
static void DecodeHDREndpointMode7(u32 v0, u32 v1, u32 v2, u32 v3, s32& r0, s32& g0, s32& b0,
|
||||
s32& r1, s32& g1, s32& b1) {
|
||||
const u32 modeval = ((v0 & 0xC0) >> 6) | ((v1 & 0x80) >> 5) | ((v2 & 0x80) >> 4);
|
||||
|
||||
u32 majcomp;
|
||||
u32 mode;
|
||||
if ((modeval & 0xC) != 0xC) {
|
||||
majcomp = modeval >> 2;
|
||||
mode = modeval & 3;
|
||||
} else if (modeval != 0xF) {
|
||||
majcomp = modeval & 3;
|
||||
mode = 4;
|
||||
} else {
|
||||
majcomp = 0;
|
||||
mode = 5;
|
||||
}
|
||||
|
||||
s32 red = static_cast<s32>(v0 & 0x3f);
|
||||
s32 green = static_cast<s32>(v1 & 0x1f);
|
||||
s32 blue = static_cast<s32>(v2 & 0x1f);
|
||||
s32 scale = static_cast<s32>(v3 & 0x1f);
|
||||
|
||||
const u32 x0 = (v1 >> 6) & 1;
|
||||
const u32 x1 = (v1 >> 5) & 1;
|
||||
const u32 x2 = (v2 >> 6) & 1;
|
||||
const u32 x3 = (v2 >> 5) & 1;
|
||||
const u32 x4 = (v3 >> 7) & 1;
|
||||
const u32 x5 = (v3 >> 6) & 1;
|
||||
const u32 x6 = (v3 >> 5) & 1;
|
||||
|
||||
const u32 ohm = 1u << mode;
|
||||
if (ohm & 0x30)
|
||||
green |= static_cast<s32>(x0 << 6);
|
||||
if (ohm & 0x3A)
|
||||
green |= static_cast<s32>(x1 << 5);
|
||||
if (ohm & 0x30)
|
||||
blue |= static_cast<s32>(x2 << 6);
|
||||
if (ohm & 0x3A)
|
||||
blue |= static_cast<s32>(x3 << 5);
|
||||
if (ohm & 0x3D)
|
||||
scale |= static_cast<s32>(x6 << 5);
|
||||
if (ohm & 0x2D)
|
||||
scale |= static_cast<s32>(x5 << 6);
|
||||
if (ohm & 0x04)
|
||||
scale |= static_cast<s32>(x4 << 7);
|
||||
if (ohm & 0x3B)
|
||||
red |= static_cast<s32>(x4 << 6);
|
||||
if (ohm & 0x04)
|
||||
red |= static_cast<s32>(x3 << 6);
|
||||
if (ohm & 0x10)
|
||||
red |= static_cast<s32>(x5 << 7);
|
||||
if (ohm & 0x0F)
|
||||
red |= static_cast<s32>(x2 << 7);
|
||||
if (ohm & 0x05)
|
||||
red |= static_cast<s32>(x1 << 8);
|
||||
if (ohm & 0x0A)
|
||||
red |= static_cast<s32>(x0 << 8);
|
||||
if (ohm & 0x05)
|
||||
red |= static_cast<s32>(x0 << 9);
|
||||
if (ohm & 0x02)
|
||||
red |= static_cast<s32>(x6 << 9);
|
||||
if (ohm & 0x01)
|
||||
red |= static_cast<s32>(x3 << 10);
|
||||
if (ohm & 0x02)
|
||||
red |= static_cast<s32>(x5 << 10);
|
||||
|
||||
static constexpr int shamts[6] = {1, 1, 2, 3, 4, 5};
|
||||
const s32 shamt = shamts[mode];
|
||||
red <<= shamt;
|
||||
green <<= shamt;
|
||||
blue <<= shamt;
|
||||
scale <<= shamt;
|
||||
|
||||
if (mode != 5) {
|
||||
green = red - green;
|
||||
blue = red - blue;
|
||||
}
|
||||
|
||||
if (majcomp == 1)
|
||||
std::swap(red, green);
|
||||
if (majcomp == 2)
|
||||
std::swap(red, blue);
|
||||
|
||||
r1 = std::clamp(red, 0, 0xFFF);
|
||||
g1 = std::clamp(green, 0, 0xFFF);
|
||||
b1 = std::clamp(blue, 0, 0xFFF);
|
||||
|
||||
r0 = std::clamp(red - scale, 0, 0xFFF);
|
||||
g0 = std::clamp(green - scale, 0, 0xFFF);
|
||||
b0 = std::clamp(blue - scale, 0, 0xFFF);
|
||||
}
|
||||
|
||||
// HDR Endpoint Mode 11 (C.2.15): direct RGB pair. Shared by modes 11, 14 and 15,
|
||||
// which all decode their RGB the same way and only differ in how alpha is filled in.
|
||||
static HDREndpointRGB DecodeHDREndpointMode11(u32 v0, u32 v1, u32 v2, u32 v3, u32 v4, u32 v5) {
|
||||
const u32 majcomp = ((v4 & 0x80) >> 7) | ((v5 & 0x80) >> 6);
|
||||
if (majcomp == 3) {
|
||||
HDREndpointRGB result;
|
||||
result.r0 = static_cast<s32>(v0 << 4);
|
||||
result.g0 = static_cast<s32>(v2 << 4);
|
||||
result.b0 = static_cast<s32>((v4 & 0x7f) << 5);
|
||||
result.r1 = static_cast<s32>(v1 << 4);
|
||||
result.g1 = static_cast<s32>(v3 << 4);
|
||||
result.b1 = static_cast<s32>((v5 & 0x7f) << 5);
|
||||
return result;
|
||||
}
|
||||
|
||||
const u32 mode = ((v1 & 0x80) >> 7) | ((v2 & 0x80) >> 6) | ((v3 & 0x80) >> 5);
|
||||
s32 va = static_cast<s32>(v0 | ((v1 & 0x40) << 2));
|
||||
s32 vb0 = static_cast<s32>(v2 & 0x3f);
|
||||
s32 vb1 = static_cast<s32>(v3 & 0x3f);
|
||||
s32 vc = static_cast<s32>(v1 & 0x3f);
|
||||
s32 vd0 = static_cast<s32>(v4 & 0x7f);
|
||||
s32 vd1 = static_cast<s32>(v5 & 0x7f);
|
||||
|
||||
static constexpr int dbitstab[8] = {7, 6, 7, 6, 5, 6, 5, 6};
|
||||
vd0 = SignExtend(vd0, dbitstab[mode]);
|
||||
vd1 = SignExtend(vd1, dbitstab[mode]);
|
||||
|
||||
const u32 x0 = (v2 >> 6) & 1;
|
||||
const u32 x1 = (v3 >> 6) & 1;
|
||||
const u32 x2 = (v4 >> 6) & 1;
|
||||
const u32 x3 = (v5 >> 6) & 1;
|
||||
const u32 x4 = (v4 >> 5) & 1;
|
||||
const u32 x5 = (v5 >> 5) & 1;
|
||||
|
||||
const u32 ohm = 1u << mode;
|
||||
if (ohm & 0xA4)
|
||||
va |= static_cast<s32>(x0 << 9);
|
||||
if (ohm & 0x08)
|
||||
va |= static_cast<s32>(x2 << 9);
|
||||
if (ohm & 0x50)
|
||||
va |= static_cast<s32>(x4 << 9);
|
||||
if (ohm & 0x50)
|
||||
va |= static_cast<s32>(x5 << 10);
|
||||
if (ohm & 0xA0)
|
||||
va |= static_cast<s32>(x1 << 10);
|
||||
if (ohm & 0xC0)
|
||||
va |= static_cast<s32>(x2 << 11);
|
||||
if (ohm & 0x04)
|
||||
vc |= static_cast<s32>(x1 << 6);
|
||||
if (ohm & 0xE8)
|
||||
vc |= static_cast<s32>(x3 << 6);
|
||||
if (ohm & 0x20)
|
||||
vc |= static_cast<s32>(x2 << 7);
|
||||
if (ohm & 0x5B)
|
||||
vb0 |= static_cast<s32>(x0 << 6);
|
||||
if (ohm & 0x5B)
|
||||
vb1 |= static_cast<s32>(x1 << 6);
|
||||
if (ohm & 0x12)
|
||||
vb0 |= static_cast<s32>(x2 << 7);
|
||||
if (ohm & 0x12)
|
||||
vb1 |= static_cast<s32>(x3 << 7);
|
||||
|
||||
// NOTE: the published spec text says "modeval >> 1" here, but no "modeval" is defined
|
||||
// in this decode (that name belongs to Mode 7's unrelated decode) -- substituting the
|
||||
// "mode" computed just above reproduces exactly Table C.2.23's per-mode shift amounts
|
||||
// (3,3,2,2,1,1,0,0 for modes 0..7), so this is a spec transcription error, not a real
|
||||
// "modeval" this function forgot to compute.
|
||||
const s32 shamt = (static_cast<s32>(mode) >> 1) ^ 3;
|
||||
va <<= shamt;
|
||||
vb0 <<= shamt;
|
||||
vb1 <<= shamt;
|
||||
vc <<= shamt;
|
||||
vd0 <<= shamt;
|
||||
vd1 <<= shamt;
|
||||
|
||||
HDREndpointRGB result;
|
||||
result.r1 = std::clamp(va, 0, 0xFFF);
|
||||
result.g1 = std::clamp(va - vb0, 0, 0xFFF);
|
||||
result.b1 = std::clamp(va - vb1, 0, 0xFFF);
|
||||
|
||||
result.r0 = std::clamp(va - vc, 0, 0xFFF);
|
||||
result.g0 = std::clamp(va - vb0 - vc - vd0, 0, 0xFFF);
|
||||
result.b0 = std::clamp(va - vb1 - vc - vd1, 0, 0xFFF);
|
||||
|
||||
if (majcomp == 1) {
|
||||
std::swap(result.r0, result.g0);
|
||||
std::swap(result.r1, result.g1);
|
||||
} else if (majcomp == 2) {
|
||||
std::swap(result.r0, result.b0);
|
||||
std::swap(result.r1, result.b1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Section C.2.14
|
||||
static void ComputeEndpoints(Pixel& ep1, Pixel& ep2, const u32*& colorValues,
|
||||
u32 colorEndpointMode) {
|
||||
@@ -1596,85 +1382,8 @@ static void ComputeEndpoints(Pixel& ep1, Pixel& ep2, const u32*& colorValues,
|
||||
ep2.ClampByte();
|
||||
} break;
|
||||
|
||||
case 2: {
|
||||
READ_UINT_VALUES(2)
|
||||
u32 y0, y1;
|
||||
if (v[1] >= v[0]) {
|
||||
y0 = v[0] << 4;
|
||||
y1 = v[1] << 4;
|
||||
} else {
|
||||
y0 = (v[1] << 4) + 8;
|
||||
y1 = (v[0] << 4) - 8;
|
||||
}
|
||||
ep1 = Pixel(0x780, y0, y0, y0);
|
||||
ep2 = Pixel(0x780, y1, y1, y1);
|
||||
} break;
|
||||
|
||||
case 3: {
|
||||
READ_UINT_VALUES(2)
|
||||
u32 y0, d;
|
||||
if (v[0] & 0x80) {
|
||||
y0 = ((v[1] & 0xE0) << 4) | ((v[0] & 0x7F) << 2);
|
||||
d = (v[1] & 0x1F) << 2;
|
||||
} else {
|
||||
y0 = ((v[1] & 0xF0) << 4) | ((v[0] & 0x7F) << 1);
|
||||
d = (v[1] & 0x0F) << 1;
|
||||
}
|
||||
const u32 y1 = (std::min)(y0 + d, 0xFFFU);
|
||||
ep1 = Pixel(0x780, y0, y0, y0);
|
||||
ep2 = Pixel(0x780, y1, y1, y1);
|
||||
} break;
|
||||
|
||||
case 7: {
|
||||
READ_UINT_VALUES(4)
|
||||
s32 r0, g0, b0, r1, g1, b1;
|
||||
DecodeHDREndpointMode7(v[0], v[1], v[2], v[3], r0, g0, b0, r1, g1, b1);
|
||||
ep1 = Pixel(0x780, r0, g0, b0);
|
||||
ep2 = Pixel(0x780, r1, g1, b1);
|
||||
} break;
|
||||
|
||||
case 11: {
|
||||
READ_UINT_VALUES(6)
|
||||
const HDREndpointRGB rgb = DecodeHDREndpointMode11(v[0], v[1], v[2], v[3], v[4], v[5]);
|
||||
ep1 = Pixel(0x780, rgb.r0, rgb.g0, rgb.b0);
|
||||
ep2 = Pixel(0x780, rgb.r1, rgb.g1, rgb.b1);
|
||||
} break;
|
||||
|
||||
case 14: {
|
||||
READ_UINT_VALUES(8)
|
||||
const HDREndpointRGB rgb = DecodeHDREndpointMode11(v[0], v[1], v[2], v[3], v[4], v[5]);
|
||||
// Only mode with LDR (8-bit UNORM)-interpreted alpha; left as-is (0-255).
|
||||
ep1 = Pixel(v[6], rgb.r0, rgb.g0, rgb.b0);
|
||||
ep2 = Pixel(v[7], rgb.r1, rgb.g1, rgb.b1);
|
||||
} break;
|
||||
|
||||
case 15: {
|
||||
READ_UINT_VALUES(8)
|
||||
const HDREndpointRGB rgb = DecodeHDREndpointMode11(v[0], v[1], v[2], v[3], v[4], v[5]);
|
||||
const u32 mode = ((v[6] >> 7) & 1) | ((v[7] >> 6) & 2);
|
||||
s32 a6 = static_cast<s32>(v[6] & 0x7F);
|
||||
s32 a7 = static_cast<s32>(v[7] & 0x7F);
|
||||
s32 alpha0, alpha1;
|
||||
if (mode == 3) {
|
||||
alpha0 = a6 << 5;
|
||||
alpha1 = a7 << 5;
|
||||
} else {
|
||||
a6 |= (a7 << (mode + 1)) & 0x780;
|
||||
a7 &= (0x3F >> mode);
|
||||
a7 ^= 0x20 >> mode;
|
||||
a7 -= 0x20 >> mode;
|
||||
a6 <<= (4 - mode);
|
||||
a7 <<= (4 - mode);
|
||||
a7 += a6;
|
||||
alpha0 = a6;
|
||||
alpha1 = std::clamp(a7, 0, 0xFFF);
|
||||
}
|
||||
ep1 = Pixel(alpha0, rgb.r0, rgb.g0, rgb.b0);
|
||||
ep2 = Pixel(alpha1, rgb.r1, rgb.g1, rgb.b1);
|
||||
} break;
|
||||
|
||||
default:
|
||||
assert(false && "Unsupported color endpoint mode");
|
||||
assert(false && "Unsupported color endpoint mode (is it HDR?)");
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1705,39 +1414,6 @@ static void FillVoidExtentLDR(InputBitStream& strm, std::span<u32> outBuf, u32 b
|
||||
}
|
||||
}
|
||||
|
||||
static float HalfToFloat(u16 h) {
|
||||
const u32 sign = static_cast<u32>(h & 0x8000) << 16;
|
||||
u32 exp = (h & 0x7C00) >> 10;
|
||||
u32 mant = h & 0x3FF;
|
||||
u32 bits;
|
||||
if (exp == 0) {
|
||||
if (mant == 0) {
|
||||
bits = sign;
|
||||
} else {
|
||||
s32 e = 127 - 15 + 1;
|
||||
while ((mant & 0x400) == 0) {
|
||||
mant <<= 1;
|
||||
--e;
|
||||
}
|
||||
mant &= 0x3FF;
|
||||
bits = sign | (static_cast<u32>(e) << 23) | (mant << 13);
|
||||
}
|
||||
} else if (exp == 0x1F) {
|
||||
bits = sign | 0x7F800000 | (mant << 13);
|
||||
} else {
|
||||
bits = sign | ((exp - 15 + 127) << 23) | (mant << 13);
|
||||
}
|
||||
float result;
|
||||
std::memcpy(&result, &bits, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
static u16 HalfToClampedByte(u16 half_bits) {
|
||||
const float value = HalfToFloat(half_bits);
|
||||
const float clamped = std::clamp(value, 0.0f, 1.0f);
|
||||
return static_cast<u16>(clamped * 255.0f + 0.5f);
|
||||
}
|
||||
|
||||
static void FillError(std::span<u32> outBuf, u32 blockWidth, u32 blockHeight) {
|
||||
for (u32 j = 0; j < blockHeight; j++) {
|
||||
for (u32 i = 0; i < blockWidth; i++) {
|
||||
@@ -1955,49 +1631,22 @@ static void DecompressBlock(std::span<const u8, 16> inBuf, const u32 blockWidth,
|
||||
Pixel p;
|
||||
for (u32 c = 0; c < 4; c++) {
|
||||
u32 C0 = endpoints[partition][0].Component(c);
|
||||
C0 = ReplicateByteTo16(C0);
|
||||
u32 C1 = endpoints[partition][1].Component(c);
|
||||
C1 = ReplicateByteTo16(C1);
|
||||
|
||||
u32 plane = 0;
|
||||
if (weightParams.m_bDualPlane && (((planeIdx + 1) & 3) == c)) {
|
||||
plane = 1;
|
||||
}
|
||||
|
||||
u32 weight = weights[plane][j * blockWidth + i];
|
||||
|
||||
// Mode 14 is RGB-HDR but keeps an LDR (8-bit UNORM)-interpreted alpha
|
||||
// (component 0 here, see Pixel::A()) -- the only HDR mode with this split.
|
||||
const bool is_hdr = IsHDRColorEndpointMode(colorEndpointMode[partition]) &&
|
||||
!(colorEndpointMode[partition] == 14 && c == 0);
|
||||
|
||||
if (is_hdr) {
|
||||
// Endpoints are raw 12-bit pseudo-logarithmic values; shift left 4 bits
|
||||
// to become 16-bit before interpolating, per C.2.19.
|
||||
C0 <<= 4;
|
||||
C1 <<= 4;
|
||||
const u32 C = (C0 * (64 - weight) + C1 * weight + 32) / 64;
|
||||
const u32 E = (C & 0xF800) >> 11;
|
||||
const u32 M = C & 0x7FF;
|
||||
u32 Mt;
|
||||
if (M < 512) {
|
||||
Mt = 3 * M;
|
||||
} else if (M >= 1536) {
|
||||
Mt = 5 * M - 2048;
|
||||
} else {
|
||||
Mt = 4 * M - 512;
|
||||
}
|
||||
const u32 Cf = (E << 10) + (Mt >> 3);
|
||||
// +Inf/NaN clamps to the largest finite FP16 value (0x7BFF).
|
||||
const u16 half_bits = (Cf >= 0x7C00) ? u16{0x7BFF} : static_cast<u16>(Cf);
|
||||
p.Component(c) = HalfToClampedByte(half_bits);
|
||||
u32 C = (C0 * (64 - weight) + C1 * weight + 32) / 64;
|
||||
if (C == 65535) {
|
||||
p.Component(c) = 255;
|
||||
} else {
|
||||
C0 = ReplicateByteTo16(C0);
|
||||
C1 = ReplicateByteTo16(C1);
|
||||
const u32 C = (C0 * (64 - weight) + C1 * weight + 32) / 64;
|
||||
if (C == 65535) {
|
||||
p.Component(c) = 255;
|
||||
} else {
|
||||
double Cf = static_cast<double>(C);
|
||||
p.Component(c) = static_cast<u16>(255.0 * (Cf / 65536.0) + 0.5);
|
||||
}
|
||||
double Cf = static_cast<double>(C);
|
||||
p.Component(c) = static_cast<u16>(255.0 * (Cf / 65536.0) + 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -95,12 +95,6 @@ constexpr std::array VK_FORMAT_A4B4G4R4_UNORM_PACK16{
|
||||
VK_FORMAT_UNDEFINED,
|
||||
};
|
||||
|
||||
constexpr std::array B10G11R11_UFLOAT_PACK32{
|
||||
VK_FORMAT_R16G16B16A16_SFLOAT,
|
||||
VK_FORMAT_A8B8G8R8_SRGB_PACK32,
|
||||
VK_FORMAT_UNDEFINED,
|
||||
};
|
||||
|
||||
} // namespace Alternatives
|
||||
|
||||
template <typename T>
|
||||
@@ -133,8 +127,6 @@ constexpr const VkFormat* GetFormatAlternatives(VkFormat format) {
|
||||
return Alternatives::VK_FORMAT_R32G32B32_SFLOAT.data();
|
||||
case VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT:
|
||||
return Alternatives::VK_FORMAT_A4B4G4R4_UNORM_PACK16.data();
|
||||
case VK_FORMAT_B10G11R11_UFLOAT_PACK32:
|
||||
return Alternatives::B10G11R11_UFLOAT_PACK32.data();
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
@@ -300,10 +292,6 @@ ankerl::unordered_dense::map<VkFormat, VkFormatProperties> GetFormatProperties(v
|
||||
VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK,
|
||||
VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK,
|
||||
VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK,
|
||||
VK_FORMAT_EAC_R11_UNORM_BLOCK,
|
||||
VK_FORMAT_EAC_R11_SNORM_BLOCK,
|
||||
VK_FORMAT_EAC_R11G11_UNORM_BLOCK,
|
||||
VK_FORMAT_EAC_R11G11_SNORM_BLOCK,
|
||||
};
|
||||
ankerl::unordered_dense::map<VkFormat, VkFormatProperties> format_properties;
|
||||
for (const auto format : formats) {
|
||||
@@ -504,20 +492,18 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
CollectToolingInfo();
|
||||
|
||||
if (is_qualcomm) {
|
||||
LOG_WARNING(Render_Vulkan, "Qualcomm drivers require scaled vertex format emulation");
|
||||
LOG_WARNING(Render_Vulkan,
|
||||
"Qualcomm drivers require scaled vertex format emulation");
|
||||
must_emulate_scaled_formats = true;
|
||||
LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken custom border color.");
|
||||
RemoveExtensionFeature(extensions.custom_border_color, features.custom_border_color,
|
||||
VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME);
|
||||
LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken border color swizzle.");
|
||||
RemoveExtensionFeature(extensions.border_color_swizzle, features.border_color_swizzle,
|
||||
VK_EXT_BORDER_COLOR_SWIZZLE_EXTENSION_NAME);
|
||||
LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken color write enable.");
|
||||
RemoveExtensionFeature(extensions.color_write_enable, features.color_write_enable,
|
||||
VK_EXT_COLOR_WRITE_ENABLE_EXTENSION_NAME);
|
||||
LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken shader float controls.");
|
||||
LOG_WARNING(Render_Vulkan,
|
||||
"Qualcomm drivers have broken provoking vertex");
|
||||
RemoveExtension(extensions.provoking_vertex, VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME);
|
||||
LOG_WARNING(Render_Vulkan,
|
||||
"Qualcomm drivers have slow push descriptor implementation");
|
||||
RemoveExtension(extensions.push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
|
||||
LOG_WARNING(Render_Vulkan,
|
||||
"Disabling shader float controls and 64-bit integer features on Qualcomm proprietary drivers");
|
||||
RemoveExtension(extensions.shader_float_controls, VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME);
|
||||
LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken shader atomic int64.");
|
||||
RemoveExtensionFeature(extensions.shader_atomic_int64, features.shader_atomic_int64,
|
||||
VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME);
|
||||
features.shader_atomic_int64.shaderBufferInt64Atomics = false;
|
||||
@@ -575,6 +561,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
features.shader_float16_int8.shaderFloat16 = false;
|
||||
}
|
||||
|
||||
// Mali/ NVIDIA proprietary drivers: Shader stencil export not supported
|
||||
// Use hardware depth/stencil blits instead when available
|
||||
if (!extensions.shader_stencil_export) {
|
||||
LOG_INFO(Render_Vulkan,
|
||||
@@ -670,6 +657,14 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
}
|
||||
|
||||
const auto dyna_state = Settings::values.dyna_state.GetValue();
|
||||
|
||||
// Base dynamic states (VIEWPORT, SCISSOR, DEPTH_BIAS, etc.) are ALWAYS active in vk_graphics_pipeline.cpp
|
||||
// This slider controls EXTENDED dynamic states with accumulative levels per Vulkan specs:
|
||||
// Level 0 = Core Dynamic States only (Vulkan 1.0)
|
||||
// Level 1 = Core + VK_EXT_extended_dynamic_state
|
||||
// Level 2 = Core + VK_EXT_extended_dynamic_state + VK_EXT_extended_dynamic_state2
|
||||
// Level 3 = Core + VK_EXT_extended_dynamic_state + VK_EXT_extended_dynamic_state2 + VK_EXT_extended_dynamic_state3
|
||||
|
||||
switch (dyna_state) {
|
||||
case Settings::ExtendedDynamicState::Disabled:
|
||||
// Level 0: Disable all extended dynamic state extensions
|
||||
@@ -704,7 +699,8 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
break;
|
||||
}
|
||||
|
||||
// VK_EXT_vertex_input_dynamic_state
|
||||
// VK_EXT_vertex_input_dynamic_state is independent from EDS
|
||||
// It can be enabled even without extended_dynamic_state
|
||||
if (!Settings::values.vertex_input_dynamic_state.GetValue()) {
|
||||
RemoveExtensionFeature(extensions.vertex_input_dynamic_state, features.vertex_input_dynamic_state, VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
|
||||
}
|
||||
@@ -1174,11 +1170,6 @@ bool Device::GetSuitability(bool requires_swapchain) {
|
||||
}
|
||||
|
||||
void Device::RemoveUnsuitableExtensions() {
|
||||
// VK_EXT_color_write_enable
|
||||
extensions.color_write_enable = features.color_write_enable.colorWriteEnable;
|
||||
RemoveExtensionFeatureIfUnsuitable(extensions.color_write_enable, features.color_write_enable,
|
||||
VK_EXT_COLOR_WRITE_ENABLE_EXTENSION_NAME);
|
||||
|
||||
// VK_EXT_custom_border_color
|
||||
if (extensions.custom_border_color) {
|
||||
extensions.custom_border_color =
|
||||
@@ -1188,17 +1179,6 @@ void Device::RemoveUnsuitableExtensions() {
|
||||
RemoveExtensionFeatureIfUnsuitable(extensions.custom_border_color, features.custom_border_color,
|
||||
VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME);
|
||||
|
||||
// VK_EXT_border_color_swizzle
|
||||
if (extensions.border_color_swizzle) {
|
||||
extensions.border_color_swizzle =
|
||||
extensions.custom_border_color &&
|
||||
features.border_color_swizzle.borderColorSwizzle &&
|
||||
features.border_color_swizzle.borderColorSwizzleFromImage;
|
||||
}
|
||||
RemoveExtensionFeatureIfUnsuitable(extensions.border_color_swizzle,
|
||||
features.border_color_swizzle,
|
||||
VK_EXT_BORDER_COLOR_SWIZZLE_EXTENSION_NAME);
|
||||
|
||||
// VK_EXT_depth_bias_control
|
||||
extensions.depth_bias_control =
|
||||
features.depth_bias_control.depthBiasControl &&
|
||||
@@ -1395,11 +1375,6 @@ void Device::RemoveUnsuitableExtensions() {
|
||||
// VK_KHR_maintenance8
|
||||
extensions.maintenance8 = loaded_extensions.contains(VK_KHR_MAINTENANCE_8_EXTENSION_NAME);
|
||||
RemoveExtensionIfUnsuitable(extensions.maintenance8, VK_KHR_MAINTENANCE_8_EXTENSION_NAME);
|
||||
|
||||
// VK_KHR_synchronization2
|
||||
extensions.synchronization2 = features.synchronization2.synchronization2;
|
||||
RemoveExtensionFeatureIfUnsuitable(extensions.synchronization2, features.synchronization2,
|
||||
VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME);
|
||||
}
|
||||
|
||||
void Device::SetupFamilies(VkSurfaceKHR surface) {
|
||||
|
||||
@@ -43,15 +43,12 @@ VK_DEFINE_HANDLE(VmaAllocator)
|
||||
FEATURE(EXT, ShaderDemoteToHelperInvocation, SHADER_DEMOTE_TO_HELPER_INVOCATION, \
|
||||
shader_demote_to_helper_invocation) \
|
||||
FEATURE(EXT, SubgroupSizeControl, SUBGROUP_SIZE_CONTROL, subgroup_size_control) \
|
||||
FEATURE(KHR, Maintenance4, MAINTENANCE_4, maintenance4) \
|
||||
FEATURE(KHR, Synchronization2, SYNCHRONIZATION_2, synchronization2)
|
||||
FEATURE(KHR, Maintenance4, MAINTENANCE_4, maintenance4)
|
||||
|
||||
#define FOR_EACH_VK_FEATURE_1_4(FEATURE)
|
||||
|
||||
// Define all features which may be used by the implementation and require an extension here.
|
||||
#define FOR_EACH_VK_FEATURE_EXT(FEATURE) \
|
||||
FEATURE(EXT, BorderColorSwizzle, BORDER_COLOR_SWIZZLE, border_color_swizzle) \
|
||||
FEATURE(EXT, ColorWriteEnable, COLOR_WRITE_ENABLE, color_write_enable) \
|
||||
FEATURE(EXT, CustomBorderColor, CUSTOM_BORDER_COLOR, custom_border_color) \
|
||||
FEATURE(EXT, DepthBiasControl, DEPTH_BIAS_CONTROL, depth_bias_control) \
|
||||
FEATURE(EXT, DepthClipControl, DEPTH_CLIP_CONTROL, depth_clip_control) \
|
||||
@@ -72,9 +69,7 @@ VK_DEFINE_HANDLE(VmaAllocator)
|
||||
FEATURE(KHR, PipelineExecutableProperties, PIPELINE_EXECUTABLE_PROPERTIES, \
|
||||
pipeline_executable_properties) \
|
||||
FEATURE(KHR, WorkgroupMemoryExplicitLayout, WORKGROUP_MEMORY_EXPLICIT_LAYOUT, \
|
||||
workgroup_memory_explicit_layout) \
|
||||
FEATURE(EXT, TextureCompressionASTCHDR, TEXTURE_COMPRESSION_ASTC_HDR, \
|
||||
texture_compression_astc_hdr)
|
||||
workgroup_memory_explicit_layout)
|
||||
|
||||
|
||||
// Define miscellaneous extensions which may be used by the implementation here.
|
||||
@@ -185,7 +180,6 @@ VK_DEFINE_HANDLE(VmaAllocator)
|
||||
FEATURE_NAME(robustness2, nullDescriptor) \
|
||||
FEATURE_NAME(shader_float16_int8, shaderFloat16) \
|
||||
FEATURE_NAME(shader_float16_int8, shaderInt8) \
|
||||
FEATURE_NAME(synchronization2, synchronization2) \
|
||||
FEATURE_NAME(timeline_semaphore, timelineSemaphore) \
|
||||
FEATURE_NAME(transform_feedback, transformFeedback) \
|
||||
FEATURE_NAME(uniform_buffer_standard_layout, uniformBufferStandardLayout) \
|
||||
@@ -350,10 +344,9 @@ FN_MAX_LIMIT_LIST
|
||||
return properties.float_controls;
|
||||
}
|
||||
|
||||
/// Returns true if ASTC is natively supported, including HDR-profile (non-LDR-only)
|
||||
/// Returns true if ASTC is natively supported.
|
||||
bool IsOptimalAstcSupported() const {
|
||||
return features.features.textureCompressionASTC_LDR &&
|
||||
features.texture_compression_astc_hdr.textureCompressionASTC_HDR;
|
||||
return features.features.textureCompressionASTC_LDR;
|
||||
}
|
||||
|
||||
/// Returns true if BCn is natively supported.
|
||||
@@ -390,26 +383,6 @@ FN_MAX_LIMIT_LIST
|
||||
return features.shader_float16_int8.shaderInt8;
|
||||
}
|
||||
|
||||
/// Returns true if the device allows 8-bit integer members in uniform/storage buffers.
|
||||
bool IsUniformAndStorageBuffer8BitAccessSupported() const {
|
||||
return features.bit8_storage.uniformAndStorageBuffer8BitAccess;
|
||||
}
|
||||
|
||||
/// Returns true if the device allows 16-bit integer members in uniform/storage buffers.
|
||||
bool IsUniformAndStorageBuffer16BitAccessSupported() const {
|
||||
return features.bit16_storage.uniformAndStorageBuffer16BitAccess;
|
||||
}
|
||||
|
||||
/// Returns true if the device supports reading 8-bit values from a storage buffer.
|
||||
bool IsStorageBuffer8BitAccessSupported() const {
|
||||
return features.bit8_storage.storageBuffer8BitAccess;
|
||||
}
|
||||
|
||||
/// Returns true if the device supports reading 16-bit values from a storage buffer.
|
||||
bool IsStorageBuffer16BitAccessSupported() const {
|
||||
return features.bit16_storage.storageBuffer16BitAccess;
|
||||
}
|
||||
|
||||
/// Returns true if the device supports binding multisample images as storage images.
|
||||
bool IsStorageImageMultisampleSupported() const {
|
||||
return features.features.shaderStorageImageMultisample;
|
||||
@@ -430,10 +403,6 @@ FN_MAX_LIMIT_LIST
|
||||
return properties.subgroup_properties.supportedOperations & feature;
|
||||
}
|
||||
|
||||
VkShaderStageFlags GetSubgroupSupportedStages() const {
|
||||
return properties.subgroup_properties.supportedStages;
|
||||
}
|
||||
|
||||
/// Returns the maximum number of push descriptors.
|
||||
u32 MaxPushDescriptors() const {
|
||||
return properties.push_descriptor.maxPushDescriptors;
|
||||
@@ -540,6 +509,7 @@ FN_MAX_LIMIT_LIST
|
||||
}
|
||||
|
||||
/// Returns true if the device supports VK_EXT_shader_stencil_export.
|
||||
/// Note: Most Mali/NVIDIA drivers don't support this. Use hardware blits as fallback.
|
||||
bool IsExtShaderStencilExportSupported() const {
|
||||
return extensions.shader_stencil_export;
|
||||
}
|
||||
@@ -576,11 +546,6 @@ FN_MAX_LIMIT_LIST
|
||||
return extensions.subgroup_size_control;
|
||||
}
|
||||
|
||||
/// Returns true if vkResetQueryPool (host-side query reset) is supported.
|
||||
bool IsHostQueryResetSupported() const {
|
||||
return features.host_query_reset.hostQueryReset != VK_FALSE;
|
||||
}
|
||||
|
||||
/// Returns true if the device supports VK_EXT_transform_feedback.
|
||||
bool IsExtTransformFeedbackSupported() const {
|
||||
return extensions.transform_feedback;
|
||||
@@ -617,21 +582,6 @@ FN_MAX_LIMIT_LIST
|
||||
return features.custom_border_color.customBorderColorWithoutFormat;
|
||||
}
|
||||
|
||||
/// Returns true if the device supports VK_EXT_color_write_enable.
|
||||
bool IsExtColorWriteEnableSupported() const {
|
||||
return extensions.color_write_enable;
|
||||
}
|
||||
|
||||
/// Returns true if the device supports VK_EXT_border_color_swizzle.
|
||||
bool IsExtBorderColorSwizzleSupported() const {
|
||||
return extensions.border_color_swizzle;
|
||||
}
|
||||
|
||||
/// Returns true if borderColorSwizzleFromImage is available.
|
||||
bool IsBorderColorSwizzleFromImageSupported() const {
|
||||
return features.border_color_swizzle.borderColorSwizzleFromImage;
|
||||
}
|
||||
|
||||
/// Returns true if the device supports VK_EXT_extended_dynamic_state.
|
||||
bool IsExtExtendedDynamicStateSupported() const {
|
||||
return extensions.extended_dynamic_state;
|
||||
@@ -772,11 +722,6 @@ FN_MAX_LIMIT_LIST
|
||||
|
||||
bool HasTimelineSemaphore() const;
|
||||
|
||||
/// Returns true if the device supports VK_KHR_synchronization2.
|
||||
bool HasSynchronization2() const {
|
||||
return extensions.synchronization2;
|
||||
}
|
||||
|
||||
/// Returns the minimum supported version of SPIR-V.
|
||||
u32 SupportedSpirvVersion() const {
|
||||
if (instance_version >= VK_API_VERSION_1_3) {
|
||||
|
||||
@@ -123,7 +123,6 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
|
||||
X(vkCmdEndDebugUtilsLabelEXT);
|
||||
X(vkCmdFillBuffer);
|
||||
X(vkCmdPipelineBarrier);
|
||||
X(vkCmdPipelineBarrier2);
|
||||
X(vkCmdPushConstants);
|
||||
X(vkCmdPushDescriptorSetWithTemplateKHR);
|
||||
X(vkCmdSetBlendConstants);
|
||||
@@ -162,10 +161,8 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
|
||||
X(vkCmdSetStencilTestEnableEXT);
|
||||
X(vkCmdSetVertexInputEXT);
|
||||
X(vkCmdSetColorWriteMaskEXT);
|
||||
X(vkCmdSetColorWriteEnableEXT);
|
||||
X(vkCmdSetColorBlendEnableEXT);
|
||||
X(vkCmdSetColorBlendEquationEXT);
|
||||
X(vkCmdResetQueryPool);
|
||||
X(vkCmdResolveImage);
|
||||
X(vkCreateBuffer);
|
||||
X(vkCreateBufferView);
|
||||
@@ -229,7 +226,6 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
|
||||
X(vkGetSemaphoreCounterValue);
|
||||
X(vkMapMemory);
|
||||
X(vkQueueSubmit);
|
||||
X(vkQueueSubmit2);
|
||||
X(vkResetFences);
|
||||
X(vkResetQueryPool);
|
||||
X(vkSetDebugUtilsObjectNameEXT);
|
||||
@@ -256,14 +252,6 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
|
||||
Proc(dld.vkCmdDrawIndirectCount, dld, "vkCmdDrawIndirectCountKHR", device);
|
||||
Proc(dld.vkCmdDrawIndexedIndirectCount, dld, "vkCmdDrawIndexedIndirectCountKHR", device);
|
||||
}
|
||||
|
||||
// Synchronization2 is core in Vulkan 1.3, otherwise requires VK_KHR_synchronization2
|
||||
if (!dld.vkCmdPipelineBarrier2) {
|
||||
Proc(dld.vkCmdPipelineBarrier2, dld, "vkCmdPipelineBarrier2KHR", device);
|
||||
}
|
||||
if (!dld.vkQueueSubmit2) {
|
||||
Proc(dld.vkQueueSubmit2, dld, "vkQueueSubmit2KHR", device);
|
||||
}
|
||||
#undef X
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <exception>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
@@ -238,10 +237,8 @@ struct DeviceDispatch : InstanceDispatch {
|
||||
PFN_vkCmdEndTransformFeedbackEXT vkCmdEndTransformFeedbackEXT{};
|
||||
PFN_vkCmdFillBuffer vkCmdFillBuffer{};
|
||||
PFN_vkCmdPipelineBarrier vkCmdPipelineBarrier{};
|
||||
PFN_vkCmdPipelineBarrier2 vkCmdPipelineBarrier2{};
|
||||
PFN_vkCmdPushConstants vkCmdPushConstants{};
|
||||
PFN_vkCmdPushDescriptorSetWithTemplateKHR vkCmdPushDescriptorSetWithTemplateKHR{};
|
||||
PFN_vkCmdResetQueryPool vkCmdResetQueryPool{};
|
||||
PFN_vkCmdResolveImage vkCmdResolveImage{};
|
||||
PFN_vkCmdSetBlendConstants vkCmdSetBlendConstants{};
|
||||
PFN_vkCmdSetCullModeEXT vkCmdSetCullModeEXT{};
|
||||
@@ -278,7 +275,6 @@ struct DeviceDispatch : InstanceDispatch {
|
||||
PFN_vkCmdSetVertexInputEXT vkCmdSetVertexInputEXT{};
|
||||
PFN_vkCmdSetViewport vkCmdSetViewport{};
|
||||
PFN_vkCmdSetColorWriteMaskEXT vkCmdSetColorWriteMaskEXT{};
|
||||
PFN_vkCmdSetColorWriteEnableEXT vkCmdSetColorWriteEnableEXT{};
|
||||
PFN_vkCmdSetColorBlendEnableEXT vkCmdSetColorBlendEnableEXT{};
|
||||
PFN_vkCmdSetColorBlendEquationEXT vkCmdSetColorBlendEquationEXT{};
|
||||
PFN_vkCmdWaitEvents vkCmdWaitEvents{};
|
||||
@@ -344,7 +340,6 @@ struct DeviceDispatch : InstanceDispatch {
|
||||
PFN_vkGetSemaphoreCounterValue vkGetSemaphoreCounterValue{};
|
||||
PFN_vkMapMemory vkMapMemory{};
|
||||
PFN_vkQueueSubmit vkQueueSubmit{};
|
||||
PFN_vkQueueSubmit2 vkQueueSubmit2{};
|
||||
PFN_vkResetFences vkResetFences{};
|
||||
PFN_vkResetQueryPool vkResetQueryPool{};
|
||||
PFN_vkSetDebugUtilsObjectNameEXT vkSetDebugUtilsObjectNameEXT{};
|
||||
@@ -824,13 +819,6 @@ public:
|
||||
return dld->vkQueueSubmit(queue, submit_infos.size(), submit_infos.data(), fence);
|
||||
}
|
||||
|
||||
/// Submits using VK_KHR_synchronization2 / Vulkan 1.3 vkQueueSubmit2.
|
||||
/// Only valid to call when the device dispatch table has vkQueueSubmit2 loaded.
|
||||
VkResult Submit2(Span<VkSubmitInfo2> submit_infos,
|
||||
VkFence fence = VK_NULL_HANDLE) const noexcept {
|
||||
return dld->vkQueueSubmit2(queue, submit_infos.size(), submit_infos.data(), fence);
|
||||
}
|
||||
|
||||
VkResult Present(const VkPresentInfoKHR& present_info) const noexcept {
|
||||
return dld->vkQueuePresentKHR(queue, &present_info);
|
||||
}
|
||||
@@ -1192,10 +1180,6 @@ public:
|
||||
dld->vkCmdEndQuery(handle, query_pool, query);
|
||||
}
|
||||
|
||||
void ResetQueryPool(VkQueryPool query_pool, u32 first, u32 count) const noexcept {
|
||||
dld->vkCmdResetQueryPool(handle, query_pool, first, count);
|
||||
}
|
||||
|
||||
void BindDescriptorSets(VkPipelineBindPoint bind_point, VkPipelineLayout layout, u32 first,
|
||||
Span<VkDescriptorSet> sets, Span<u32> dynamic_offsets) const noexcept {
|
||||
dld->vkCmdBindDescriptorSets(handle, bind_point, layout, first, sets.size(), sets.data(),
|
||||
@@ -1303,74 +1287,6 @@ public:
|
||||
VkDependencyFlags dependency_flags, Span<VkMemoryBarrier> memory_barriers,
|
||||
Span<VkBufferMemoryBarrier> buffer_barriers,
|
||||
Span<VkImageMemoryBarrier> image_barriers) const noexcept {
|
||||
// Legacy VkPipelineStageFlagBits/VkAccessFlagBits are bit-compatible with their
|
||||
// Synchronization2 *2 counterparts, so barriers can be widened without a lookup table.
|
||||
static constexpr u32 MaxBarriers = 16;
|
||||
if (dld->vkCmdPipelineBarrier2 && memory_barriers.size() <= MaxBarriers &&
|
||||
buffer_barriers.size() <= MaxBarriers && image_barriers.size() <= MaxBarriers) {
|
||||
const auto src_stage_mask2 = static_cast<VkPipelineStageFlags2>(src_stage_mask);
|
||||
const auto dst_stage_mask2 = static_cast<VkPipelineStageFlags2>(dst_stage_mask);
|
||||
|
||||
std::array<VkMemoryBarrier2, MaxBarriers> memory_barriers2;
|
||||
for (u32 i = 0; i < memory_barriers.size(); ++i) {
|
||||
memory_barriers2[i] = VkMemoryBarrier2{
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2,
|
||||
.pNext = nullptr,
|
||||
.srcStageMask = src_stage_mask2,
|
||||
.srcAccessMask = static_cast<VkAccessFlags2>(memory_barriers[i].srcAccessMask),
|
||||
.dstStageMask = dst_stage_mask2,
|
||||
.dstAccessMask = static_cast<VkAccessFlags2>(memory_barriers[i].dstAccessMask),
|
||||
};
|
||||
}
|
||||
std::array<VkBufferMemoryBarrier2, MaxBarriers> buffer_barriers2;
|
||||
for (u32 i = 0; i < buffer_barriers.size(); ++i) {
|
||||
const auto& barrier = buffer_barriers[i];
|
||||
buffer_barriers2[i] = VkBufferMemoryBarrier2{
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2,
|
||||
.pNext = nullptr,
|
||||
.srcStageMask = src_stage_mask2,
|
||||
.srcAccessMask = static_cast<VkAccessFlags2>(barrier.srcAccessMask),
|
||||
.dstStageMask = dst_stage_mask2,
|
||||
.dstAccessMask = static_cast<VkAccessFlags2>(barrier.dstAccessMask),
|
||||
.srcQueueFamilyIndex = barrier.srcQueueFamilyIndex,
|
||||
.dstQueueFamilyIndex = barrier.dstQueueFamilyIndex,
|
||||
.buffer = barrier.buffer,
|
||||
.offset = barrier.offset,
|
||||
.size = barrier.size,
|
||||
};
|
||||
}
|
||||
std::array<VkImageMemoryBarrier2, MaxBarriers> image_barriers2;
|
||||
for (u32 i = 0; i < image_barriers.size(); ++i) {
|
||||
const auto& barrier = image_barriers[i];
|
||||
image_barriers2[i] = VkImageMemoryBarrier2{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
|
||||
.pNext = nullptr,
|
||||
.srcStageMask = src_stage_mask2,
|
||||
.srcAccessMask = static_cast<VkAccessFlags2>(barrier.srcAccessMask),
|
||||
.dstStageMask = dst_stage_mask2,
|
||||
.dstAccessMask = static_cast<VkAccessFlags2>(barrier.dstAccessMask),
|
||||
.oldLayout = barrier.oldLayout,
|
||||
.newLayout = barrier.newLayout,
|
||||
.srcQueueFamilyIndex = barrier.srcQueueFamilyIndex,
|
||||
.dstQueueFamilyIndex = barrier.dstQueueFamilyIndex,
|
||||
.image = barrier.image,
|
||||
.subresourceRange = barrier.subresourceRange,
|
||||
};
|
||||
}
|
||||
const VkDependencyInfo dependency_info{
|
||||
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
|
||||
.pNext = nullptr,
|
||||
.dependencyFlags = dependency_flags,
|
||||
.memoryBarrierCount = memory_barriers.size(),
|
||||
.pMemoryBarriers = memory_barriers2.data(),
|
||||
.bufferMemoryBarrierCount = buffer_barriers.size(),
|
||||
.pBufferMemoryBarriers = buffer_barriers2.data(),
|
||||
.imageMemoryBarrierCount = image_barriers.size(),
|
||||
.pImageMemoryBarriers = image_barriers2.data(),
|
||||
};
|
||||
dld->vkCmdPipelineBarrier2(handle, &dependency_info);
|
||||
return;
|
||||
}
|
||||
dld->vkCmdPipelineBarrier(handle, src_stage_mask, dst_stage_mask, dependency_flags,
|
||||
memory_barriers.size(), memory_barriers.data(),
|
||||
buffer_barriers.size(), buffer_barriers.data(),
|
||||
@@ -1595,10 +1511,6 @@ public:
|
||||
dld->vkCmdSetColorWriteMaskEXT(handle, first, masks.size(), masks.data());
|
||||
}
|
||||
|
||||
void SetColorWriteEnableEXT(Span<VkBool32> enables) const noexcept {
|
||||
dld->vkCmdSetColorWriteEnableEXT(handle, enables.size(), enables.data());
|
||||
}
|
||||
|
||||
void SetColorBlendEnableEXT(u32 first, Span<VkBool32> enables) const noexcept {
|
||||
dld->vkCmdSetColorBlendEnableEXT(handle, first, enables.size(), enables.data());
|
||||
}
|
||||
|
||||
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
|
||||
@@ -1,47 +0,0 @@
|
||||
#!/bin/sh -e
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright 2026 crueter
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: cpmutil.sh package download [-a|--all] [PACKAGE]...
|
||||
|
||||
Get the download URL 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
|
||||
|
||||
if [ "$CI" = "true" ]; then
|
||||
echo "-- $PACKAGE: https://$GIT_HOST/$REPO"
|
||||
else
|
||||
echo -- "$PACKAGE: $DOWNLOAD"
|
||||
fi
|
||||
done
|
||||
@@ -1,44 +0,0 @@
|
||||
#!/bin/sh -e
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright 2026 crueter
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
. "$SCRIPTS/util/fetch.sh"
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
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.
|
||||
|
||||
EOF
|
||||
|
||||
exit 0
|
||||
}
|
||||
|
||||
while :; do
|
||||
case "$1" in
|
||||
-h | --help) usage ;;
|
||||
-a | --all) ALL=1 ;;
|
||||
"$0") break ;;
|
||||
"") break ;;
|
||||
*) packages="$packages $1" ;;
|
||||
esac
|
||||
|
||||
shift
|
||||
done
|
||||
|
||||
[ "$ALL" != 1 ] || packages="${LIBS:-$packages}"
|
||||
[ -n "$packages" ] || usage
|
||||
|
||||
for PACKAGE in $packages; do
|
||||
unset JSON
|
||||
export PACKAGE
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
. "$SCRIPTS"/vars.sh
|
||||
|
||||
fetch_package
|
||||
done
|
||||
@@ -1,91 +0,0 @@
|
||||
#!/bin/sh -e
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright 2026 crueter
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
RETURN=0
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: cpmutil.sh package hash [-n|--dry-run] [-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:
|
||||
-n, --dry-run Don't update the package's hash if it's a mismatch
|
||||
-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.
|
||||
|
||||
EOF
|
||||
|
||||
exit $RETURN
|
||||
}
|
||||
|
||||
while :; do
|
||||
case "$1" in
|
||||
-[a-z]*)
|
||||
opt=$(printf '%s' "$1" | sed 's/^-//')
|
||||
while [ -n "$opt" ]; do
|
||||
# cut out first char from the optstring
|
||||
char=$(echo "$opt" | cut -c1)
|
||||
opt=$(echo "$opt" | cut -c2-)
|
||||
|
||||
case "$char" in
|
||||
a) ALL=1 ;;
|
||||
n) DRY=1 ;;
|
||||
h) usage ;;
|
||||
*) die "Invalid option -$char" ;;
|
||||
esac
|
||||
done
|
||||
;;
|
||||
--dry-run) DRY=1 ;;
|
||||
--all) ALL=1 ;;
|
||||
--help) usage ;;
|
||||
"$0") break ;;
|
||||
"") break ;;
|
||||
*) packages="$packages $1" ;;
|
||||
esac
|
||||
|
||||
shift
|
||||
done
|
||||
|
||||
[ "$ALL" != 1 ] || packages="${LIBS:-$packages}"
|
||||
[ "$DRY" = 1 ] && UPDATE=false || UPDATE=true
|
||||
[ -n "$packages" ] || usage
|
||||
|
||||
export UPDATE
|
||||
|
||||
for pkg in $packages; do
|
||||
unset JSON
|
||||
echo "-- Package $pkg"
|
||||
|
||||
export PACKAGE="$pkg"
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
. "$SCRIPTS"/vars.sh
|
||||
|
||||
[ "$CI" = null ] || continue
|
||||
|
||||
ACTUAL=$("$SCRIPTS"/util/url-hash.sh "$DOWNLOAD")
|
||||
|
||||
if [ "$ACTUAL" != "$HASH" ] && [ "$QUIET" != true ]; then
|
||||
echo "-- * Expected $HASH"
|
||||
echo "-- * Got $ACTUAL"
|
||||
|
||||
if [ "$UPDATE" != "true" ]; then
|
||||
RETURN=1
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$UPDATE" = "true" ] && [ "$ACTUAL" != "$HASH" ]; then
|
||||
NEW_JSON=$(echo "$JSON" | jq ".hash = \"$ACTUAL\"")
|
||||
|
||||
"$SCRIPTS"/util/replace.sh "$PACKAGE" "$NEW_JSON"
|
||||
fi
|
||||
done
|
||||
|
||||
exit $RETURN
|
||||
+6
-163
@@ -4,170 +4,13 @@
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
. "$SCRIPTS"/../common.sh
|
||||
|
||||
RETURN=0
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: cpmutil.sh package patch [PACKAGE]
|
||||
|
||||
Create an in-tree patch for the specified package.
|
||||
|
||||
EOF
|
||||
|
||||
exit "$RETURN"
|
||||
}
|
||||
|
||||
die() {
|
||||
echo "-- $*" >&2
|
||||
RETURN=1 usage
|
||||
}
|
||||
|
||||
while :; do
|
||||
case "$1" in
|
||||
-h | --help) usage ;;
|
||||
"$0") break ;;
|
||||
"") break ;;
|
||||
*)
|
||||
if [ -n "$PACKAGE" ]; then
|
||||
die "You may only specify one package."
|
||||
else
|
||||
PACKAGE="$1"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
shift
|
||||
done
|
||||
|
||||
[ -n "$PACKAGE" ] || usage
|
||||
|
||||
unset JSON
|
||||
export PACKAGE
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
. "$SCRIPTS"/vars.sh
|
||||
|
||||
if [ "$CI" = true ]; then
|
||||
die "CI packages do not support in-tree patching"
|
||||
fi
|
||||
|
||||
patch_dir="$PWD/.patch/$PACKAGE"
|
||||
TMP=$(make_temp_dir)
|
||||
|
||||
tmp_package="${TMP}/${LOWER_PACKAGE}/${KEY}"
|
||||
|
||||
# First fetch the package...
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
. "$SCRIPTS"/util/fetch.sh
|
||||
|
||||
local_dir="$CPM_SOURCE_CACHE/$LOWER_PACKAGE/$KEY"
|
||||
|
||||
if [ ! -d "$local_dir" ]; then
|
||||
echo "-- $PACKAGE_NAME is not fetched locally" >&2
|
||||
printf -- "-- Enter a patch description: "
|
||||
if ! read -r description; then
|
||||
echo "" >&2
|
||||
echo "-- Patch cancelled" >&2
|
||||
rm -rf "$TMP"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
src_dir="$tmp_package/$LOWER_PACKAGE/$KEY"
|
||||
|
||||
CPM_SOURCE_CACHE="$tmp_package" fetch_package
|
||||
|
||||
mkdir -p "$patch_dir"
|
||||
|
||||
existing=$(find "$patch_dir" -maxdepth 1 -type f -name '[0-9][0-9][0-9][0-9]-*.patch' 2>/dev/null | sort | tail -1)
|
||||
if [ -n "$existing" ]; then
|
||||
last_num=$(basename "$existing" | cut -c1-4)
|
||||
last_num=$(echo "$last_num" | sed 's/^0*//')
|
||||
last_num="${last_num:-0}"
|
||||
next_num=$(printf "%04d" $((last_num + 1)))
|
||||
else
|
||||
next_num="0001"
|
||||
fi
|
||||
|
||||
echo "-- Creating patch for $PACKAGE..."
|
||||
|
||||
orig_dir="$PWD"
|
||||
if cmd_exists git; then
|
||||
cd "$src_dir"
|
||||
|
||||
git init >/dev/null 2>&1
|
||||
git add -A >/dev/null 2>&1
|
||||
git commit -m "base" >/dev/null 2>&1
|
||||
|
||||
git --work-tree="$local_dir" add -A #>/dev/null 2>&1
|
||||
|
||||
if git diff --cached --quiet; then
|
||||
echo "-- No differences found between local and source"
|
||||
cd "$orig_dir"
|
||||
rm -rf "$TMP"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if ! git commit -v; then
|
||||
echo "-- Patch cancelled" >&2
|
||||
cd "$orig_dir"
|
||||
rm -rf "$TMP"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
description=$(git log -1 --format="%s")
|
||||
|
||||
git format-patch -1 HEAD --stdout >"$patch_dir/tmp.patch"
|
||||
rm -rf .git
|
||||
|
||||
cd "$orig_dir"
|
||||
unset orig_dir
|
||||
else
|
||||
if diff -ruN "$src_dir" "$local_dir" >/dev/null 2>&1; then
|
||||
echo "-- No differences found between local and source"
|
||||
rm -rf "$TMP"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
printf -- "-- Enter a patch description: "
|
||||
if ! read -r description; then
|
||||
echo "" >&2
|
||||
echo "-- Patch cancelled" >&2
|
||||
rm -rf "$TMP"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$TMP/patchdir/a" "$TMP/patchdir/b"
|
||||
cp -r "$src_dir/." "$TMP/patchdir/a/"
|
||||
cp -r "$local_dir/." "$TMP/patchdir/b/"
|
||||
cd "$TMP/patchdir"
|
||||
{
|
||||
echo "Subject: [PATCH] $description" | fold -s -w 80
|
||||
echo "---"
|
||||
diff -ruN a b || true
|
||||
} >"$patch_dir/tmp.patch"
|
||||
|
||||
cd "$orig_dir"
|
||||
unset orig_dir
|
||||
fi
|
||||
|
||||
rm -rf "$TMP"
|
||||
|
||||
if [ ! -s "$patch_dir/tmp.patch" ]; then
|
||||
rm -f "$patch_dir/tmp.patch"
|
||||
echo "-- No differences found between local and source for $PACKAGE"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
name_part=$(printf "%s" "$description" | sed 's/^[0-9]\{4\}-//' | sed 's/ /-/g; s/[^a-zA-Z0-9-]//g; s/--*/-/g; s/^-//; s/-$//')
|
||||
[ -n "$name_part" ] || name_part="patch"
|
||||
|
||||
# max is 60 chars, minus 5 for number, minus 6 for patch suffix
|
||||
max_name_len=49
|
||||
name_part=$(printf "%s" "$name_part" | cut -c1-"$max_name_len" | sed 's/-$//')
|
||||
|
||||
patch_name="${next_num}-${name_part}.patch"
|
||||
|
||||
mv "$patch_dir/tmp.patch" "$patch_dir/$patch_name"
|
||||
|
||||
NEW_JSON=$(echo "$JSON" | jq ".patches += [\"$patch_name\"]")
|
||||
"$SCRIPTS"/util/replace.sh "$PACKAGE" "$NEW_JSON"
|
||||
|
||||
echo "-- Patch created at $patch_dir/$patch_name"
|
||||
cmake -DDESCRIPTION="$description" -P "$CMAKE"/patch.cmake "$@"
|
||||
|
||||
@@ -1,56 +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 reset [a|--all] [PACKAGE]...
|
||||
|
||||
Reset a locally fetched package to its original state.
|
||||
This is most useful for dropping any changes you've made.
|
||||
|
||||
EOF
|
||||
|
||||
exit 0
|
||||
}
|
||||
|
||||
while :; do
|
||||
case "$1" in
|
||||
-h | --help) usage ;;
|
||||
-a | --all) ALL=1 ;;
|
||||
"$0") break ;;
|
||||
"") break ;;
|
||||
*) packages="$packages $1" ;;
|
||||
esac
|
||||
|
||||
shift
|
||||
done
|
||||
|
||||
[ "$ALL" != 1 ] || packages="${LIBS:-$packages}"
|
||||
[ -n "$packages" ] || usage
|
||||
|
||||
for PACKAGE in $packages; do
|
||||
unset JSON
|
||||
export PACKAGE
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
. "$SCRIPTS"/vars.sh
|
||||
|
||||
if [ "$CI" = true ]; then
|
||||
dir="${CPM_SOURCE_CACHE}/${LOWER_PACKAGE}"
|
||||
else
|
||||
dir="${CPM_SOURCE_CACHE}/${LOWER_PACKAGE}/${KEY}"
|
||||
fi
|
||||
|
||||
echo "-- Removing $dir"
|
||||
rm -rf "$dir"
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
. "$SCRIPTS"/util/fetch.sh
|
||||
|
||||
fetch_package
|
||||
done
|
||||
@@ -1,30 +0,0 @@
|
||||
#!/bin/sh -e
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright 2026 crueter
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
RETURN=0
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: cpmutil.sh package rm [PACKAGE]...
|
||||
|
||||
Delete a package or packages' cpmfile definition.
|
||||
|
||||
EOF
|
||||
|
||||
exit $RETURN
|
||||
}
|
||||
|
||||
[ $# -ge 1 ] || usage
|
||||
|
||||
for pkg in "$@"; do
|
||||
"$SCRIPTS"/which.sh "$pkg" || {
|
||||
echo "!! No cpmfile definition for $pkg"
|
||||
continue
|
||||
}
|
||||
|
||||
jq --indent 4 "del(.\"$pkg\")" cpmfile.json >cpmfile.json.new
|
||||
mv cpmfile.json.new cpmfile.json
|
||||
echo "-- Removed $pkg"
|
||||
done
|
||||
@@ -1,174 +0,0 @@
|
||||
#!/bin/sh -e
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright 2026 crueter
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
filter_out() {
|
||||
TAGS=$(echo "$TAGS" | jq "[.[] | select(.name | test(\"$1\"; \"i\") | not)]")
|
||||
}
|
||||
|
||||
filter_in() {
|
||||
TAGS=$(echo "$TAGS" | jq "[.[] | select(.name | test(\"$1\"; \"i\"))]")
|
||||
}
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: cpmutil.sh package update [-n|--dry-run] [-a|--all] [PACKAGE]...
|
||||
|
||||
Check a specific package or packages for updates.
|
||||
|
||||
Options:
|
||||
-n, --dry-run Do not update the package if it has an update available
|
||||
-a, --all Operate on all packages in this project.
|
||||
-c, --commit Automatically generate a commit message
|
||||
|
||||
EOF
|
||||
|
||||
exit 0
|
||||
}
|
||||
|
||||
while :; do
|
||||
case "$1" in
|
||||
-[a-z]*)
|
||||
opt=$(printf '%s' "$1" | sed 's/^-//')
|
||||
while [ -n "$opt" ]; do
|
||||
# cut out first char from the optstring
|
||||
char=$(echo "$opt" | cut -c1)
|
||||
opt=$(echo "$opt" | cut -c2-)
|
||||
|
||||
case "$char" in
|
||||
a) ALL=1 ;;
|
||||
n) UPDATE=false ;;
|
||||
c) COMMIT=true ;;
|
||||
h) usage ;;
|
||||
*) die "Invalid option -$char" ;;
|
||||
esac
|
||||
done
|
||||
;;
|
||||
--dry-run) UPDATE=false ;;
|
||||
--all) ALL=1 ;;
|
||||
--help) usage ;;
|
||||
--commit) COMMIT=true ;;
|
||||
"$0") break ;;
|
||||
"") break ;;
|
||||
*) packages="$packages $1" ;;
|
||||
esac
|
||||
|
||||
shift
|
||||
done
|
||||
|
||||
[ "$ALL" != 1 ] || packages="${LIBS:-$packages}"
|
||||
: "${UPDATE:=true}"
|
||||
: "${COMMIT:=false}"
|
||||
[ -n "$packages" ] || usage
|
||||
|
||||
for pkg in $packages; do
|
||||
unset JSON
|
||||
export PACKAGE="$pkg"
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
. "$SCRIPTS"/vars.sh
|
||||
|
||||
SKIP=$(value "skip_updates")
|
||||
|
||||
[ "$SKIP" != "true" ] || continue
|
||||
|
||||
[ "$REPO" != null ] || continue
|
||||
[ "$GIT_HOST" = "github.com" ] || continue # TODO
|
||||
|
||||
[ "$CI" != "true" ] || continue
|
||||
|
||||
# shellcheck disable=SC2153
|
||||
[ "$TAG" != null ] || continue
|
||||
|
||||
echo "-- Package $PACKAGE"
|
||||
|
||||
# TODO(crueter): Support for forgejo_token?
|
||||
endpoint="/repos/$REPO/tags"
|
||||
if command -v gh >/dev/null 2>&1; then
|
||||
TAGS=$(gh api --method GET "$endpoint")
|
||||
elif [ "$GIT_HOST" = github.com ]; then
|
||||
TAGS=$(curl -sfL "https://api.github.com$endpoint")
|
||||
else
|
||||
TAGS=$(curl -sfL "https://$GIT_HOST/api/v1$endpoint")
|
||||
fi
|
||||
|
||||
# filter out some commonly known annoyances
|
||||
# TODO add more
|
||||
|
||||
if [ "$PACKAGE" = "vulkan-validation-layers" ]; then
|
||||
filter_in vulkan-sdk
|
||||
else
|
||||
filter_out vulkan-sdk
|
||||
fi
|
||||
|
||||
filter_out yotta # mbedtls
|
||||
|
||||
filter_out vksc
|
||||
|
||||
# ignore betas/alphas (remove if needed)
|
||||
filter_out alpha
|
||||
filter_out beta
|
||||
filter_out rc
|
||||
|
||||
# Add package-specific overrides here, e.g. here for fmt:
|
||||
[ "$PACKAGE" != fmt ] || filter_out v0.11
|
||||
|
||||
# Or for OpenSSL:
|
||||
if [ "$PACKAGE" = openssl ]; then
|
||||
filter_out rsaref
|
||||
filter_in "openssl-"
|
||||
fi
|
||||
|
||||
LATEST=$(echo "$TAGS" | jq -r '.[0].name')
|
||||
|
||||
if [ "$LATEST" = "null" ] ||
|
||||
{ [ "$LATEST" = "$TAG" ] && [ "$FORCE" != "true" ]; }; then
|
||||
echo "-- * Up-to-date"
|
||||
continue
|
||||
fi
|
||||
|
||||
# TODO: This is identical to version.sh
|
||||
|
||||
if [ "$HAS_REPLACE" = "true" ]; then
|
||||
# this just extracts the tag prefix
|
||||
VERSION_PREFIX=$(echo "$ORIGINAL_TAG" | cut -d"%" -f1)
|
||||
|
||||
# then we strip out the prefix from the new tag, and make that our new version
|
||||
if [ -z "$VERSION_PREFIX" ]; then
|
||||
NEW_VERSION="$LATEST"
|
||||
else
|
||||
NEW_VERSION=$(echo "$LATEST" | sed "s/$VERSION_PREFIX//g")
|
||||
fi
|
||||
else
|
||||
NEW_VERSION="$LATEST"
|
||||
fi
|
||||
|
||||
_commit="$_commit
|
||||
* $PACKAGE: $VERSION -> $NEW_VERSION"
|
||||
|
||||
echo "-- * Version $LATEST available, current is $TAG"
|
||||
|
||||
if [ "$UPDATE" = "true" ]; then
|
||||
if [ "$HAS_REPLACE" = "true" ]; then
|
||||
JSON=$(echo "$JSON" | jq ".version = \"$NEW_VERSION\"")
|
||||
else
|
||||
JSON=$(echo "$JSON" | jq ".tag = \"$NEW_VERSION\"")
|
||||
fi
|
||||
|
||||
echo "-- * -- Updating hash"
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
. "$SCRIPTS"/vars.sh
|
||||
HASH=$("$SCRIPTS"/util/url-hash.sh "$DOWNLOAD")
|
||||
JSON=$(echo "$JSON" | jq ".hash = \"$HASH\"")
|
||||
|
||||
"$SCRIPTS"/util/replace.sh "$PACKAGE" "$JSON"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$UPDATE" = "true" ] && [ "$COMMIT" = "true" ] && [ -n "$_commit" ]; then
|
||||
git add "cpmfile.json"
|
||||
git commit -m "Update dependencies
|
||||
$_commit"
|
||||
fi
|
||||
@@ -1,134 +0,0 @@
|
||||
#!/bin/sh -e
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright 2026 crueter
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
. "$SCRIPTS"/../common.sh
|
||||
|
||||
download_package() {
|
||||
mkdir -p "$CPM_SOURCE_CACHE"
|
||||
|
||||
tmp=$(make_temp_dir)
|
||||
|
||||
FILENAME=$(basename "$DOWNLOAD")
|
||||
|
||||
OUTFILE="$tmp/$FILENAME"
|
||||
|
||||
OUTDIR="${CPM_SOURCE_CACHE}/${LOWER_PACKAGE}/${KEY}"
|
||||
if [ -d "$OUTDIR" ]; then return; fi
|
||||
|
||||
curl "$DOWNLOAD" -sS -L -o "$OUTFILE"
|
||||
|
||||
ACTUAL_HASH=$(sha512sum "$OUTFILE" | cut -d" " -f1)
|
||||
if [ "$ACTUAL_HASH" != "$HASH" ]; then
|
||||
echo "!! $FILENAME did not match expected hash; expected $HASH but got $ACTUAL_HASH" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TMPDIR="$tmp/extracted"
|
||||
mkdir -p "$OUTDIR"
|
||||
mkdir -p "$TMPDIR"
|
||||
|
||||
PREVDIR="$PWD"
|
||||
mkdir -p "$TMPDIR"
|
||||
cd "$TMPDIR"
|
||||
|
||||
case "$FILENAME" in
|
||||
*.7z)
|
||||
must_install 7z
|
||||
7z x "$OUTFILE" >/dev/null
|
||||
;;
|
||||
*.tar*)
|
||||
# TODO: Extensions
|
||||
must_install tar
|
||||
tar xf "$OUTFILE" >/dev/null
|
||||
;;
|
||||
*.zip)
|
||||
must_install unzip
|
||||
unzip "$OUTFILE" >/dev/null
|
||||
;;
|
||||
esac
|
||||
|
||||
# basically if only one real item exists at the top we just move everything from there
|
||||
# since github and some vendors hate me
|
||||
DIRS=$(find . -maxdepth 1 -type d -o -type f)
|
||||
|
||||
# thanks gnu
|
||||
if [ "$(echo "$DIRS" | wc -l)" -eq 2 ]; then
|
||||
SUBDIR=$(find . -maxdepth 1 -type d -not -name ".")
|
||||
mv "$SUBDIR"/* "$OUTDIR"
|
||||
mv "$SUBDIR"/.* "$OUTDIR" 2>/dev/null || true
|
||||
rmdir "$SUBDIR"
|
||||
else
|
||||
mv ./* "$OUTDIR"
|
||||
mv ./.* "$OUTDIR" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
cd "$OUTDIR"
|
||||
|
||||
# TODO: Common custom patch/source cache dirs
|
||||
if echo "$JSON" | grep -e "patches" >/dev/null; then
|
||||
PATCHES=$(echo "$JSON" | jq -r '.patches | join(" ")')
|
||||
for patch in $PATCHES; do
|
||||
abs_patch="$CPMUTIL_PATCH_DIR/$PACKAGE/$patch"
|
||||
if [ ! -f "$abs_patch" ]; then
|
||||
echo "-- * Attempted to apply nonexistent patch $patch!"
|
||||
continue
|
||||
fi
|
||||
|
||||
echo "-- * Applying patch $patch"
|
||||
patch --binary -p1 <"$abs_patch"
|
||||
done
|
||||
fi
|
||||
|
||||
cd "$PREVDIR"
|
||||
|
||||
rm -rf "$tmp"
|
||||
}
|
||||
|
||||
# TODO: individual platform fetch?
|
||||
ci_package() {
|
||||
[ "$REPO" != null ] || { echo "-- ! No repo defined" && return; }
|
||||
mkdir -p "$CPM_SOURCE_CACHE"
|
||||
|
||||
echo "-- CI package $PACKAGE_NAME"
|
||||
|
||||
for platform in \
|
||||
windows-amd64 windows-arm64 \
|
||||
mingw-amd64 mingw-arm64 \
|
||||
android-aarch64 android-x86_64 \
|
||||
linux-amd64 linux-aarch64 \
|
||||
macos-universal ios-aarch64; do
|
||||
echo "-- * platform $platform"
|
||||
|
||||
case $DISABLED in
|
||||
*"$platform"*)
|
||||
echo "-- * -- disabled"
|
||||
continue
|
||||
;;
|
||||
esac
|
||||
|
||||
FILENAME="${NAME}-${platform}-${VERSION}.${EXT}"
|
||||
DOWNLOAD="https://$GIT_HOST/${REPO}/releases/download/v${VERSION}/${FILENAME}"
|
||||
KEY="$platform-$VERSION"
|
||||
|
||||
OUTDIR="${CPM_SOURCE_CACHE}/${LOWER_PACKAGE}/${KEY}"
|
||||
[ -d "$OUTDIR" ] && continue
|
||||
|
||||
HASH_URL="${DOWNLOAD}.sha512sum"
|
||||
|
||||
HASH=$(curl "$HASH_URL" -sS -q -L -o -)
|
||||
|
||||
download_package
|
||||
done
|
||||
}
|
||||
|
||||
fetch_package() {
|
||||
if [ "$CI" = "true" ]; then
|
||||
ci_package
|
||||
else
|
||||
echo "-- Downloading regular package $PACKAGE, with key $KEY, from $DOWNLOAD"
|
||||
download_package
|
||||
fi
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
#!/bin/sh -e
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright 2026 crueter
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
|
||||
: "${PACKAGE:=$1}"
|
||||
|
||||
# re-read json files
|
||||
# shellcheck disable=SC2016
|
||||
. "$SCRIPTS"/vars.sh
|
||||
|
||||
[ "$CI" = null ] || exit 0
|
||||
|
||||
ACTUAL=$("$SCRIPTS"/util/url-hash.sh "$DOWNLOAD")
|
||||
|
||||
if [ "$ACTUAL" != "$HASH" ] && [ "$QUIET" != true ]; then
|
||||
echo "-- * Expected $HASH"
|
||||
echo "-- * Got $ACTUAL"
|
||||
[ "$UPDATE" != "true" ] && exit 1
|
||||
fi
|
||||
|
||||
if [ "$UPDATE" = "true" ] && [ "$ACTUAL" != "$HASH" ]; then
|
||||
NEW_JSON=$(echo "$JSON" | jq ".hash = \"$ACTUAL\"")
|
||||
|
||||
"$SCRIPTS"/util/replace.sh "$PACKAGE" "$NEW_JSON"
|
||||
fi
|
||||
@@ -1,228 +0,0 @@
|
||||
#!/bin/sh -e
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright 2026 crueter
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
# 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
|
||||
}
|
||||
|
||||
# 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"
|
||||
|
||||
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)."
|
||||
|
||||
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"
|
||||
|
||||
required "Numeric version of the bundled package" \
|
||||
"The semantic version of the bundled package. This is only used for package identification,
|
||||
and if you use tag/artifact fetching. Do not input the entire tag here; for example, if you're using
|
||||
tag v1.3.0, then set this to 1.3.0 and set the tag to v%VERSION%."
|
||||
|
||||
VERSION="$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
|
||||
while :; do
|
||||
required "Use tag or commit sha versioning? [tag/sha]" \
|
||||
"Tag versioning is compatible with auto-updating.
|
||||
Use sha versioning for projects with improper tagging practices"
|
||||
|
||||
if [ "$reply" = "tag" ]; then
|
||||
TAG=1
|
||||
break
|
||||
elif [ "$reply" = "sha" ]; then
|
||||
SHA=1
|
||||
break
|
||||
else
|
||||
echo "-- Invalid choice $reply"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$TAG" = "1" ]; then
|
||||
optional "Name of the upstream tag. %VERSION% is replaced by the numeric version ($VERSION) (default: %VERSION%)" \
|
||||
"Most commonly this will be something like v%VERSION% or release-%VERSION%, or just %VERSION%."
|
||||
|
||||
TAGNAME="$reply"
|
||||
[ -n "$TAGNAME" ] || TAGNAME="%VERSION%"
|
||||
|
||||
optional "Name of the release artifact to download, if applicable.
|
||||
-- %VERSION% is replaced by the numeric version ($VERSION) and %TAG% is replaced by the tag name" \
|
||||
"Download the specified artifact from the release with the previously specified tag.
|
||||
If unspecified, the source code at the specified tag will be used instead."
|
||||
|
||||
ARTIFACT="$reply"
|
||||
else
|
||||
required "Commit sha" \
|
||||
"The short Git commit sha to use. You're recommended to keep this short, e.g. 10 characters."
|
||||
|
||||
SHA="$reply"
|
||||
fi
|
||||
|
||||
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
|
||||
|
||||
# now time to construct the actual json
|
||||
jq_input='{repo: "'"$REPO"'"}'
|
||||
|
||||
# common trivial fields
|
||||
[ -z "$PACKAGE" ] || jq_input="$jq_input + {package: \"$PACKAGE\"}"
|
||||
[ -z "$MIN_VERSION" ] || jq_input="$jq_input + {min_version: \"$MIN_VERSION\"}"
|
||||
[ -z "$FIND_ARGS" ] || jq_input="$jq_input + {find_args: \"$FIND_ARGS\"}"
|
||||
jq_input="$jq_input + {version: \"$VERSION\"}"
|
||||
|
||||
if [ -n "$GIT_HOST" ] && [ "$GIT_HOST" != "github.com" ]; then
|
||||
jq_input="$jq_input + {git_host: \"$GIT_HOST\"}"
|
||||
fi
|
||||
|
||||
if [ "$CI" = "true" ]; then
|
||||
jq_input="$jq_input + {
|
||||
ci: true,
|
||||
artifact: \"$ARTIFACT\"
|
||||
}"
|
||||
|
||||
# disabled platforms
|
||||
if [ -n "$DISABLED_PLATFORMS" ] && [ -n "$(printf '%s' "$DISABLED_PLATFORMS" | tr -d ' \t\n\r')" ]; then
|
||||
disabled_json=$(printf '%s\n' "$DISABLED_PLATFORMS" | jq -R . | jq -s .)
|
||||
jq_input="$jq_input + {disabled_platforms: $disabled_json}"
|
||||
fi
|
||||
else
|
||||
[ -z "$MIN_VERSION" ] || jq_input="$jq_input + {version: \"$MIN_VERSION\"}"
|
||||
jq_input="$jq_input + {hash: \"\"}"
|
||||
|
||||
# options
|
||||
if [ -n "$OPTIONS" ] && [ -n "$(printf '%s' "$OPTIONS" | tr -d ' \t\n\r')" ]; then
|
||||
options_json=$(printf '%s\n' "$OPTIONS" | jq -R . | jq -s .)
|
||||
jq_input="$jq_input + {options: $options_json}"
|
||||
fi
|
||||
|
||||
# versioning stuff
|
||||
if [ "$TAG" = 1 ]; then
|
||||
jq_input="$jq_input + {tag: \"$TAGNAME\"}"
|
||||
[ -z "$ARTIFACT" ] || jq_input="$jq_input + {artifact: \"$ARTIFACT\"}"
|
||||
else
|
||||
jq_input="$jq_input + {sha: \"$SHA\"}"
|
||||
fi
|
||||
fi
|
||||
|
||||
JSON=$(jq -n "$jq_input")
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
. "$SCRIPTS"/vars.sh
|
||||
|
||||
if [ "$CI" != true ]; then
|
||||
HASH=$("$SCRIPTS"/util/url-hash.sh "$DOWNLOAD")
|
||||
JSON=$(echo "$JSON" | jq ".hash = \"$HASH\"")
|
||||
fi
|
||||
|
||||
jq --arg key "$PKG" --argjson new "$JSON" \
|
||||
'.[$key] = $new' "cpmfile.json" --indent 4 >"cpmfile.json.tmp" &&
|
||||
mv "cpmfile.json.tmp" cpmfile.json
|
||||
|
||||
"$SCRIPTS"/format.sh
|
||||
|
||||
echo "Added package $PKG to cpmfile.json. Include it in your project with AddJsonPackage($PKG)"
|
||||
@@ -1,11 +0,0 @@
|
||||
#!/bin/sh -e
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright 2026 crueter
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
# Replace a specified package with a modified json.
|
||||
|
||||
jq --indent 4 --argjson repl "$2" ".\"$1\" *= \$repl" cpmfile.json >cpmfile.json.new
|
||||
mv cpmfile.json.new cpmfile.json
|
||||
|
||||
echo "-- * -- Updated cpmfile.json"
|
||||
@@ -1,7 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright 2025 crueter
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
SUM=$(curl -Ls "$1" -o - | sha512sum)
|
||||
echo "$SUM" | cut -d " " -f1
|
||||
@@ -1,27 +0,0 @@
|
||||
#!/bin/sh -e
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright 2026 crueter
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
# Get the download URL for a package
|
||||
|
||||
if [ "$URL" != "null" ]; then
|
||||
DOWNLOAD="$URL"
|
||||
elif [ "$REPO" != "null" ]; then
|
||||
GIT_URL="https://$GIT_HOST/$REPO"
|
||||
|
||||
if [ "$SHA" != "null" ]; then
|
||||
DOWNLOAD="${GIT_URL}/archive/${SHA}.tar.gz"
|
||||
elif [ "$TAG" != "null" ]; then
|
||||
if [ "$ARTIFACT" != "null" ]; then
|
||||
DOWNLOAD="${GIT_URL}/releases/download/${TAG}/${ARTIFACT}"
|
||||
else
|
||||
DOWNLOAD="${GIT_URL}/archive/refs/tags/${TAG}.tar.gz"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo "!! No repo or URL defined for $PACKAGE_NAME"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
export DOWNLOAD
|
||||
@@ -1,158 +0,0 @@
|
||||
#!/bin/sh -e
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright 2026 crueter
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
|
||||
value() {
|
||||
echo "$JSON" | jq -r ".$1"
|
||||
}
|
||||
|
||||
if [ -z "$JSON" ]; then
|
||||
[ -n "$PACKAGE" ] || { echo "Package was not specified" && exit 0; }
|
||||
|
||||
# shellcheck disable=SC2153
|
||||
JSON=$(jq -r ".\"$PACKAGE\" | select( . != null )" cpmfile.json)
|
||||
|
||||
if [ -z "$JSON" ]; then
|
||||
echo "!! No cpmfile definition for $PACKAGE" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# unset stuff
|
||||
export PACKAGE_NAME="null"
|
||||
export REPO="null"
|
||||
export CI="null"
|
||||
export GIT_HOST="null"
|
||||
export EXT="null"
|
||||
export NAME="null"
|
||||
export DISABLED="null"
|
||||
export TAG="null"
|
||||
export ARTIFACT="null"
|
||||
export SHA="null"
|
||||
export VERSION="null"
|
||||
export MIN_VERSION="null"
|
||||
export DOWNLOAD="null"
|
||||
export URL="null"
|
||||
export KEY="null"
|
||||
export HASH="null"
|
||||
export ORIGINAL_TAG="null"
|
||||
export HAS_REPLACE="null"
|
||||
export VERSION_REPLACE="null"
|
||||
|
||||
########
|
||||
# Meta #
|
||||
########
|
||||
|
||||
REPO=$(value "repo")
|
||||
CI=$(value "ci")
|
||||
|
||||
PACKAGE_NAME=$(value "package")
|
||||
[ "$PACKAGE_NAME" != null ] || PACKAGE_NAME="$PACKAGE"
|
||||
|
||||
GIT_HOST=$(value "git_host")
|
||||
[ "$GIT_HOST" != null ] || GIT_HOST=github.com
|
||||
|
||||
# used for cache key
|
||||
LOWER_PACKAGE=$(echo "$PACKAGE_NAME" | tr '[:upper:]' '[:lower:]')
|
||||
|
||||
export PACKAGE_NAME
|
||||
export LOWER_PACKAGE
|
||||
export REPO
|
||||
export CI
|
||||
export GIT_HOST
|
||||
|
||||
######################
|
||||
# CI Package Parsing #
|
||||
######################
|
||||
|
||||
MIN_VERSION=$(value "min_version")
|
||||
VERSION=$(value "version")
|
||||
|
||||
export VERSION
|
||||
export MIN_VERSION
|
||||
|
||||
if [ "$CI" = "true" ]; then
|
||||
EXT=$(value "extension")
|
||||
[ "$EXT" != null ] || EXT="tar.zst"
|
||||
|
||||
NAME=$(value "name")
|
||||
DISABLED=$(echo "$JSON" | jq -j '.disabled_platforms')
|
||||
|
||||
[ "$NAME" != null ] || NAME="$PACKAGE_NAME"
|
||||
|
||||
export EXT
|
||||
export NAME
|
||||
export DISABLED
|
||||
|
||||
return 0
|
||||
fi
|
||||
|
||||
##############
|
||||
# Versioning #
|
||||
##############
|
||||
|
||||
TAG=$(value "tag")
|
||||
ARTIFACT=$(value "artifact")
|
||||
SHA=$(value "sha")
|
||||
|
||||
if echo "$TAG" | grep -e "%VERSION%" >/dev/null; then
|
||||
HAS_REPLACE=true
|
||||
else
|
||||
HAS_REPLACE=false
|
||||
fi
|
||||
|
||||
ORIGINAL_TAG="$TAG"
|
||||
|
||||
TAG=$(echo "$TAG" | sed "s/%VERSION%/$VERSION/g")
|
||||
ARTIFACT=$(echo "$ARTIFACT" | sed "s/%VERSION%/$VERSION/g")
|
||||
ARTIFACT=$(echo "$ARTIFACT" | sed "s/%TAG%/$TAG/g")
|
||||
|
||||
export TAG
|
||||
export ARTIFACT
|
||||
export SHA
|
||||
export VERSION
|
||||
export ORIGINAL_TAG
|
||||
export HAS_REPLACE
|
||||
|
||||
###############
|
||||
# URL Parsing #
|
||||
###############
|
||||
|
||||
URL=$(value "url")
|
||||
|
||||
. "$SCRIPTS"/util/url.sh
|
||||
|
||||
export DOWNLOAD
|
||||
|
||||
###############
|
||||
# Key Parsing #
|
||||
###############
|
||||
|
||||
if [ "$SHA" != null ]; then
|
||||
KEY=$(echo "$SHA" | cut -c1-4)
|
||||
elif [ "$VERSION" != null ]; then
|
||||
KEY="$VERSION"
|
||||
elif [ "$TAG" != null ]; then
|
||||
KEY="$TAG"
|
||||
else
|
||||
echo "!! No valid key could be determined for $PACKAGE_NAME. Must define one of: sha, tag, version"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
export KEY
|
||||
|
||||
################
|
||||
# Hash Parsing #
|
||||
################
|
||||
|
||||
HASH=$(value "hash")
|
||||
|
||||
if [ "$HASH" = null ]; then
|
||||
echo "!! No hash defined for $PACKAGE_NAME" >&2
|
||||
fi
|
||||
|
||||
export HASH
|
||||
export JSON
|
||||
@@ -1,62 +0,0 @@
|
||||
#!/bin/sh -e
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright 2026 crueter
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
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.
|
||||
|
||||
EOF
|
||||
|
||||
exit 0
|
||||
}
|
||||
|
||||
PACKAGE="$1"
|
||||
NEW_VERSION="$2"
|
||||
|
||||
[ -n "$PACKAGE" ] || usage
|
||||
[ -n "$NEW_VERSION" ] || usage
|
||||
|
||||
export PACKAGE
|
||||
|
||||
. "$SCRIPTS"/vars.sh
|
||||
|
||||
[ "$REPO" != null ] || exit 0
|
||||
|
||||
if [ "$HAS_REPLACE" = "true" ]; then
|
||||
# this just extracts the tag prefix
|
||||
VERSION_PREFIX=$(echo "$ORIGINAL_TAG" | cut -d"%" -f1)
|
||||
|
||||
# then we strip out the prefix from the new tag, and make that our new git_version
|
||||
if [ -n "$VERSION_PREFIX" ]; then
|
||||
NEW_VERSION=$(echo "$NEW_VERSION" | sed "s/$VERSION_PREFIX//g")
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$SHA" != null ]; then
|
||||
JSON=$(echo "$JSON" | jq ".sha = \"$NEW_VERSION\"")
|
||||
elif [ "$CI" = "true" ] || [ "$HAS_REPLACE" = "true" ]; then
|
||||
JSON=$(echo "$JSON" | jq ".version = \"$NEW_VERSION\"")
|
||||
else
|
||||
JSON=$(echo "$JSON" | jq ".tag = \"$NEW_VERSION\"")
|
||||
fi
|
||||
|
||||
echo "-- * Updating $PACKAGE to version $NEW_VERSION"
|
||||
|
||||
# TODO: ci hash thing please
|
||||
if [ "$CI" != true ]; then
|
||||
echo "-- * -- Updating hash"
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
. "$SCRIPTS"/vars.sh
|
||||
HASH=$("$SCRIPTS"/util/url-hash.sh "$DOWNLOAD")
|
||||
JSON=$(echo "$JSON" | jq ".hash = \"$HASH\"")
|
||||
fi
|
||||
|
||||
"$SCRIPTS"/util/replace.sh "$PACKAGE" "$JSON"
|
||||
@@ -1,8 +0,0 @@
|
||||
#!/bin/sh -e
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright 2026 crueter
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
# check if a package is defined
|
||||
|
||||
echo "$LIBS" | grep "$1" >/dev/null 2>&1
|
||||
+37
-16
@@ -7,8 +7,7 @@
|
||||
|
||||
ROOTDIR=$(CDPATH='' cd -- "$(dirname -- "$0")/cpm" && pwd)
|
||||
SCRIPTS="$ROOTDIR"
|
||||
|
||||
. "$SCRIPTS"/common.sh
|
||||
CMAKE="$ROOTDIR/cmake"
|
||||
|
||||
RETURN=0
|
||||
|
||||
@@ -26,33 +25,55 @@ General command-line utility for CPMUtil operations.
|
||||
Commands:
|
||||
package Run operations on a package or packages
|
||||
format Format cpmfile
|
||||
ls List all packages in the cpmfile
|
||||
update Update CPMUtil and its tooling
|
||||
migrate Convert submodules to a basic cpmfile
|
||||
|
||||
Package 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 Create an in-tree patch based on local modifications
|
||||
|
||||
EOF
|
||||
|
||||
exit $RETURN
|
||||
}
|
||||
|
||||
export ROOTDIR
|
||||
export ROOTDIR CMAKE SCRIPTS
|
||||
|
||||
while :; do
|
||||
case "$1" in
|
||||
format | update | migrate)
|
||||
"$SCRIPTS/$1".sh
|
||||
format | update)
|
||||
cmake -P "$CMAKE/$1".cmake
|
||||
break
|
||||
;;
|
||||
ls)
|
||||
shift
|
||||
print_usage=false
|
||||
filtered=""
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
-h | --help) print_usage=true ;;
|
||||
-n) ;;
|
||||
*) filtered="$filtered $arg" ;;
|
||||
esac
|
||||
done
|
||||
cmake_defines=""
|
||||
$print_usage && cmake_defines="-DPRINT_USAGE=TRUE"
|
||||
# shellcheck disable=SC2086
|
||||
cmake $cmake_defines -P "$CMAKE/ls.cmake" -- $filtered
|
||||
break
|
||||
;;
|
||||
migrate)
|
||||
"$SCRIPTS"/migrate.sh
|
||||
break
|
||||
;;
|
||||
package)
|
||||
|
||||
Reference in New Issue
Block a user