diff --git a/CMakeModules/CPMUtil.cmake b/CMakeModules/CPMUtil.cmake index 834e07e665..4c9b645d16 100644 --- a/CMakeModules/CPMUtil.cmake +++ b/CMakeModules/CPMUtil.cmake @@ -41,6 +41,11 @@ function(cpm_utils_message level name message) message(${level} "[CPMUtil] ${name}: ${message}") endfunction() +# propagate a variable to parent scope +macro(Propagate var) + set(${var} ${${var}} PARENT_SCOPE) +endmacro() + function(array_to_list array length out) math(EXPR range "${length} - 1") @@ -72,6 +77,159 @@ function(get_json_element object out member default) set("${out}" "${outvar}" PARENT_SCOPE) endfunction() +# Determine whether or not a package has a viable system candidate. +function(SystemPackageViable JSON_NAME) + string(JSON object GET "${CPMFILE_CONTENT}" "${JSON_NAME}") + + parse_object(${object}) + + string(REPLACE " " ";" find_args "${find_args}") + find_package(${package} ${version} ${find_args} QUIET NO_POLICY_SCOPE) + + set(${pkg}_VIABLE ${${package}_FOUND} PARENT_SCOPE) + set(${pkg}_PACKAGE ${package} PARENT_SCOPE) +endfunction() + +# Add several packages such that if one is bundled, +# all the rest must also be bundled. +function(AddDependentPackages) + set(_some_system OFF) + set(_some_bundled OFF) + + foreach(pkg ${ARGN}) + SystemPackageViable(${pkg}) + + if (${pkg}_VIABLE) + set(_some_system ON) + list(APPEND _system_pkgs ${${pkg}_PACKAGE}) + else() + set(_some_bundled ON) + list(APPEND _bundled_pkgs ${${pkg}_PACKAGE}) + endif() + endforeach() + + if (_some_system AND _some_bundled) + foreach(pkg ${ARGN}) + list(APPEND package_names ${${pkg}_PACKAGE}) + endforeach() + + string(REPLACE ";" ", " package_names "${package_names}") + string(REPLACE ";" ", " bundled_names "${_bundled_pkgs}") + foreach(sys ${_system_pkgs}) + list(APPEND system_names ${sys}_FORCE_BUNDLED) + endforeach() + + string(REPLACE ";" ", " system_names "${system_names}") + + message(FATAL_ERROR "Partial dependency installation detected " + "for the following packages:\n${package_names}\n" + "You can solve this in one of two ways:\n" + "1. Install the following packages to your system if available:" + "\n\t${bundled_names}\n" + "2. Set the following variables to ON:" + "\n\t${system_names}\n" + "This may also be caused by a version mismatch, " + "such as one package being newer than the other.") + endif() + + foreach(pkg ${ARGN}) + AddJsonPackage(${pkg}) + endforeach() +endfunction() + +# json util +macro(parse_object object) + get_json_element("${object}" package package ${JSON_NAME}) + get_json_element("${object}" repo repo "") + get_json_element("${object}" ci ci OFF) + get_json_element("${object}" version version "") + + if(ci) + get_json_element("${object}" name name "${JSON_NAME}") + get_json_element("${object}" extension extension "tar.zst") + get_json_element("${object}" min_version min_version "") + get_json_element("${object}" raw_disabled disabled_platforms "") + + if(raw_disabled) + array_to_list("${raw_disabled}" + ${raw_disabled_LENGTH} disabled_platforms) + else() + set(disabled_platforms "") + endif() + else() + get_json_element("${object}" hash hash "") + get_json_element("${object}" hash_suffix hash_suffix "") + get_json_element("${object}" sha sha "") + get_json_element("${object}" url url "") + get_json_element("${object}" key key "") + get_json_element("${object}" tag tag "") + get_json_element("${object}" artifact artifact "") + get_json_element("${object}" git_version git_version "") + get_json_element("${object}" git_host git_host "") + get_json_element("${object}" source_subdir source_subdir "") + get_json_element("${object}" bundled bundled "unset") + get_json_element("${object}" find_args find_args "") + get_json_element("${object}" raw_patches patches "") + + # okay here comes the fun part: REPLACEMENTS! + # first: tag gets %VERSION% replaced if applicable, + # with either git_version (preferred) or version + # second: artifact gets %VERSION% and %TAG% replaced + # accordingly (same rules for VERSION) + + if(git_version) + set(version_replace ${git_version}) + else() + set(version_replace ${version}) + endif() + + # TODO(crueter): fmt module for cmake + if(tag) + string(REPLACE "%VERSION%" "${version_replace}" tag ${tag}) + endif() + + if(artifact) + string(REPLACE "%VERSION%" "${version_replace}" + artifact ${artifact}) + string(REPLACE "%TAG%" "${tag}" artifact ${artifact}) + endif() + + # format patchdir + if(raw_patches) + math(EXPR range "${raw_patches_LENGTH} - 1") + + foreach(IDX RANGE ${range}) + string(JSON _patch GET "${raw_patches}" "${IDX}") + + set(full_patch + "${PROJECT_SOURCE_DIR}/.patch/${JSON_NAME}/${_patch}") + if(NOT EXISTS ${full_patch}) + cpm_utils_message(FATAL_ERROR ${JSON_NAME} + "specifies patch ${full_patch} which does not exist") + endif() + + list(APPEND patches "${full_patch}") + endforeach() + endif() + # end format patchdir + + # options + get_json_element("${object}" raw_options options "") + + if(raw_options) + array_to_list("${raw_options}" ${raw_options_LENGTH} options) + endif() + + set(options ${options} ${JSON_OPTIONS}) + # end options + + # system/bundled + if(bundled STREQUAL "unset" AND DEFINED JSON_BUNDLED_PACKAGE) + set(bundled ${JSON_BUNDLED_PACKAGE}) + endif() + endif() +endmacro() + # The preferred usage function(AddJsonPackage) set(oneValueArgs @@ -80,7 +238,8 @@ function(AddJsonPackage) # these are overrides that can be generated at runtime, # so can be defined separately from the json DOWNLOAD_ONLY - BUNDLED_PACKAGE) + BUNDLED_PACKAGE + FORCE_BUNDLED_PACKAGE) set(multiValueArgs OPTIONS) @@ -111,24 +270,9 @@ function(AddJsonPackage) cpm_utils_message(FATAL_ERROR ${JSON_NAME} "Not found in cpmfile") endif() - get_json_element("${object}" package package ${JSON_NAME}) - get_json_element("${object}" repo repo "") - get_json_element("${object}" ci ci OFF) - get_json_element("${object}" version version "") + parse_object(${object}) if(ci) - get_json_element("${object}" name name "${JSON_NAME}") - get_json_element("${object}" extension extension "tar.zst") - get_json_element("${object}" min_version min_version "") - get_json_element("${object}" raw_disabled disabled_platforms "") - - if(raw_disabled) - array_to_list("${raw_disabled}" - ${raw_disabled_LENGTH} disabled_platforms) - else() - set(disabled_platforms "") - endif() - AddCIPackage( VERSION ${version} NAME ${name} @@ -138,116 +282,38 @@ function(AddJsonPackage) MIN_VERSION ${min_version} DISABLED_PLATFORMS ${disabled_platforms}) - # pass stuff to parent scope - set(${package}_ADDED "${${package}_ADDED}" - PARENT_SCOPE) - set(${package}_SOURCE_DIR "${${package}_SOURCE_DIR}" - PARENT_SCOPE) - set(${package}_BINARY_DIR "${${package}_BINARY_DIR}" - PARENT_SCOPE) - - return() - endif() - - get_json_element("${object}" hash hash "") - get_json_element("${object}" hash_suffix hash_suffix "") - get_json_element("${object}" sha sha "") - get_json_element("${object}" url url "") - get_json_element("${object}" key key "") - get_json_element("${object}" tag tag "") - get_json_element("${object}" artifact artifact "") - get_json_element("${object}" git_version git_version "") - get_json_element("${object}" git_host git_host "") - get_json_element("${object}" source_subdir source_subdir "") - get_json_element("${object}" bundled bundled "unset") - get_json_element("${object}" find_args find_args "") - get_json_element("${object}" raw_patches patches "") - - # okay here comes the fun part: REPLACEMENTS! - # first: tag gets %VERSION% replaced if applicable, - # with either git_version (preferred) or version - # second: artifact gets %VERSION% and %TAG% replaced - # accordingly (same rules for VERSION) - - if(git_version) - set(version_replace ${git_version}) else() - set(version_replace ${version}) - endif() - - # TODO(crueter): fmt module for cmake - if(tag) - string(REPLACE "%VERSION%" "${version_replace}" tag ${tag}) - endif() - - if(artifact) - string(REPLACE "%VERSION%" "${version_replace}" artifact ${artifact}) - string(REPLACE "%TAG%" "${tag}" artifact ${artifact}) - endif() - - # format patchdir - if(raw_patches) - math(EXPR range "${raw_patches_LENGTH} - 1") - - foreach(IDX RANGE ${range}) - string(JSON _patch GET "${raw_patches}" "${IDX}") - - set(full_patch - "${PROJECT_SOURCE_DIR}/.patch/${JSON_NAME}/${_patch}") - if(NOT EXISTS ${full_patch}) - cpm_utils_message(FATAL_ERROR ${JSON_NAME} - "specifies patch ${full_patch} which does not exist") - endif() - - list(APPEND patches "${full_patch}") - endforeach() - endif() - # end format patchdir - - # options - get_json_element("${object}" raw_options options "") - - if(raw_options) - array_to_list("${raw_options}" ${raw_options_LENGTH} options) - endif() - - set(options ${options} ${JSON_OPTIONS}) - # end options + if (NOT DEFINED JSON_FORCE_BUNDLED_PACKAGE) + set(JSON_FORCE_BUNDLED_PACKAGE OFF) + endif() - # system/bundled - if(bundled STREQUAL "unset" AND DEFINED JSON_BUNDLED_PACKAGE) - set(bundled ${JSON_BUNDLED_PACKAGE}) + AddPackage( + NAME "${package}" + VERSION "${version}" + URL "${url}" + HASH "${hash}" + HASH_SUFFIX "${hash_suffix}" + SHA "${sha}" + REPO "${repo}" + KEY "${key}" + PATCHES "${patches}" + OPTIONS "${options}" + FIND_PACKAGE_ARGUMENTS "${find_args}" + BUNDLED_PACKAGE "${bundled}" + FORCE_BUNDLED_PACKAGE "${JSON_FORCE_BUNDLED_PACKAGE}" + SOURCE_SUBDIR "${source_subdir}" + + GIT_VERSION ${git_version} + GIT_HOST ${git_host} + + ARTIFACT ${artifact} + TAG ${tag}) endif() - AddPackage( - NAME "${package}" - VERSION "${version}" - URL "${url}" - HASH "${hash}" - HASH_SUFFIX "${hash_suffix}" - SHA "${sha}" - REPO "${repo}" - KEY "${key}" - PATCHES "${patches}" - OPTIONS "${options}" - FIND_PACKAGE_ARGUMENTS "${find_args}" - BUNDLED_PACKAGE "${bundled}" - SOURCE_SUBDIR "${source_subdir}" - - GIT_VERSION ${git_version} - GIT_HOST ${git_host} - - ARTIFACT ${artifact} - TAG ${tag}) - # pass stuff to parent scope - set(${package}_ADDED "${${package}_ADDED}" - PARENT_SCOPE) - set(${package}_SOURCE_DIR "${${package}_SOURCE_DIR}" - PARENT_SCOPE) - set(${package}_BINARY_DIR "${${package}_BINARY_DIR}" - PARENT_SCOPE) - + Propagate(${package}_ADDED) + Propagate(${package}_SOURCE_DIR) + Propagate(${package}_BINARY_DIR) endfunction() function(AddPackage) @@ -343,7 +409,7 @@ function(AddPackage) if(DEFINED PKG_ARGS_ARTIFACT) set(pkg_url - ${pkg_git_url}/releases/download/${PKG_ARGS_TAG}/${PKG_ARGS_ARTIFACT}) + "${pkg_git_url}/releases/download/${PKG_ARGS_TAG}/${PKG_ARGS_ARTIFACT}") else() set(pkg_url ${pkg_git_url}/archive/refs/tags/${PKG_ARGS_TAG}.tar.gz) @@ -625,7 +691,8 @@ function(AddCIPackage) endif() if (DEFINED pkgname AND NOT "${pkgname}" IN_LIST DISABLED_PLATFORMS) - set(ARTIFACT "${ARTIFACT_NAME}-${pkgname}-${ARTIFACT_VERSION}.${ARTIFACT_EXT}") + set(ARTIFACT + "${ARTIFACT_NAME}-${pkgname}-${ARTIFACT_VERSION}.${ARTIFACT_EXT}") AddPackage( NAME ${ARTIFACT_PACKAGE} diff --git a/docs/CPMUtil/AddDependentPackage.md b/docs/CPMUtil/AddDependentPackage.md new file mode 100644 index 0000000000..bb6651e4b8 --- /dev/null +++ b/docs/CPMUtil/AddDependentPackage.md @@ -0,0 +1,41 @@ +# AddDependentPackage + +Use `AddDependentPackage` when you have multiple packages that are required to all be from the system, OR bundled. This is useful in cases where e.g. versions must absolutely match. + +## Versioning + +Versioning must be handled by the package itself. + +## Examples + +### Vulkan + +`cpmfile.json` + +```json +{ + "vulkan-headers": { + "repo": "KhronosGroup/Vulkan-Headers", + "package": "VulkanHeaders", + "version": "1.4.317", + "hash": "26e0ad8fa34ab65a91ca62ddc54cc4410d209a94f64f2817dcdb8061dc621539a4262eab6387e9b9aa421db3dbf2cf8e2a4b041b696d0d03746bae1f25191272", + "git_version": "1.4.342", + "tag": "v%VERSION%" + }, + "vulkan-utility-libraries": { + "repo": "KhronosGroup/Vulkan-Utility-Libraries", + "package": "VulkanUtilityLibraries", + "hash": "8147370f964fd82c315d6bb89adeda30186098427bf3efaa641d36282d42a263f31e96e4586bfd7ae0410ff015379c19aa4512ba160630444d3d8553afd1ec14", + "git_version": "1.4.342", + "tag": "v%VERSION%" + } +} +``` + +`CMakeLists.txt`: + +```cmake +AddDependentPackages(vulkan-headers vulkan-utility-libraries) +``` + +If Vulkan Headers are installed, but NOT Vulkan Utility Libraries, then CPMUtil will throw an error. diff --git a/docs/CPMUtil/README.md b/docs/CPMUtil/README.md index ff19cb4f76..dbc5f0922a 100644 --- a/docs/CPMUtil/README.md +++ b/docs/CPMUtil/README.md @@ -31,6 +31,10 @@ The core of CPMUtil is the [`AddPackage`](./AddPackage.md) function. [`AddPackag [`AddJsonPackage`](./AddJsonPackage.md) is the recommended method of usage for CPMUtil. +## AddDependentPackage + +[`AddDependentPackage`](./AddDependentPackage.md) allows you to add multiple packages such that all of them must be from the system OR bundled. + ## AddQt [`AddQt`](./AddQt.md) adds a specific version of Qt to your project. diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index b57c52d151..980d8f668d 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -84,13 +84,7 @@ endif() AddJsonPackage(mcl) # Vulkan stuff -AddJsonPackage(vulkan-headers) -AddJsonPackage(vulkan-utility-libraries) - -# small hack -if (NOT VulkanUtilityLibraries_ADDED) - find_package(VulkanHeaders 1.3.274 REQUIRED) -endif() +AddDependentPackages(vulkan-headers vulkan-utility-libraries) # frozen AddJsonPackage(frozen) diff --git a/externals/cpmfile.json b/externals/cpmfile.json index 2f900e4ebe..7c04e389e2 100644 --- a/externals/cpmfile.json +++ b/externals/cpmfile.json @@ -276,7 +276,7 @@ "vulkan-headers": { "repo": "KhronosGroup/Vulkan-Headers", "package": "VulkanHeaders", - "version": "1.3.274", + "version": "1.4.317", "hash": "26e0ad8fa34ab65a91ca62ddc54cc4410d209a94f64f2817dcdb8061dc621539a4262eab6387e9b9aa421db3dbf2cf8e2a4b041b696d0d03746bae1f25191272", "git_version": "1.4.342", "tag": "v%VERSION%" diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 0b3aea6d2e..ab4e5b1b65 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -1230,6 +1230,10 @@ void Device::RemoveUnsuitableExtensions() { } RemoveExtensionFeatureIfUnsuitable(extensions.custom_border_color, features.custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME); + // VK_KHR_unified_image_layouts + extensions.unified_image_layouts = features.unified_image_layouts.unifiedImageLayouts; + RemoveExtensionFeatureIfUnsuitable(extensions.unified_image_layouts, features.unified_image_layouts, + VK_KHR_UNIFIED_IMAGE_LAYOUTS_EXTENSION_NAME); // VK_EXT_depth_bias_control extensions.depth_bias_control = diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index 744b5f827e..73a232ddee 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -70,7 +70,9 @@ VK_DEFINE_HANDLE(VmaAllocator) FEATURE(KHR, PipelineExecutableProperties, PIPELINE_EXECUTABLE_PROPERTIES, \ pipeline_executable_properties) \ FEATURE(KHR, WorkgroupMemoryExplicitLayout, WORKGROUP_MEMORY_EXPLICIT_LAYOUT, \ - workgroup_memory_explicit_layout) + workgroup_memory_explicit_layout) \ + FEATURE(KHR, UnifiedImageLayouts, UNIFIED_IMAGE_LAYOUTS, unified_image_layouts) + // Define miscellaneous extensions which may be used by the implementation here. #define FOR_EACH_VK_EXTENSION(EXTENSION) \ diff --git a/tools/cpm/common.sh b/tools/cpm/common.sh index 2dc3a477d4..97a2fed003 100755 --- a/tools/cpm/common.sh +++ b/tools/cpm/common.sh @@ -23,7 +23,7 @@ MAXDEPTH=3 # For your project you'll want to change this to define what dirs you have cpmfiles in # Remember to account for the MAXDEPTH variable! # Adding ./ before each will help to remove duplicates -CPMFILES=$(find . src -maxdepth "$MAXDEPTH" -name cpmfile.json | sort | uniq) +CPMFILES=$(find . -maxdepth "$MAXDEPTH" -name cpmfile.json | sort | uniq) # shellcheck disable=SC2016 PACKAGES=$(echo "$CPMFILES" | xargs jq -s 'reduce .[] as $item ({}; . * $item)')