diff --git a/src/xrt/auxiliary/util/CMakeLists.txt b/src/xrt/auxiliary/util/CMakeLists.txt index f3858a897..d45cd60c9 100644 --- a/src/xrt/auxiliary/util/CMakeLists.txt +++ b/src/xrt/auxiliary/util/CMakeLists.txt @@ -1,5 +1,5 @@ # Copyright 2019-2024, Collabora, Ltd. -# Copyright 2024-2025, NVIDIA CORPORATION. +# Copyright 2024-2026, NVIDIA CORPORATION. # SPDX-License-Identifier: BSL-1.0 #### @@ -89,9 +89,9 @@ add_library( u_session.h u_space_overseer.c u_space_overseer.h - u_string_list.cpp - u_string_list.h - u_string_list.hpp + u_extension_list.cpp + u_extension_list.h + u_extension_list.hpp u_system.c u_system.h u_system_helpers.c diff --git a/src/xrt/auxiliary/util/u_extension_list.cpp b/src/xrt/auxiliary/util/u_extension_list.cpp new file mode 100644 index 000000000..c8bbfd7e9 --- /dev/null +++ b/src/xrt/auxiliary/util/u_extension_list.cpp @@ -0,0 +1,449 @@ +// Copyright 2021-2026, Collabora, Ltd. +// Copyright 2025-2026, NVIDIA CORPORATION. +// SPDX-License-Identifier: BSL-1.0 +/*! + * @file + * @brief A collection of strings for extensions. + * + * @author Rylie Pavlik + * @ingroup aux_util + * + */ + +#include "u_extension_list.h" +#include "u_extension_list.hpp" + +#include +#include +#include +#include + + +/* + * + * Defines and structs. + * + */ + +using xrt::auxiliary::util::ExtensionList; +using xrt::auxiliary::util::ExtensionListBuilder; + +struct u_extension_list +{ + u_extension_list() = default; + u_extension_list(ExtensionList &&extension_list) : list(std::move(extension_list)) {} + + ExtensionList list; +}; + +struct u_extension_list_builder +{ + u_extension_list_builder() = default; + u_extension_list_builder(size_t capacity) : builder(capacity) {} + + u_extension_list_builder(u_extension_list_builder &&) = delete; + u_extension_list_builder(u_extension_list_builder const &) = delete; + + u_extension_list_builder & + operator=(u_extension_list_builder &&) = delete; + u_extension_list_builder & + operator=(u_extension_list_builder const &) = delete; + + ExtensionListBuilder builder; +}; + + +/* + * + * Helpers + * + */ + +enum class ExtensionType +{ + KHR = 0, // Khronos extensions + EXT = 1, // Multi-vendor extensions + VENDOR = 2, // Vendor-specific extensions (AMD, NV, INTEL, etc.) + EXPERIMENTAL = 3 // Experimental extensions +}; + +/*! + * @brief Helper class for sorting extension names. + * + * Encapsulates the sort key with comparison operators for cleaner sorting. + */ +struct ExtensionSortKey +{ + std::string_view api_prefix; + ExtensionType type; + std::string_view name; + + /*! + * Sorts in field declaration order: + * + * 1. API prefix (VK, XR, etc.) + * 2. Extension type (KHR < EXT < VENDOR < EXPERIMENTAL) + * 3. Alphabetically by full name + */ + auto + operator<=>(const ExtensionSortKey &other) const = default; +}; + +/*! + * @brief Check if a vendor string represents an experimental extension. + * + * Experimental extensions have vendor codes ending with 'X' optionally followed + * by digits. + * + * Examples: NVX, AMDX, NVX1, NVX2, INTELX + * + * Special case: QNX is a legitimate vendor (QNX operating system), not + * experimental. + */ +static bool +is_experimental_vendor(std::string_view vendor) +{ + // Special case: QNX is a legitimate vendor, not experimental + if (vendor == "QNX") { + return false; + } + + // Check if vendor ends with 'X' optionally followed by digits + if (vendor.empty()) { + return false; + } + + size_t len = vendor.length(); + size_t i = len - 1; + + // Skip trailing digits + while (i > 0 && std::isdigit(vendor[i])) { + i--; + } + + // Check if we found an 'X' and there's something before it + return (vendor[i] == 'X' && i > 0); +} + +/*! + * @brief Get the extension type and API prefix for sorting purposes. + * + * Returns an ExtensionSortKey for comparison. + */ +static ExtensionSortKey +get_extension_sort_key(std::string_view name) +{ + // Find the first underscore to separate API prefix (e.g., "VK", "XR") + size_t first_underscore = name.find('_'); + if (first_underscore == std::string::npos) { + // No underscore, treat as is + return {name, ExtensionType::VENDOR, name}; + } + + std::string_view api_prefix = name.substr(0, first_underscore); + + // Find the second underscore to get the vendor/type part + size_t second_underscore = name.find('_', first_underscore + 1); + if (second_underscore == std::string::npos) { + // Only one underscore, treat as vendor + return {api_prefix, ExtensionType::VENDOR, name}; + } + + std::string_view vendor = name.substr(first_underscore + 1, second_underscore - first_underscore - 1); + + // Determine extension type based on vendor string + ExtensionType type; + if (vendor == "KHR") { + type = ExtensionType::KHR; + } else if (vendor == "EXT") { + type = ExtensionType::EXT; + } else if (is_experimental_vendor(vendor)) { + type = ExtensionType::EXPERIMENTAL; + } else { + type = ExtensionType::VENDOR; + } + + return {api_prefix, type, name}; +} + + +/* + * + * 'Exported' functions - Immutable Extension List + * + */ + +struct u_extension_list * +u_extension_list_create() +{ + try { + auto ret = std::make_unique(); + return ret.release(); + } catch (std::exception const &) { + return nullptr; + } +} + +struct u_extension_list * +u_extension_list_create_from_array(const char *const *arr, uint32_t size) +{ + if (arr == nullptr || size == 0) { + return u_extension_list_create(); + } + try { + ExtensionListBuilder builder(size); + for (uint32_t i = 0; i < size; ++i) { + if (arr[i] != nullptr) { + builder.append(arr[i]); + } + } + auto ret = std::make_unique(std::move(builder).build()); + return ret.release(); + } catch (std::exception const &) { + return nullptr; + } +} + +uint32_t +u_extension_list_get_size(const struct u_extension_list *uel) +{ + if (uel == nullptr) { + return 0; + } + return uel->list.size(); +} + +const char *const * +u_extension_list_get_data(const struct u_extension_list *uel) +{ + if (uel == nullptr) { + return nullptr; + } + return uel->list.data(); +} + +bool +u_extension_list_contains(const struct u_extension_list *uel, const char *str) +{ + if (uel == nullptr || str == nullptr) { + return false; + } + return uel->list.contains(str); +} + +bool +u_extension_list_contains_len(const struct u_extension_list *uel, const char *str, size_t len) +{ + if (uel == nullptr || str == nullptr) { + return false; + } + return uel->list.contains(std::string_view(str, len)); +} + +void +u_extension_list_destroy(struct u_extension_list **list_ptr) +{ + if (list_ptr == nullptr) { + return; + } + u_extension_list *list = *list_ptr; + if (list == nullptr) { + return; + } + delete list; + *list_ptr = nullptr; +} + + +/* + * + * Extension List Builder Methods + * + */ + +void +ExtensionListBuilder::sortForExtensions() +{ + if (strings.empty()) { + return; + } + + // Sort using our custom comparison function that works with std::string + auto cmp = [](const std::string &a, const std::string &b) { + return get_extension_sort_key(a) < get_extension_sort_key(b); + }; + std::sort(strings.begin(), strings.end(), cmp); +} + + +/* + * + * 'Exported' functions - Extension List Builder + * + */ + +struct u_extension_list_builder * +u_extension_list_builder_create() +{ + try { + auto ret = std::make_unique(); + return ret.release(); + } catch (std::exception const &) { + return nullptr; + } +} + +struct u_extension_list_builder * +u_extension_list_builder_create_with_capacity(uint32_t capacity) +{ + try { + auto ret = std::make_unique(capacity); + return ret.release(); + } catch (std::exception const &) { + return nullptr; + } +} + +struct u_extension_list_builder * +u_extension_list_builder_create_from_array(const char *const *arr, uint32_t size) +{ + if (arr == nullptr || size == 0) { + return u_extension_list_builder_create(); + } + try { + auto ret = std::make_unique(); + for (uint32_t i = 0; i < size; ++i) { + if (arr[i] != nullptr) { + ret->builder.append(arr[i]); + } + } + return ret.release(); + } catch (std::exception const &) { + return nullptr; + } +} + +int +u_extension_list_builder_append(struct u_extension_list_builder *uelb, const char *str) +{ + if (uelb == nullptr || str == nullptr) { + return -1; + } + try { + uelb->builder.append(str); + return 1; + } catch (std::exception const &) { + return -1; + } +} + +int +u_extension_list_builder_append_len(struct u_extension_list_builder *uelb, const char *str, size_t len) +{ + if (uelb == nullptr || str == nullptr) { + return -1; + } + try { + uelb->builder.append(std::string_view(str, len)); + return 1; + } catch (std::exception const &) { + return -1; + } +} + +int +u_extension_list_builder_append_array(struct u_extension_list_builder *uelb, const char *const *arr, uint32_t size) +{ + if (uelb == nullptr || arr == nullptr) { + return -1; + } + try { + for (uint32_t i = 0; i < size; ++i) { + if (arr[i] != nullptr) { + uelb->builder.append(arr[i]); + } + } + return 1; + } catch (std::exception const &) { + return -1; + } +} + +int +u_extension_list_builder_append_unique(struct u_extension_list_builder *uelb, const char *str) +{ + if (uelb == nullptr || str == nullptr) { + return -1; + } + try { + auto added = uelb->builder.appendUnique(str); + return added ? 1 : 0; + } catch (std::exception const &) { + return -1; + } +} + +int +u_extension_list_builder_append_unique_len(struct u_extension_list_builder *uelb, const char *str, size_t len) +{ + if (uelb == nullptr || str == nullptr) { + return -1; + } + try { + auto added = uelb->builder.appendUnique(std::string_view(str, len)); + return added ? 1 : 0; + } catch (std::exception const &) { + return -1; + } +} + +struct u_extension_list * +u_extension_list_builder_build(struct u_extension_list_builder **builder_ptr) +{ + u_extension_list_builder *builder = *builder_ptr; + if (builder == nullptr) { + return nullptr; + } + + try { + auto ret = std::make_unique(std::move(builder->builder).build()); + delete builder; + *builder_ptr = nullptr; + return ret.release(); + } catch (std::exception const &) { + return nullptr; + } +} + +struct u_extension_list * +u_extension_list_builder_build_sorted_for_extensions(struct u_extension_list_builder **builder_ptr) +{ + u_extension_list_builder *builder = *builder_ptr; + if (builder == nullptr) { + return nullptr; + } + + try { + // Sort the builder in-place before building + builder->builder.sortForExtensions(); + + // Now build the sorted list + auto ret = std::make_unique(std::move(builder->builder).build()); + delete builder; + *builder_ptr = nullptr; + return ret.release(); + } catch (std::exception const &) { + return nullptr; + } +} + +void +u_extension_list_builder_destroy(struct u_extension_list_builder **builder_ptr) +{ + u_extension_list_builder *builder = *builder_ptr; + if (builder == nullptr) { + return; + } + delete builder; + *builder_ptr = nullptr; +} diff --git a/src/xrt/auxiliary/util/u_extension_list.h b/src/xrt/auxiliary/util/u_extension_list.h new file mode 100644 index 000000000..e2e9b2b5f --- /dev/null +++ b/src/xrt/auxiliary/util/u_extension_list.h @@ -0,0 +1,283 @@ +// Copyright 2021, Collabora, Ltd. +// Copyright 2025-2026, NVIDIA CORPORATION. +// SPDX-License-Identifier: BSL-1.0 +/*! + * @file + * @brief A collection of strings, like a list of extensions to enable + * + * @author Rylie Pavlik + * @ingroup aux_util + * + */ +#pragma once + +#include "xrt/xrt_compiler.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +/*! + * @brief An immutable collection of extension strings, these are used to + * interface with APIs such as Vulkan and OpenXR who use strings for extensions. + * + * This structure stores copies of strings internally and provides read-only access. + * + * @see xrt::auxiliary::util::ExtensionList + */ +struct u_extension_list; + +/*! + * @brief A builder for constructing immutable extension lists. + * + * This structure allows write-only operations to build up a list of extension + * strings. + * + * @see xrt::auxiliary::util::ExtensionListBuilder + */ +struct u_extension_list_builder; + +/* + * + * Immutable Extension List API + * + */ + +/*! + * @brief Create an empty extension list, some APIs might require an extension + * list to be passed in so there is some utility for this function. + * + * @public @memberof u_extension_list + */ +struct u_extension_list * +u_extension_list_create(void); + +/*! + * @brief Create a new extension list from an array of strings. + * + * The strings are copied into the list. + * + * @param arr an array of zero or more non-null, null-terminated strings. + * @param size the number of elements in the array. + * + * @public @memberof u_extension_list + */ +struct u_extension_list * +u_extension_list_create_from_array(const char *const *arr, uint32_t size); + +/*! + * @brief Retrieve the number of elements in the list + * + * @public @memberof u_extension_list + */ +uint32_t +u_extension_list_get_size(const struct u_extension_list *uel); + +/*! + * @brief Retrieve the data pointer of the list + * + * @public @memberof u_extension_list + */ +const char *const * +u_extension_list_get_data(const struct u_extension_list *uel); + +/*! + * @brief Check if the string is in the list. + * + * (Comparing string contents, not pointers) + * + * @param usl self pointer + * @param str a non-null, null-terminated string. + * + * @return true if the string is in the list. + * + * @public @memberof u_extension_list + */ +bool +u_extension_list_contains(const struct u_extension_list *uel, const char *str); + +/*! + * @brief Check if the string is in the list. + * + * (Comparing string contents, not pointers) + * + * @param usl self pointer + * @param str a string pointer. + * @param len the length of the string. + * + * @return true if the string is in the list. + * + * @public @memberof u_extension_list + */ +bool +u_extension_list_contains_len(const struct u_extension_list *uel, const char *str, size_t len); + +/*! + * @brief Destroy an extension list. + * + * Performs null checks and sets your pointer to zero. + * + * @public @memberof u_extension_list + */ +void +u_extension_list_destroy(struct u_extension_list **list_ptr); + + +/* + * + * Extension List Builder API + * + */ + +/*! + * @brief Create an extension list builder. + * + * @public @memberof u_extension_list_builder + */ +struct u_extension_list_builder * +u_extension_list_builder_create(void); + +/*! + * @brief Create an extension list builder with room for at least the given number of strings. + * + * @public @memberof u_extension_list_builder + */ +struct u_extension_list_builder * +u_extension_list_builder_create_with_capacity(uint32_t capacity); + +/*! + * @brief Create an extension list builder from an array of strings. + * + * The strings are copied into the builder. + * + * @param arr an array of zero or more non-null, null-terminated strings. + * @param size the number of elements in the array. + * + * @public @memberof u_extension_list_builder + */ +struct u_extension_list_builder * +u_extension_list_builder_create_from_array(const char *const *arr, uint32_t size); + +/*! + * @brief Append a new string to the builder. + * + * The string is copied into the builder. + * + * @param uslb self pointer + * @param str a non-null, null-terminated string. + * @return 1 if successfully added, negative for errors. + * + * @public @memberof u_extension_list_builder + */ +int +u_extension_list_builder_append(struct u_extension_list_builder *uelb, const char *str); + +/*! + * @brief Append a new string to the builder. + * + * The string is copied into the builder. + * + * @param uslb self pointer + * @param str a string pointer. + * @param len the length of the string. + * @return 1 if successfully added, negative for errors. + * + * @public @memberof u_extension_list_builder + */ +int +u_extension_list_builder_append_len(struct u_extension_list_builder *uelb, const char *str, size_t len); + +/*! + * @brief Append an array of new strings to the builder. + * + * The strings are copied into the builder. + * + * @param uslb self pointer + * @param arr an array of zero or more non-null, null-terminated strings. + * @param size the number of elements in the array. + * @return 1 if successfully added, negative for errors. + * + * @public @memberof u_extension_list_builder + */ +int +u_extension_list_builder_append_array(struct u_extension_list_builder *uelb, const char *const *arr, uint32_t size); + +/*! + * @brief Append a new string to the builder, if it's not the same as a string already in the builder. + * + * (Comparing string contents, not pointers) + * + * The string is copied into the builder. + * + * @param uslb self pointer + * @param str a non-null, null-terminated string. + * @return 1 if successfully added, 0 if already existing so not added, negative for errors. + * + * @public @memberof u_extension_list_builder + */ +int +u_extension_list_builder_append_unique(struct u_extension_list_builder *uelb, const char *str); + +/*! + * @brief Append a new string to the builder, if it's not the same as a string already in the builder. + * + * (Comparing string contents, not pointers) + * + * The string is copied into the builder. + * + * @param uslb self pointer + * @param str a string pointer. + * @param len the length of the string. + * @return 1 if successfully added, 0 if already existing so not added, negative for errors. + * + * @public @memberof u_extension_list_builder + */ +int +u_extension_list_builder_append_unique_len(struct u_extension_list_builder *uelb, const char *str, size_t len); + +/*! + * @brief Build an immutable extension list from the builder. + * + * After calling this, the builder is destroyed and the pointer is set to NULL. + * + * @param builder_ptr pointer to self pointer + * @return a new immutable extension list, or NULL on error. + * + * @public @memberof u_extension_list_builder + */ +XRT_NONNULL_ALL struct u_extension_list * +u_extension_list_builder_build(struct u_extension_list_builder **builder_ptr); + +/*! + * @brief Build an immutable extension list from the builder and sort it for + * extension lists. + * + * The list will be sorted first by API (VK, XR, etc.), then all KHR extensions, + * then all EXT extensions, then all Vendor extensions, then all experimental + * extensions. (Alphabetical within each group.) + * + * After calling this, the builder is destroyed and the pointer is set to NULL. + * + * @param builder_ptr pointer to self pointer + * @return a new immutable extension list, sorted for extension lists, or NULL on error. + * + * @public @memberof u_extension_list_builder + */ +XRT_NONNULL_ALL struct u_extension_list * +u_extension_list_builder_build_sorted_for_extensions(struct u_extension_list_builder **builder_ptr); + +/*! + * @brief Destroy an extension list builder. + * + * Performs null checks and sets your pointer to zero. + * + * @public @memberof u_extension_list_builder + */ +XRT_NONNULL_ALL void +u_extension_list_builder_destroy(struct u_extension_list_builder **builder_ptr); + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/src/xrt/auxiliary/util/u_extension_list.hpp b/src/xrt/auxiliary/util/u_extension_list.hpp new file mode 100644 index 000000000..6dac3b750 --- /dev/null +++ b/src/xrt/auxiliary/util/u_extension_list.hpp @@ -0,0 +1,249 @@ +// Copyright 2021, Collabora, Ltd. +// Copyright 2025-2026, NVIDIA CORPORATION. +// SPDX-License-Identifier: BSL-1.0 +/*! + * @file + * @brief A collection of strings, like a list of extensions to enable + * + * @author Rylie Pavlik + * @ingroup aux_util + * + */ + +#pragma once + +#include "u_extension_list.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace xrt::auxiliary::util { + +// Forward declaration +class ExtensionListBuilder; + +/*! + * @brief An immutable collection of strings, like a list of extensions to enable. + * + * This class stores copies of strings internally and provides read-only access. + * + * Size is limited to one less than the max value of uint32_t which shouldn't be a problem, + * the size really should be much smaller. + */ +class ExtensionList +{ +public: + //! Default constructor - creates an empty list + ExtensionList() = default; + + //! Move constructor + ExtensionList(ExtensionList &&) = default; + + //! Copy constructor, makes sure to do a deep copy. + ExtensionList(ExtensionList const &other) : ExtensionList({other.strings.begin(), other.strings.end()}) {} + + //! Move assignment + ExtensionList & + operator=(ExtensionList &&) = default; + + //! Copy assignment + ExtensionList & + operator=(ExtensionList const &other) + { + /* + * This uses the move assignment to set this to a deep copy of + * the other list. We need to do a deep copy of the other list + * so that the set and pointers are correctly recalculated using + * the new strings. + */ + return *this = ExtensionList(other); + } + + /*! + * @brief Get the size of the array (the number of strings) + */ + uint32_t + size() const noexcept + { + return static_cast(ptrs.size()); + } + + /*! + * @brief Get the data pointer of the array (array of const char*) + */ + const char *const * + data() const noexcept + { + return ptrs.data(); + } + + /*! + * @brief Check if the string is in the list. + * + * (Comparing string contents) + * + * @param str a string view to search for. + * + * @return true if the string is in the list. + */ + bool + contains(std::string_view str) const + { + return set.find(str) != set.end(); + } + + +private: + friend class ExtensionListBuilder; + + //! Main storage of strings. + std::vector strings; + + /*! + * Internal set of strings for fast lookup, memory kept alive by the + * strings vector. Used to quickly check if a string is in the list. + */ + std::unordered_set set; + + /*! + * Internal vector of pointers to the strings, memory kept alive by the + * strings vector. + */ + std::vector ptrs; + + +private: + // Internal constructor used by ExtensionListBuilder + ExtensionList(std::vector &&strings_) : strings(std::move(strings_)) + { + ptrs.reserve(strings.size()); + for (const auto &s : strings) { + set.insert(s); + ptrs.push_back(s.c_str()); + } + } +}; + +/*! + * @brief A builder for constructing ExtensionList objects. + * + * This class allows write-only operations to build up an extension list, + * which can then be converted to an immutable ExtensionList. + */ +class ExtensionListBuilder +{ +public: + //! Default constructor + ExtensionListBuilder() = default; + + //! Construct with capacity hint + explicit ExtensionListBuilder(uint32_t capacity) + { + strings.reserve(capacity); + } + + //! Construct from an array of strings + template explicit ExtensionListBuilder(const char *(&arr)[N]) + { + strings.reserve(N); + for (auto &&elt : arr) { + append(elt); + } + } + + ExtensionListBuilder(ExtensionListBuilder &&) = delete; + ExtensionListBuilder(ExtensionListBuilder const &) = delete; + + ExtensionListBuilder & + operator=(ExtensionListBuilder &&) = delete; + ExtensionListBuilder & + operator=(ExtensionListBuilder const &) = delete; + + /*! + * @brief Append a new string to the builder. + * + * @param str a string view to append. + * + * @throws std::out_of_range if you have a ridiculous number of strings in your list already. + */ + void + append(std::string_view str) + { + if (strings.size() > (std::numeric_limits::max)() - 1) { + throw std::out_of_range("Size limit reached"); + } + strings.emplace_back(str); + } + + /*! + * @brief Append a new string to the builder if it doesn't match any existing string. + * + * (Comparing string contents) + * + * @param str a string view to append. + * + * @return true if we added it + * + * @throws std::out_of_range if you have a ridiculous number of strings in your list already. + */ + bool + appendUnique(std::string_view str) + { + if (strings.size() > (std::numeric_limits::max)() - 1) { + throw std::out_of_range("Size limit reached"); + } + auto it = + std::find_if(strings.begin(), strings.end(), [str](const std::string &elt) { return str == elt; }); + if (it != strings.end()) { + // already have it + return false; + } + strings.emplace_back(str); + return true; + } + + /*! + * @brief Sort the strings in the builder suitable for extension lists. + * + * The list will be sorted first by API (VK, XR, etc.), then all KHR extensions, + * then all EXT extensions, then all Vendor extensions, then all experimental + * extensions. (Alphabetical within each group.) + * + * This modifies the builder in-place. + */ + void + sortForExtensions(); + + /*! + * @brief Build and return an immutable ExtensionList. + * + * This function consumes the builder by moving its internal data into + * the returned ExtensionList. After calling this, the builder is left in a + * valid but empty state. + * + * The && qualifier (rvalue reference qualifier) means this can only be + * called on rvalue references, i.e. on: + * - Temporaries: `ExtensionListBuilder().build()` + * - Moved objects: `std::move(builder).build()` + * + * This prevents accidentally calling build() twice on the same builder, + * which would be a bug. + */ + ExtensionList + build() && + { + return ExtensionList(std::move(strings)); + } + + +private: + std::vector strings; +}; + +} // namespace xrt::auxiliary::util diff --git a/src/xrt/auxiliary/util/u_pretty_print.c b/src/xrt/auxiliary/util/u_pretty_print.c index 079226d36..41c764fe7 100644 --- a/src/xrt/auxiliary/util/u_pretty_print.c +++ b/src/xrt/auxiliary/util/u_pretty_print.c @@ -11,7 +11,7 @@ #include "xrt/xrt_macro_lists.h" #include "util/u_misc.h" -#include "util/u_string_list.h" +#include "util/u_extension_list.h" #include "util/u_pretty_print.h" #include @@ -87,14 +87,14 @@ stack_only_sink(void *ptr, const char *str, size_t length) } int -update_longest_extension_name_length(struct u_string_list *list, int current_longest_extension) +update_longest_extension_name_length(struct u_extension_list *list, int current_longest_extension) { if (list == NULL) { return current_longest_extension; } - size_t count = u_string_list_get_size(list); - const char *const *data = u_string_list_get_data(list); + size_t count = u_extension_list_get_size(list); + const char *const *data = u_extension_list_get_data(list); for (size_t i = 0; i < count; i++) { int len = (int)strlen(data[i]); @@ -498,15 +498,15 @@ u_pp_array2d_f64(u_pp_delegate_t dg, const double *arr, size_t n, size_t m, cons /* * - * String list printers. + * Extension list printers. * */ void -u_pp_string_list(struct u_pp_delegate dg, struct u_string_list *usl, const char *prefix) +u_pp_string_list(struct u_pp_delegate dg, struct u_extension_list *usl, const char *prefix) { - uint32_t count = u_string_list_get_size(usl); - const char *const *data = u_string_list_get_data(usl); + uint32_t count = u_extension_list_get_size(usl); + const char *const *data = u_extension_list_get_data(usl); for (uint32_t i = 0; i < count; i++) { u_pp(dg, "%s%s", prefix, data[i]); @@ -515,15 +515,15 @@ u_pp_string_list(struct u_pp_delegate dg, struct u_string_list *usl, const char void u_pp_string_list_extensions(struct u_pp_delegate dg, - struct u_string_list *enabled_list, - struct u_string_list *optional_list, - struct u_string_list *skipped_list) + struct u_extension_list *enabled_list, + struct u_extension_list *optional_list, + struct u_extension_list *skipped_list) { assert(optional_list != NULL); assert(skipped_list != NULL); - uint32_t count = u_string_list_get_size(enabled_list); - const char *const *data = u_string_list_get_data(enabled_list); + uint32_t count = u_extension_list_get_size(enabled_list); + const char *const *data = u_extension_list_get_data(enabled_list); // Find the longest extension name for alignment int longest_extension = update_longest_extension_name_length(enabled_list, 0); @@ -536,7 +536,7 @@ u_pp_string_list_extensions(struct u_pp_delegate dg, uint32_t optional_enabled_count = 0; for (uint32_t i = 0; i < count; i++) { const char *ext = data[i]; - bool is_optional = u_string_list_contains(optional_list, ext); + bool is_optional = u_extension_list_contains(optional_list, ext); if (is_optional) { optional_enabled_count++; } @@ -545,22 +545,22 @@ u_pp_string_list_extensions(struct u_pp_delegate dg, } // All optional extensions have been enabled. - uint32_t optional_count = u_string_list_get_size(optional_list); + uint32_t optional_count = u_extension_list_get_size(optional_list); if (optional_enabled_count == optional_count) { return; } // Print optional extensions that were not enabled u_pp(dg, "\n\tNot enabled optional extensions:"); - const char *const *optional_data = u_string_list_get_data(optional_list); + const char *const *optional_data = u_extension_list_get_data(optional_list); for (uint32_t i = 0; i < optional_count; i++) { const char *ext = optional_data[i]; - if (u_string_list_contains(enabled_list, ext)) { + if (u_extension_list_contains(enabled_list, ext)) { continue; } // Check if this extension was skipped or unsupported - bool was_skipped = u_string_list_contains(skipped_list, ext); + bool was_skipped = u_extension_list_contains(skipped_list, ext); const char *reason = was_skipped ? "skipped" : "unsupported"; u_pp(dg, "\n\t\t%-*s %s", longest_extension, ext, reason); diff --git a/src/xrt/auxiliary/util/u_pretty_print.h b/src/xrt/auxiliary/util/u_pretty_print.h index 813751d9e..1add87ea0 100644 --- a/src/xrt/auxiliary/util/u_pretty_print.h +++ b/src/xrt/auxiliary/util/u_pretty_print.h @@ -238,11 +238,11 @@ u_pp_array2d_f64(u_pp_delegate_t dg, const double *arr, size_t n, size_t m, cons /* * - * String list printers. + * Extension list printers. * */ -struct u_string_list; +struct u_extension_list; /*! * @brief Print all the strings in the list with the given prefix. @@ -253,10 +253,10 @@ struct u_string_list; * @ingroup aux_pretty */ XRT_NONNULL_ALL void -u_pp_string_list(struct u_pp_delegate dg, struct u_string_list *usl, const char *prefix); +u_pp_string_list(struct u_pp_delegate dg, struct u_extension_list *usl, const char *prefix); /*! - * @brief Pretty print the string list with extension information. + * @brief Pretty print the extension list with extension information. * * It will start on a new line with the enabled extensions, showing which are * required vs optional. Then if there are optional extensions not enabled it @@ -270,9 +270,9 @@ u_pp_string_list(struct u_pp_delegate dg, struct u_string_list *usl, const char */ XRT_NONNULL_ALL void u_pp_string_list_extensions(struct u_pp_delegate dg, - struct u_string_list *enabled_list, - struct u_string_list *optional_list, - struct u_string_list *skipped_list); + struct u_extension_list *enabled_list, + struct u_extension_list *optional_list, + struct u_extension_list *skipped_list); /* diff --git a/src/xrt/auxiliary/util/u_string_list.cpp b/src/xrt/auxiliary/util/u_string_list.cpp deleted file mode 100644 index 8bd0f7129..000000000 --- a/src/xrt/auxiliary/util/u_string_list.cpp +++ /dev/null @@ -1,311 +0,0 @@ -// Copyright 2021, Collabora, Ltd. -// Copyright 2025-2026, NVIDIA CORPORATION. -// SPDX-License-Identifier: BSL-1.0 -/*! - * @file - * @brief A collection of strings, like a list of extensions to enable - * - * @author Rylie Pavlik - * @ingroup aux_util - * - */ - -#include "u_string_list.h" -#include "u_string_list.hpp" - -#include -#include -#include - - -/* - * - * Defines and structs. - * - */ - -using xrt::auxiliary::util::StringList; - -struct u_string_list -{ - u_string_list() = default; - u_string_list(StringList &&sl) : list(std::move(sl)) {} - - StringList list; -}; - - -/* - * - * Helpers - * - */ - -enum class ExtensionType -{ - KHR = 0, // Khronos extensions - EXT = 1, // Multi-vendor extensions - VENDOR = 2, // Vendor-specific extensions (AMD, NV, INTEL, etc.) - EXPERIMENTAL = 3 // Experimental extensions -}; - -/*! - * @brief Helper class for sorting extension names. - * - * Encapsulates the sort key with comparison operators for cleaner sorting. - */ -struct ExtensionSortKey -{ - std::string api_prefix; - ExtensionType type; - std::string name; - - /*! - * Sorts in field declaration order: - * - * 1. API prefix (VK, XR, etc.) - * 2. Extension type (KHR < EXT < VENDOR < EXPERIMENTAL) - * 3. Alphabetically by full name - */ - auto - operator<=>(const ExtensionSortKey &other) const = default; -}; - -/*! - * @brief Check if a vendor string represents an experimental extension. - * - * Experimental extensions have vendor codes ending with 'X' optionally followed - * by digits. - * - * Examples: NVX, AMDX, NVX1, NVX2, INTELX - * - * Special case: QNX is a legitimate vendor (QNX operating system), not - * experimental. - */ -static bool -is_experimental_vendor(const std::string &vendor) -{ - // Special case: QNX is a legitimate vendor, not experimental - if (vendor == "QNX") { - return false; - } - - // Check if vendor ends with 'X' optionally followed by digits - if (vendor.empty()) { - return false; - } - - size_t len = vendor.length(); - size_t i = len - 1; - - // Skip trailing digits - while (i > 0 && std::isdigit(vendor[i])) { - i--; - } - - // Check if we found an 'X' and there's something before it - return (vendor[i] == 'X' && i > 0); -} - -/*! - * @brief Get the extension type and API prefix for sorting purposes. - * - * Returns an ExtensionSortKey for comparison. - */ -static ExtensionSortKey -get_extension_sort_key(const char *ext) -{ - std::string name(ext); - - // Find the first underscore to separate API prefix (e.g., "VK", "XR") - size_t first_underscore = name.find('_'); - if (first_underscore == std::string::npos) { - // No underscore, treat as is - return {name, ExtensionType::VENDOR, name}; - } - - std::string api_prefix = name.substr(0, first_underscore); - - // Find the second underscore to get the vendor/type part - size_t second_underscore = name.find('_', first_underscore + 1); - if (second_underscore == std::string::npos) { - // Only one underscore, treat as vendor - return {api_prefix, ExtensionType::VENDOR, name}; - } - - std::string vendor = name.substr(first_underscore + 1, second_underscore - first_underscore - 1); - - // Determine extension type based on vendor string - ExtensionType type; - if (vendor == "KHR") { - type = ExtensionType::KHR; - } else if (vendor == "EXT") { - type = ExtensionType::EXT; - } else if (is_experimental_vendor(vendor)) { - type = ExtensionType::EXPERIMENTAL; - } else { - type = ExtensionType::VENDOR; - } - - return {api_prefix, type, name}; -} - - -/* - * - * 'Exported' functions. - * - */ - -struct u_string_list * -u_string_list_create() -{ - try { - auto ret = std::make_unique(); - return ret.release(); - } catch (std::exception const &) { - return nullptr; - } -} - -struct u_string_list * -u_string_list_create_with_capacity(uint32_t capacity) -{ - - try { - auto ret = std::make_unique(xrt::auxiliary::util::StringList{capacity}); - return ret.release(); - } catch (std::exception const &) { - return nullptr; - } -} - -struct u_string_list * -u_string_list_create_from_list(struct u_string_list *usl) -{ - try { - auto ret = std::make_unique(xrt::auxiliary::util::StringList{usl->list}); - return ret.release(); - } catch (std::exception const &) { - return nullptr; - } -} - -struct u_string_list * -u_string_list_create_from_array(const char *const *arr, uint32_t size) -{ - if (arr == nullptr || size == 0) { - return u_string_list_create(); - } - try { - auto ret = std::make_unique(xrt::auxiliary::util::StringList{size}); - for (uint32_t i = 0; i < size; ++i) { - ret->list.push_back(arr[i]); - } - return ret.release(); - } catch (std::exception const &) { - return nullptr; - } -} - -uint32_t -u_string_list_get_size(const struct u_string_list *usl) -{ - if (usl == nullptr) { - return 0; - } - return usl->list.size(); -} - -const char *const * -u_string_list_get_data(const struct u_string_list *usl) -{ - - if (usl == nullptr) { - return nullptr; - } - return usl->list.data(); -} - - -int -u_string_list_append(struct u_string_list *usl, const char *str) -{ - if (usl == nullptr) { - return -1; - } - try { - usl->list.push_back(str); - return 1; - } catch (std::exception const &) { - return -1; - } -} - -int -u_string_list_append_array(struct u_string_list *usl, const char *const *arr, uint32_t size) -{ - - if (usl == nullptr) { - return -1; - } - try { - for (uint32_t i = 0; i < size; ++i) { - usl->list.push_back(arr[i]); - } - return 1; - } catch (std::exception const &) { - return -1; - } -} - -int -u_string_list_append_unique(struct u_string_list *usl, const char *str) -{ - if (usl == nullptr) { - return -1; - } - try { - auto added = usl->list.push_back_unique(str); - return added ? 1 : 0; - } catch (std::exception const &) { - return -1; - } -} - -bool -u_string_list_contains(struct u_string_list *usl, const char *str) -{ - return usl->list.contains(str); -} - -void -u_string_list_sort_extensions(struct u_string_list *usl) -{ - if (usl == nullptr || usl->list.size() == 0) { - return; - } - - // Get the data pointer and size from the StringList - // We need to const_cast because std::sort needs non-const iterators - auto data_ptr = const_cast(usl->list.data()); - auto count = usl->list.size(); - auto cmp = [](const char *a, const char *b) { return get_extension_sort_key(a) < get_extension_sort_key(b); }; - - // Sort using our custom comparison function - std::sort(data_ptr, data_ptr + count, cmp); -} - -void -u_string_list_destroy(struct u_string_list **list_ptr) -{ - if (list_ptr == nullptr) { - return; - } - u_string_list *list = *list_ptr; - if (list == nullptr) { - return; - } - delete list; - *list_ptr = nullptr; -} diff --git a/src/xrt/auxiliary/util/u_string_list.h b/src/xrt/auxiliary/util/u_string_list.h deleted file mode 100644 index d6c542abe..000000000 --- a/src/xrt/auxiliary/util/u_string_list.h +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright 2021, Collabora, Ltd. -// Copyright 2025-2026, NVIDIA CORPORATION. -// SPDX-License-Identifier: BSL-1.0 -/*! - * @file - * @brief A collection of strings, like a list of extensions to enable - * - * @author Rylie Pavlik - * @ingroup aux_util - * - */ -#pragma once - -#include "xrt/xrt_compiler.h" - - -#ifdef __cplusplus -extern "C" { -#endif - - -/*! - * @brief A collection of string literals (const char *), such as used for extension name lists. - * - * @see xrt::auxiliary::util::StringList - */ -struct u_string_list; - -/*! - * @brief Create a string list with room for at least the given number of strings. - * - * @public @memberof u_string_list - */ -struct u_string_list * -u_string_list_create(void); - -/*! - * @brief Create a string list with room for at least the given number of strings. - * - * @public @memberof u_string_list - */ -struct u_string_list * -u_string_list_create_with_capacity(uint32_t capacity); - -/*! - * @brief Create a new string list from an existing string list. - * - * @public @memberof u_string_list - */ -struct u_string_list * -u_string_list_create_from_list(struct u_string_list *usl); - -/*! - * @brief Create a new string list from an array of suitable strings. - * - * @param arr an array of zero or more non-null, null-terminated string that must live at least as long as the list, - * preferably string literals. - * @param size the number of elements in the array. - * - * @public @memberof u_string_list - */ -struct u_string_list * -u_string_list_create_from_array(const char *const *arr, uint32_t size); - -/*! - * @brief Retrieve the number of elements in the list - * - * @public @memberof u_string_list - */ -uint32_t -u_string_list_get_size(const struct u_string_list *usl); - -/*! - * @brief Retrieve the data pointer of the list - * - * @public @memberof u_string_list - */ -const char *const * -u_string_list_get_data(const struct u_string_list *usl); - -/*! - * @brief Append a new string literal to the list. - * - * @param usl self pointer - * @param str a non-null, null-terminated string that must live at least as long as the list, preferably a string - * literal. - * @return 1 if successfully added, negative for errors. - * - * @public @memberof u_string_list - */ -int -u_string_list_append(struct u_string_list *usl, const char *str); - - -/*! - * @brief Append an array of new string literals to the list. - * - * @param usl self pointer - * @param arr an array of zero or more non-null, null-terminated string that must live at least as long as the list, - * preferably string literals. - * @param size the number of elements in the array. - * @return 1 if successfully added, negative for errors. - * - * @public @memberof u_string_list - */ -int -u_string_list_append_array(struct u_string_list *usl, const char *const *arr, uint32_t size); - -/*! - * @brief Append a new string literal to the list, if it's not the same as a string already in the list. - * - * (Comparing string contents, not pointers) - * - * @param usl self pointer - * @param str a non-null, null-terminated string that must live at least as long as the list, preferably a string - * literal. - * @return 1 if successfully added, 0 if already existing so not added, negative for errors. - * - * @public @memberof u_string_list - */ -int -u_string_list_append_unique(struct u_string_list *usl, const char *str); - -/*! - * @brief Check if the string is in the list. - * - * (Comparing string contents, not pointers) - * - * @param usl self pointer - * @param str a non-null, null-terminated string. - * - * @return true if the string is in the list. - */ -bool -u_string_list_contains(struct u_string_list *usl, const char *str); - -/*! - * @brief Sort the list of strings suitable for extension lists. - * - * The list will be sorted first by API (VK, XR, etc.), then all KHR extensions, - * then all EXT extensions, then all Vendor extensions, then all experimental - * extensions. (Alphabetical within each group.) - * - * @param usl self pointer - */ -void -u_string_list_sort_extensions(struct u_string_list *usl); - -/*! - * @brief Destroy a string list. - * - * Performs null checks and sets your pointer to zero. - * - * @public @memberof u_string_list - */ -void -u_string_list_destroy(struct u_string_list **list_ptr); - -#ifdef __cplusplus -} // extern "C" -#endif diff --git a/src/xrt/auxiliary/util/u_string_list.hpp b/src/xrt/auxiliary/util/u_string_list.hpp deleted file mode 100644 index 8132a6f52..000000000 --- a/src/xrt/auxiliary/util/u_string_list.hpp +++ /dev/null @@ -1,169 +0,0 @@ -// Copyright 2021, Collabora, Ltd. -// SPDX-License-Identifier: BSL-1.0 -/*! - * @file - * @brief A collection of strings, like a list of extensions to enable - * - * @author Rylie Pavlik - * @ingroup aux_util - * - */ - -#pragma once - -#include "u_string_list.h" - -#include -#include -#include -#include -#include -#include - -namespace xrt::auxiliary::util { - -/*! - * @brief A collection of strings (const char *), like a list of extensions to enable. - * - * This version is only for use with strings that will outlive this object, preferably string literals. - * - * Size is limited to one less than the max value of uint32_t which shouldn't be a problem, - * the size really should be much smaller (especially if you use push_back_unique()). - */ -class StringList -{ -public: - /// Construct a string list. - StringList() = default; - /// Construct a string list with the given capacity. - StringList(uint32_t capacity) : vec(capacity, nullptr) - { - // best way I know to create with capacity - vec.clear(); - } - StringList(StringList &&) = default; - StringList(StringList const &) = default; - - StringList & - operator=(StringList &&) = default; - StringList & - operator=(StringList const &) = default; - - /// Construct a string list with the given items - template StringList(const char *(&arr)[N]) : StringList(N) - { - for (auto &&elt : arr) { - push_back(elt); - } - } - - /*! - * @brief Get the size of the array (the number of strings) - */ - uint32_t - size() const noexcept - { - return static_cast(vec.size()); - } - - /*! - * @brief Get the data pointer of the array - */ - const char *const * - data() const noexcept - { - return vec.data(); - } - - /*! - * @brief Append a new string to the list. - * - * @param str a non-null, null-terminated string that must live at least as long as the list, - * preferably a string literal. - * - * @throws std::out_of_range if you have a ridiculous number of strings in your list already, - * std::invalid_argument if you pass a null pointer. - */ - void - push_back(const char *str) - { - - if (vec.size() > (std::numeric_limits::max)() - 1) { - throw std::out_of_range("Size limit reached"); - } - if (str == nullptr) { - throw std::invalid_argument("Cannot pass a null pointer"); - } - vec.push_back(str); - } - - /// Add all given items - /// @throws the same as what push_back() throws - template - void - push_back_all(const char *(&arr)[N]) - { - for (auto &&elt : arr) { - push_back(elt); - } - } - - /*! - * @brief Check if the string is in the list. - * - * (Comparing string contents, not pointers) - * - * @param str a non-null, null-terminated string. - * - * @return true if the string is in the list. - */ - bool - contains(const char *str) - { - if (str == nullptr) { - throw std::invalid_argument("Cannot pass a null pointer"); - } - std::string needle{str}; - auto it = std::find_if(vec.begin(), vec.end(), [needle](const char *elt) { return needle == elt; }); - return it != vec.end(); - } - - /*! - * @brief Append a new string to the list if it doesn't match any existing string. - * - * (Comparing string contents, not pointers) - * - * This does a simple linear search, because it is assumed that the size of this list is fairly small. - * - * @param str a non-null, null-terminated string that must live at least as long as the list, - * preferably a string literal. - * - * @return true if we added it - * - * @throws std::out_of_range if you have a ridiculous number of strings in your list already, - * std::invalid_argument if you pass a null pointer. - */ - bool - push_back_unique(const char *str) - { - if (vec.size() > (std::numeric_limits::max)() - 1) { - throw std::out_of_range("Size limit reached"); - } - if (str == nullptr) { - throw std::invalid_argument("Cannot pass a null pointer"); - } - std::string needle{str}; - auto it = std::find_if(vec.begin(), vec.end(), [needle](const char *elt) { return needle == elt; }); - if (it != vec.end()) { - // already have it - return false; - } - vec.push_back(str); - return true; - } - -private: - std::vector vec; -}; - -} // namespace xrt::auxiliary::util diff --git a/src/xrt/auxiliary/vk/vk_bundle_init.c b/src/xrt/auxiliary/vk/vk_bundle_init.c index 27506a4f8..fca737ecb 100644 --- a/src/xrt/auxiliary/vk/vk_bundle_init.c +++ b/src/xrt/auxiliary/vk/vk_bundle_init.c @@ -31,14 +31,14 @@ static bool should_skip_optional_instance_ext(struct vk_bundle *vk, - struct u_string_list *required_instance_ext_list, - struct u_string_list *optional_instance_ext_list, + struct u_extension_list *required_instance_ext_list, + struct u_extension_list *optional_instance_ext_list, const char *ext) { #ifdef VK_EXT_display_surface_counter if (strcmp(ext, VK_EXT_DISPLAY_SURFACE_COUNTER_EXTENSION_NAME) == 0) { // it does not make sense to enable surface counter on anything that does not use a VkDisplayKHR - if (!u_string_list_contains(required_instance_ext_list, VK_KHR_DISPLAY_EXTENSION_NAME)) { + if (!u_extension_list_contains(required_instance_ext_list, VK_KHR_DISPLAY_EXTENSION_NAME)) { VK_DEBUG(vk, "Skipping optional instance extension %s because %s is not enabled", ext, VK_KHR_DISPLAY_EXTENSION_NAME); return true; @@ -52,8 +52,8 @@ should_skip_optional_instance_ext(struct vk_bundle *vk, static bool should_skip_optional_device_ext(struct vk_bundle *vk, - struct u_string_list *required_device_ext_list, - struct u_string_list *optional_device_ext_list, + struct u_extension_list *required_device_ext_list, + struct u_extension_list *optional_device_ext_list, const char *ext) { #ifdef VK_EXT_display_control @@ -80,9 +80,9 @@ should_skip_optional_device_ext(struct vk_bundle *vk, VkResult vk_build_instance_extensions(struct vk_bundle *vk, - struct u_string_list *required_instance_ext_list, - struct u_string_list *optional_instance_ext_list, - struct u_string_list **out_instance_ext_list) + struct u_extension_list *required_instance_ext_list, + struct u_extension_list *optional_instance_ext_list, + struct u_extension_list **out_instance_ext_list) { return vk_build_instance_extensions_with_skip( // vk, // @@ -93,7 +93,7 @@ vk_build_instance_extensions(struct vk_bundle *vk, } VkResult -vk_check_required_instance_extensions(struct vk_bundle *vk, struct u_string_list *required_instance_ext_list) +vk_check_required_instance_extensions(struct vk_bundle *vk, struct u_extension_list *required_instance_ext_list) { VkExtensionProperties *props = NULL; uint32_t prop_count = 0; @@ -109,8 +109,8 @@ vk_check_required_instance_extensions(struct vk_bundle *vk, struct u_string_list return ret; // Already logged. } - // Convert to string list for easier checking. - struct u_string_list *available_ext_list = vk_convert_extension_properties_to_string_list(props, prop_count); + // Convert to extension list for easier checking. + struct u_extension_list *available_ext_list = vk_convert_extension_properties_to_string_list(props, prop_count); // Clean up props. free(props); @@ -119,13 +119,13 @@ vk_check_required_instance_extensions(struct vk_bundle *vk, struct u_string_list ret = vk_check_required_extensions(vk, available_ext_list, required_instance_ext_list, "instance"); // Clean up. - u_string_list_destroy(&available_ext_list); + u_extension_list_destroy(&available_ext_list); return ret; } void -vk_fill_in_has_instance_extensions(struct vk_bundle *vk, struct u_string_list *ext_list) +vk_fill_in_has_instance_extensions(struct vk_bundle *vk, struct u_extension_list *ext_list) { // Include the generated extension detection code. #include "vk_bundle_init_instance_ext.c.inc" @@ -651,7 +651,7 @@ err_free: } static void -fill_in_has_device_extensions(struct vk_bundle *vk, struct u_string_list *ext_list) +fill_in_has_device_extensions(struct vk_bundle *vk, struct u_extension_list *ext_list) { #include "vk_bundle_init_device_ext.c.inc" } @@ -931,11 +931,11 @@ vk_create_device(struct vk_bundle *vk, int forced_index, bool only_compute, VkQueueGlobalPriorityEXT global_priority, - struct u_string_list *required_device_ext_list, - struct u_string_list *optional_device_ext_list, + struct u_extension_list *required_device_ext_list, + struct u_extension_list *optional_device_ext_list, const struct vk_device_features *optional_device_features) { - struct u_string_list *device_ext_list = NULL; + struct u_extension_list *device_ext_list = NULL; VkResult ret; ret = select_physical_device(vk, forced_index); @@ -1021,7 +1021,7 @@ vk_create_device(struct vk_bundle *vk, #ifdef VK_KHR_video_encode_queue // Video encode queue struct vk_queue_pair encode_queue = VK_NULL_QUEUE_PAIR; - if (u_string_list_contains(device_ext_list, VK_KHR_VIDEO_ENCODE_QUEUE_EXTENSION_NAME)) { + if (u_extension_list_contains(device_ext_list, VK_KHR_VIDEO_ENCODE_QUEUE_EXTENSION_NAME)) { struct vk_queue_family encode_queue_family = {0}; ret = find_queue_family(vk, VK_QUEUE_VIDEO_ENCODE_BIT_KHR, &encode_queue_family); if (ret == VK_SUCCESS) { @@ -1131,8 +1131,8 @@ vk_create_device(struct vk_bundle *vk, .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, .queueCreateInfoCount = queue_create_info_count, .pQueueCreateInfos = queue_create_info, - .enabledExtensionCount = u_string_list_get_size(device_ext_list), - .ppEnabledExtensionNames = u_string_list_get_data(device_ext_list), + .enabledExtensionCount = u_extension_list_get_size(device_ext_list), + .ppEnabledExtensionNames = u_extension_list_get_data(device_ext_list), .pEnabledFeatures = &enabled_features, }; @@ -1191,7 +1191,7 @@ vk_create_device(struct vk_bundle *vk, ret = vk->vkCreateDevice(vk->physical_device, &device_create_info, NULL, &vk->device); // Destroy the list now. - u_string_list_destroy(&device_ext_list); + u_extension_list_destroy(&device_ext_list); if (ret == VK_ERROR_NOT_PERMITTED_EXT) { // Don't spam the not permitted returns as we try all of the different priorities. @@ -1230,7 +1230,7 @@ err_destroy: } // Safe to call even if null. - u_string_list_destroy(&device_ext_list); + u_extension_list_destroy(&device_ext_list); return ret; } diff --git a/src/xrt/auxiliary/vk/vk_extensions_helpers.c b/src/xrt/auxiliary/vk/vk_extensions_helpers.c index 33b11a80a..c1b8469cd 100644 --- a/src/xrt/auxiliary/vk/vk_extensions_helpers.c +++ b/src/xrt/auxiliary/vk/vk_extensions_helpers.c @@ -19,13 +19,13 @@ * */ -static struct u_string_list * +static struct u_extension_list * build_extension_list(struct vk_bundle *vk, - struct u_string_list *available_ext_list, - struct u_string_list *required_ext_list, - struct u_string_list *optional_ext_list, + struct u_extension_list *available_ext_list, + struct u_extension_list *required_ext_list, + struct u_extension_list *optional_ext_list, vk_should_skip_ext_func_t skip_func, - struct u_string_list **out_skipped_ext_list) + struct u_extension_list **out_skipped_ext_list) { assert(available_ext_list != NULL); assert(required_ext_list != NULL); @@ -34,14 +34,18 @@ build_extension_list(struct vk_bundle *vk, assert(out_skipped_ext_list != NULL); // Start with required extensions (assumed to be supported). - struct u_string_list *list = u_string_list_create_from_list(required_ext_list); + uint32_t required_ext_count = u_extension_list_get_size(required_ext_list); + struct u_extension_list_builder *builder = + u_extension_list_builder_create_with_capacity(required_ext_count + 16); + u_extension_list_builder_append_array(builder, u_extension_list_get_data(required_ext_list), + required_ext_count); - // Create skipped list. - struct u_string_list *skipped_list = u_string_list_create(); + // Create skipped list builder. + struct u_extension_list_builder *skipped_builder = u_extension_list_builder_create(); // Check any supported optional extensions. - uint32_t optional_ext_count = u_string_list_get_size(optional_ext_list); - const char *const *optional_exts = u_string_list_get_data(optional_ext_list); + uint32_t optional_ext_count = u_extension_list_get_size(optional_ext_list); + const char *const *optional_exts = u_extension_list_get_data(optional_ext_list); for (uint32_t i = 0; i < optional_ext_count; i++) { const char *optional_ext = optional_exts[i]; @@ -50,25 +54,26 @@ build_extension_list(struct vk_bundle *vk, bool skip = skip_func(vk, required_ext_list, optional_ext_list, optional_ext); if (skip) { // Track skipped extensions. - u_string_list_append_unique(skipped_list, optional_ext); + u_extension_list_builder_append_unique(skipped_builder, optional_ext); continue; } // Check if the extension is available. - if (!u_string_list_contains(available_ext_list, optional_ext)) { + if (!u_extension_list_contains(available_ext_list, optional_ext)) { continue; } - int added = u_string_list_append_unique(list, optional_ext); + int added = u_extension_list_builder_append_unique(builder, optional_ext); if (added != 1) { VK_WARN(vk, "Duplicate extension %s not added twice", optional_ext); } } - // Return skipped list. - *out_skipped_ext_list = skipped_list; + // Build and return skipped list (sorted for consistent output). + *out_skipped_ext_list = u_extension_list_builder_build_sorted_for_extensions(&skipped_builder); - return list; + // Build and return the extension list (sorted for consistent output). + return u_extension_list_builder_build_sorted_for_extensions(&builder); } @@ -78,28 +83,27 @@ build_extension_list(struct vk_bundle *vk, * */ -struct u_string_list * +struct u_extension_list * vk_convert_extension_properties_to_string_list(VkExtensionProperties *props, uint32_t prop_count) { - struct u_string_list *list = u_string_list_create(); + struct u_extension_list_builder *builder = u_extension_list_builder_create_with_capacity(prop_count); for (uint32_t i = 0; i < prop_count; i++) { - u_string_list_append_unique(list, props[i].extensionName); + u_extension_list_builder_append_unique(builder, props[i].extensionName); } - return list; + return u_extension_list_builder_build_sorted_for_extensions(&builder); } void vk_log_extension_list(struct vk_bundle *vk, - struct u_string_list *ext_list, - struct u_string_list *optional_ext_list, - struct u_string_list *skipped_ext_list, + struct u_extension_list *ext_list, + struct u_extension_list *optional_ext_list, + struct u_extension_list *skipped_ext_list, const char *ext_type_name, enum u_logging_level log_level) { struct u_pp_sink_stack_only sink; u_pp_delegate_t dg = u_pp_sink_stack_only_init(&sink); - u_string_list_sort_extensions(ext_list); u_pp_string_list_extensions(dg, ext_list, optional_ext_list, skipped_ext_list); U_LOG_IFL(log_level, vk->log_level, "Vulkan %s extensions list(s):%s", ext_type_name, sink.buffer); @@ -107,8 +111,8 @@ vk_log_extension_list(struct vk_bundle *vk, VkResult vk_check_required_extensions(struct vk_bundle *vk, - struct u_string_list *available_ext_list, - struct u_string_list *required_ext_list, + struct u_extension_list *available_ext_list, + struct u_extension_list *required_ext_list, const char *ext_type_name) { struct u_pp_sink_stack_only sink; @@ -118,12 +122,12 @@ vk_check_required_extensions(struct vk_bundle *vk, u_pp_delegate_t dg = u_pp_sink_stack_only_init(&sink); // Check if required extensions are supported. - uint32_t required_ext_count = u_string_list_get_size(required_ext_list); - const char *const *required_exts = u_string_list_get_data(required_ext_list); + uint32_t required_ext_count = u_extension_list_get_size(required_ext_list); + const char *const *required_exts = u_extension_list_get_data(required_ext_list); for (uint32_t i = 0; i < required_ext_count; i++) { const char *required_ext = required_exts[i]; - if (u_string_list_contains(available_ext_list, required_ext)) { + if (u_extension_list_contains(available_ext_list, required_ext)) { continue; } @@ -148,10 +152,10 @@ vk_check_required_extensions(struct vk_bundle *vk, VkResult vk_build_instance_extensions_with_skip(struct vk_bundle *vk, - struct u_string_list *required_instance_ext_list, - struct u_string_list *optional_instance_ext_list, + struct u_extension_list *required_instance_ext_list, + struct u_extension_list *optional_instance_ext_list, vk_should_skip_ext_func_t skip_func, - struct u_string_list **out_instance_ext_list) + struct u_extension_list **out_instance_ext_list) { VkExtensionProperties *props = NULL; uint32_t prop_count = 0; @@ -165,8 +169,8 @@ vk_build_instance_extensions_with_skip(struct vk_bundle *vk, &props); // out_props VK_CHK_AND_RET(ret, "vk_enumerate_instance_extensions_properties"); - // Convert to string list for easier checking. - struct u_string_list *available_ext_list = vk_convert_extension_properties_to_string_list(props, prop_count); + // Convert to extension list for easier checking. + struct u_extension_list *available_ext_list = vk_convert_extension_properties_to_string_list(props, prop_count); // Clean up props. free(props); @@ -174,28 +178,28 @@ vk_build_instance_extensions_with_skip(struct vk_bundle *vk, // Check if all required extensions are available. ret = vk_check_required_extensions(vk, available_ext_list, required_instance_ext_list, "instance"); if (ret != VK_SUCCESS) { - u_string_list_destroy(&available_ext_list); + u_extension_list_destroy(&available_ext_list); return ret; } // Build the extension list and track skipped extensions. - struct u_string_list *skipped_list = NULL; - struct u_string_list *list = build_extension_list( // - vk, // - available_ext_list, // - required_instance_ext_list, // - optional_instance_ext_list, // - skip_func, // - &skipped_list); // + struct u_extension_list *skipped_list = NULL; + struct u_extension_list *list = build_extension_list( // + vk, // + available_ext_list, // + required_instance_ext_list, // + optional_instance_ext_list, // + skip_func, // + &skipped_list); // // Clean up available list. - u_string_list_destroy(&available_ext_list); + u_extension_list_destroy(&available_ext_list); // Log the result. vk_log_extension_list(vk, list, optional_instance_ext_list, skipped_list, "instance", U_LOGGING_INFO); // Clean up skipped list. - u_string_list_destroy(&skipped_list); + u_extension_list_destroy(&skipped_list); // Return the list. *out_instance_ext_list = list; @@ -213,10 +217,10 @@ vk_build_instance_extensions_with_skip(struct vk_bundle *vk, VkResult vk_build_device_extensions_with_skip(struct vk_bundle *vk, VkPhysicalDevice physical_device, - struct u_string_list *required_device_ext_list, - struct u_string_list *optional_device_ext_list, + struct u_extension_list *required_device_ext_list, + struct u_extension_list *optional_device_ext_list, vk_should_skip_ext_func_t skip_func, - struct u_string_list **out_device_ext_list) + struct u_extension_list **out_device_ext_list) { VkExtensionProperties *props = NULL; uint32_t prop_count = 0; @@ -230,8 +234,8 @@ vk_build_device_extensions_with_skip(struct vk_bundle *vk, &props); // out_props VK_CHK_AND_RET(ret, "vk_enumerate_physical_device_extension_properties"); - // Convert to string list for easier checking. - struct u_string_list *available_ext_list = vk_convert_extension_properties_to_string_list(props, prop_count); + // Convert to extension list for easier checking. + struct u_extension_list *available_ext_list = vk_convert_extension_properties_to_string_list(props, prop_count); // Clean up props. free(props); @@ -239,28 +243,28 @@ vk_build_device_extensions_with_skip(struct vk_bundle *vk, // Check if all required extensions are available. ret = vk_check_required_extensions(vk, available_ext_list, required_device_ext_list, "device"); if (ret != VK_SUCCESS) { - u_string_list_destroy(&available_ext_list); + u_extension_list_destroy(&available_ext_list); return ret; } // Build the extension list and track skipped extensions. - struct u_string_list *skipped_list = NULL; - struct u_string_list *list = build_extension_list( // - vk, // - available_ext_list, // - required_device_ext_list, // - optional_device_ext_list, // - skip_func, // - &skipped_list); // + struct u_extension_list *skipped_list = NULL; + struct u_extension_list *list = build_extension_list( // + vk, // + available_ext_list, // + required_device_ext_list, // + optional_device_ext_list, // + skip_func, // + &skipped_list); // // Clean up available list. - u_string_list_destroy(&available_ext_list); + u_extension_list_destroy(&available_ext_list); // Log the result. vk_log_extension_list(vk, list, optional_device_ext_list, skipped_list, "device", U_LOGGING_DEBUG); // Clean up skipped list. - u_string_list_destroy(&skipped_list); + u_extension_list_destroy(&skipped_list); // Return the list. *out_device_ext_list = list; diff --git a/src/xrt/auxiliary/vk/vk_extensions_helpers.h b/src/xrt/auxiliary/vk/vk_extensions_helpers.h index 5f626898d..a9a9c4427 100644 --- a/src/xrt/auxiliary/vk/vk_extensions_helpers.h +++ b/src/xrt/auxiliary/vk/vk_extensions_helpers.h @@ -22,15 +22,15 @@ extern "C" { * Callback type for checking if an optional extension should be skipped. */ typedef bool (*vk_should_skip_ext_func_t)(struct vk_bundle *vk, - struct u_string_list *required_ext_list, - struct u_string_list *optional_ext_list, + struct u_extension_list *required_ext_list, + struct u_extension_list *optional_ext_list, const char *ext); /*! - * Convert VkExtensionProperties array to u_string_list. + * Convert VkExtensionProperties array to u_extension_list. */ -struct u_string_list * +struct u_extension_list * vk_convert_extension_properties_to_string_list(VkExtensionProperties *props, uint32_t prop_count); /*! @@ -41,9 +41,9 @@ vk_convert_extension_properties_to_string_list(VkExtensionProperties *props, uin */ void vk_log_extension_list(struct vk_bundle *vk, - struct u_string_list *ext_list, - struct u_string_list *optional_ext_list, - struct u_string_list *skipped_ext_list, + struct u_extension_list *ext_list, + struct u_extension_list *optional_ext_list, + struct u_extension_list *skipped_ext_list, const char *ext_type_name, enum u_logging_level log_level); @@ -55,8 +55,8 @@ vk_log_extension_list(struct vk_bundle *vk, */ VkResult vk_check_required_extensions(struct vk_bundle *vk, - struct u_string_list *available_ext_list, - struct u_string_list *required_ext_list, + struct u_extension_list *available_ext_list, + struct u_extension_list *required_ext_list, const char *ext_type_name); /*! @@ -74,10 +74,10 @@ vk_check_required_extensions(struct vk_bundle *vk, */ VkResult vk_build_instance_extensions_with_skip(struct vk_bundle *vk, - struct u_string_list *required_instance_ext_list, - struct u_string_list *optional_instance_ext_list, + struct u_extension_list *required_instance_ext_list, + struct u_extension_list *optional_instance_ext_list, vk_should_skip_ext_func_t skip_func, - struct u_string_list **out_instance_ext_list); + struct u_extension_list **out_instance_ext_list); /*! * Build device extensions from required and optional device extensions with @@ -92,10 +92,10 @@ vk_build_instance_extensions_with_skip(struct vk_bundle *vk, VkResult vk_build_device_extensions_with_skip(struct vk_bundle *vk, VkPhysicalDevice physical_device, - struct u_string_list *required_device_ext_list, - struct u_string_list *optional_device_ext_list, + struct u_extension_list *required_device_ext_list, + struct u_extension_list *optional_device_ext_list, vk_should_skip_ext_func_t skip_func, - struct u_string_list **out_device_ext_list); + struct u_extension_list **out_device_ext_list); #ifdef __cplusplus diff --git a/src/xrt/auxiliary/vk/vk_generate_inc_files.py b/src/xrt/auxiliary/vk/vk_generate_inc_files.py index bc7065cb9..f3e8edfb3 100644 --- a/src/xrt/auxiliary/vk/vk_generate_inc_files.py +++ b/src/xrt/auxiliary/vk/vk_generate_inc_files.py @@ -465,7 +465,7 @@ def generate_ext_check(exts): condition_line = conditional.process_condition(compute_condition((ext,))) if condition_line: yield condition_line - yield "\tvk->{} = u_string_list_contains(ext_list, {});".format( + yield "\tvk->{} = u_extension_list_contains(ext_list, {});".format( make_ext_member_name(ext), make_ext_name_define(ext)) # close any trailing conditions condition_line = conditional.finish() diff --git a/src/xrt/auxiliary/vk/vk_helpers.h b/src/xrt/auxiliary/vk/vk_helpers.h index c35647853..b51d024ed 100644 --- a/src/xrt/auxiliary/vk/vk_helpers.h +++ b/src/xrt/auxiliary/vk/vk_helpers.h @@ -23,7 +23,7 @@ #include "xrt/xrt_vulkan_includes.h" #include "xrt/xrt_handles.h" #include "util/u_logging.h" -#include "util/u_string_list.h" +#include "util/u_extension_list.h" #include "os/os_threading.h" #ifdef __cplusplus @@ -781,7 +781,7 @@ vk_get_device_functions(struct vk_bundle *vk); * @ingroup aux_vk */ VkResult -vk_check_required_instance_extensions(struct vk_bundle *vk, struct u_string_list *required_instance_ext_list); +vk_check_required_instance_extensions(struct vk_bundle *vk, struct u_extension_list *required_instance_ext_list); /*! * Build instance extensions from required and optional instance extensions. @@ -793,15 +793,15 @@ vk_check_required_instance_extensions(struct vk_bundle *vk, struct u_string_list */ VkResult vk_build_instance_extensions(struct vk_bundle *vk, - struct u_string_list *required_instance_ext_list, - struct u_string_list *optional_instance_ext_list, - struct u_string_list **out_instance_ext_list); + struct u_extension_list *required_instance_ext_list, + struct u_extension_list *optional_instance_ext_list, + struct u_extension_list **out_instance_ext_list); /*! * Fills in has_* in vk_bundle given a string of prefiltered instance extensions */ void -vk_fill_in_has_instance_extensions(struct vk_bundle *vk, struct u_string_list *ext_list); +vk_fill_in_has_instance_extensions(struct vk_bundle *vk, struct u_extension_list *ext_list); /*! * Setup the physical device, this is called by vk_create_device but has uses @@ -843,8 +843,8 @@ vk_create_device(struct vk_bundle *vk, int forced_index, bool only_compute, VkQueueGlobalPriorityEXT global_priority, - struct u_string_list *required_device_ext_list, - struct u_string_list *optional_device_ext_list, + struct u_extension_list *required_device_ext_list, + struct u_extension_list *optional_device_ext_list, const struct vk_device_features *optional_device_features); /*! diff --git a/src/xrt/auxiliary/vk/vk_image_readback_to_xf_pool.h b/src/xrt/auxiliary/vk/vk_image_readback_to_xf_pool.h index 9dbc76933..9c1dcddb9 100644 --- a/src/xrt/auxiliary/vk/vk_image_readback_to_xf_pool.h +++ b/src/xrt/auxiliary/vk/vk_image_readback_to_xf_pool.h @@ -1,4 +1,5 @@ // Copyright 2022, Collabora, Ltd. +// Copyright 2025-2026, NVIDIA CORPORATION. // SPDX-License-Identifier: BSL-1.0 /*! * @file @@ -19,7 +20,7 @@ #include "xrt/xrt_vulkan_includes.h" #include "util/u_logging.h" -#include "util/u_string_list.h" +#include "util/u_extension_list.h" #include "vk/vk_helpers.h" diff --git a/src/xrt/compositor/main/comp_compositor.c b/src/xrt/compositor/main/comp_compositor.c index b1e058a32..b6c4947bf 100644 --- a/src/xrt/compositor/main/comp_compositor.c +++ b/src/xrt/compositor/main/comp_compositor.c @@ -1,5 +1,5 @@ // Copyright 2019-2024, Collabora, Ltd. -// Copyright 2025, NVIDIA CORPORATION. +// Copyright 2025-2026, NVIDIA CORPORATION. // SPDX-License-Identifier: BSL-1.0 /*! * @file @@ -613,10 +613,12 @@ static const char *optional_device_extensions[] = { }; static bool -select_instances_extensions(struct comp_compositor *c, struct u_string_list *required, struct u_string_list *optional) +select_instances_extensions(struct comp_compositor *c, + struct u_extension_list_builder *required_builder, + struct u_extension_list_builder *optional_builder) { #ifdef XRT_FEATURE_WINDOW_PEEK - if (!comp_window_peek_get_vk_instance_exts(required)) { + if (!comp_window_peek_get_vk_instance_exts(required_builder)) { COMP_ERROR(c, "Failed to get required vulkan instance extensions for peek window."); return false; } @@ -638,62 +640,76 @@ compositor_init_vulkan(struct comp_compositor *c) * Instance extensions. */ - struct u_string_list *required_instance_ext_list = u_string_list_create(); - struct u_string_list *optional_instance_ext_list = u_string_list_create(); + struct u_extension_list_builder *required_instance_ext_builder = u_extension_list_builder_create(); + struct u_extension_list_builder *optional_instance_ext_builder = u_extension_list_builder_create(); // Every backend needs at least the common extensions. - u_string_list_append_array( // - required_instance_ext_list, // + u_extension_list_builder_append_array( // + required_instance_ext_builder, // instance_extensions_common, // ARRAY_SIZE(instance_extensions_common)); // // Add per target required extensions. - u_string_list_append_array( // - required_instance_ext_list, // + u_extension_list_builder_append_array( // + required_instance_ext_builder, // c->target_factory->required_instance_extensions, // c->target_factory->required_instance_extension_count); // // Optional instance extensions. - u_string_list_append_array( // - optional_instance_ext_list, // + u_extension_list_builder_append_array( // + optional_instance_ext_builder, // optional_instance_extensions, // ARRAY_SIZE(optional_instance_extensions)); // - if (!select_instances_extensions(c, required_instance_ext_list, optional_instance_ext_list)) { + if (!select_instances_extensions(c, required_instance_ext_builder, optional_instance_ext_builder)) { COMP_ERROR(c, "Failed to select additional instance extensions."); - u_string_list_destroy(&required_instance_ext_list); - u_string_list_destroy(&optional_instance_ext_list); + u_extension_list_builder_destroy(&required_instance_ext_builder); + u_extension_list_builder_destroy(&optional_instance_ext_builder); return false; } + // Consumes the builder and returns a new list. + struct u_extension_list *required_instance_ext_list = + u_extension_list_builder_build(&required_instance_ext_builder); + struct u_extension_list *optional_instance_ext_list = + u_extension_list_builder_build(&optional_instance_ext_builder); + + /* * Device extensions. */ - struct u_string_list *required_device_extension_list = u_string_list_create(); - struct u_string_list *optional_device_extension_list = u_string_list_create(); + struct u_extension_list_builder *required_device_ext_builder = u_extension_list_builder_create(); + struct u_extension_list_builder *optional_device_ext_builder = u_extension_list_builder_create(); // Required device extensions. - u_string_list_append_array( // - required_device_extension_list, // + u_extension_list_builder_append_array( // + required_device_ext_builder, // required_device_extensions, // ARRAY_SIZE(required_device_extensions)); // // Optional device extensions. - u_string_list_append_array( // - optional_device_extension_list, // + u_extension_list_builder_append_array( // + optional_device_ext_builder, // optional_device_extensions, // ARRAY_SIZE(optional_device_extensions)); // // Add per target optional device extensions. - u_string_list_append_array( // - optional_device_extension_list, // + u_extension_list_builder_append_array( // + optional_device_ext_builder, // c->target_factory->optional_device_extensions, // c->target_factory->optional_device_extension_count); // + // Consumes the builder and returns a new list. + struct u_extension_list *required_device_ext_list = + u_extension_list_builder_build(&required_device_ext_builder); + struct u_extension_list *optional_device_ext_list = + u_extension_list_builder_build(&optional_device_ext_builder); + // Select required Vulkan version, suitable for both compositor and target uint32_t required_instance_version = MAX(c->target_factory->required_instance_version, VK_API_VERSION_1_0); + /* * Create the device. */ @@ -703,8 +719,8 @@ compositor_init_vulkan(struct comp_compositor *c) .required_instance_version = required_instance_version, .required_instance_extensions = required_instance_ext_list, .optional_instance_extensions = optional_instance_ext_list, - .required_device_extensions = required_device_extension_list, - .optional_device_extensions = optional_device_extension_list, + .required_device_extensions = required_device_ext_list, + .optional_device_extensions = optional_device_ext_list, .log_level = c->settings.log_level, .only_compute_queue = c->settings.use_compute, .selected_gpu_index = c->settings.selected_gpu_index, @@ -715,10 +731,10 @@ compositor_init_vulkan(struct comp_compositor *c) struct comp_vulkan_results vk_res = {0}; bool bundle_ret = comp_vulkan_init_bundle(vk, &vk_args, &vk_res); - u_string_list_destroy(&required_instance_ext_list); - u_string_list_destroy(&optional_instance_ext_list); - u_string_list_destroy(&required_device_extension_list); - u_string_list_destroy(&optional_device_extension_list); + u_extension_list_destroy(&required_instance_ext_list); + u_extension_list_destroy(&optional_instance_ext_list); + u_extension_list_destroy(&required_device_ext_list); + u_extension_list_destroy(&optional_device_ext_list); if (!bundle_ret) { return false; diff --git a/src/xrt/compositor/main/comp_window_peek.c b/src/xrt/compositor/main/comp_window_peek.c index 3ecc816cc..6a9524580 100644 --- a/src/xrt/compositor/main/comp_window_peek.c +++ b/src/xrt/compositor/main/comp_window_peek.c @@ -1,5 +1,6 @@ // Copyright 2022, Simon Zeni // Copyright 2022-2023, Collabora, Ltd. +// Copyright 2025-2026, NVIDIA CORPORATION. // SPDX-License-Identifier: BSL-1.0 /*! * @file @@ -13,7 +14,7 @@ #include "main/comp_window_peek.h" #include "util/u_debug.h" -#include "util/u_string_list.h" +#include "util/u_extension_list.h" #ifdef XRT_HAVE_SDL2 #include @@ -473,10 +474,10 @@ comp_window_peek_get_eye(struct comp_window_peek *w) } bool -comp_window_peek_get_vk_instance_exts(struct u_string_list *out_required_list) +comp_window_peek_get_vk_instance_exts(struct u_extension_list_builder *out_required_builder) { - if (out_required_list == NULL) { - U_LOG_E("comp_window_peek: out_required_list is null."); + if (out_required_builder == NULL) { + U_LOG_E("comp_window_peek: out_required_builder is null."); return false; } @@ -532,7 +533,7 @@ comp_window_peek_get_vk_instance_exts(struct u_string_list *out_required_list) } for (uint32_t i = 0; i < size; ++i) { - if (u_string_list_append_unique(out_required_list, instance_ext_names[i]) == 0) { + if (u_extension_list_builder_append_unique(out_required_builder, instance_ext_names[i]) == 0) { U_LOG_T("comp_window_peek: required instance extension: %s already exits, ignored.", instance_ext_names[i]); } else { diff --git a/src/xrt/compositor/main/comp_window_peek.h b/src/xrt/compositor/main/comp_window_peek.h index 934cd3147..15ef43263 100644 --- a/src/xrt/compositor/main/comp_window_peek.h +++ b/src/xrt/compositor/main/comp_window_peek.h @@ -1,4 +1,5 @@ // Copyright 2022, Simon Zeni +// Copyright 2025-2026, NVIDIA CORPORATION. // SPDX-License-Identifier: BSL-1.0 /*! * @file @@ -56,12 +57,12 @@ comp_window_peek_get_eye(struct comp_window_peek *w); /*! * Gets the required vulkan instance extensions for comp_window_peek support * - * @param[inout] out_required_list list of string containing the required instance extensions to enable, - * the list may already contain some/all of the required, duplicates will - * not be added. + * @param[inout] out_required_builder builder for the list of required instance extensions to enable, + * the builder may already contain some/all of the required, duplicates will + * not be added. */ bool -comp_window_peek_get_vk_instance_exts(struct u_string_list *out_required_list); +comp_window_peek_get_vk_instance_exts(struct u_extension_list_builder *out_required_builder); #ifdef __cplusplus } diff --git a/src/xrt/compositor/null/null_compositor.c b/src/xrt/compositor/null/null_compositor.c index d1732ac43..60b8c6edc 100644 --- a/src/xrt/compositor/null/null_compositor.c +++ b/src/xrt/compositor/null/null_compositor.c @@ -1,4 +1,5 @@ // Copyright 2019-2024, Collabora, Ltd. +// Copyright 2025-2026, NVIDIA CORPORATION. // SPDX-License-Identifier: BSL-1.0 /*! * @file @@ -146,10 +147,12 @@ static const char *optional_device_extensions[] = { }; static VkResult -select_instances_extensions(struct null_compositor *c, struct u_string_list *required, struct u_string_list *optional) +select_instances_extensions(struct null_compositor *c, + struct u_extension_list_builder *required_builder, + struct u_extension_list_builder *optional_builder) { #ifdef VK_EXT_display_surface_counter - u_string_list_append(optional, VK_EXT_DISPLAY_SURFACE_COUNTER_EXTENSION_NAME); + u_extension_list_builder_append(optional_builder, VK_EXT_DISPLAY_SURFACE_COUNTER_EXTENSION_NAME); #endif return VK_SUCCESS; @@ -162,25 +165,32 @@ compositor_init_vulkan(struct null_compositor *c) VkResult ret; // every backend needs at least the common extensions - struct u_string_list *required_instance_ext_list = - u_string_list_create_from_array(instance_extensions_common, ARRAY_SIZE(instance_extensions_common)); + struct u_extension_list_builder *required_instance_ext_builder = u_extension_list_builder_create_from_array( // + instance_extensions_common, // + ARRAY_SIZE(instance_extensions_common)); // - struct u_string_list *optional_instance_ext_list = u_string_list_create(); + struct u_extension_list_builder *optional_instance_ext_builder = u_extension_list_builder_create(); - ret = select_instances_extensions(c, required_instance_ext_list, optional_instance_ext_list); + ret = select_instances_extensions(c, required_instance_ext_builder, optional_instance_ext_builder); if (ret != VK_SUCCESS) { VK_ERROR(vk, "select_instances_extensions: %s\n\tFailed to select instance extensions.", vk_result_string(ret)); - u_string_list_destroy(&required_instance_ext_list); - u_string_list_destroy(&optional_instance_ext_list); + u_extension_list_builder_destroy(&required_instance_ext_builder); + u_extension_list_builder_destroy(&optional_instance_ext_builder); return ret; } - struct u_string_list *required_device_extension_list = - u_string_list_create_from_array(required_device_extensions, ARRAY_SIZE(required_device_extensions)); + // Consumes the builder and returns a new list. + struct u_extension_list *required_instance_ext_list = + u_extension_list_builder_build(&required_instance_ext_builder); + struct u_extension_list *optional_instance_ext_list = + u_extension_list_builder_build(&optional_instance_ext_builder); - struct u_string_list *optional_device_extension_list = - u_string_list_create_from_array(optional_device_extensions, ARRAY_SIZE(optional_device_extensions)); + struct u_extension_list *required_device_extension_list = + u_extension_list_create_from_array(required_device_extensions, ARRAY_SIZE(required_device_extensions)); + + struct u_extension_list *optional_device_extension_list = + u_extension_list_create_from_array(optional_device_extensions, ARRAY_SIZE(optional_device_extensions)); struct comp_vulkan_arguments vk_args = { .get_instance_proc_address = vkGetInstanceProcAddr, @@ -199,10 +209,10 @@ compositor_init_vulkan(struct null_compositor *c) struct comp_vulkan_results vk_res = {0}; bool bundle_ret = comp_vulkan_init_bundle(vk, &vk_args, &vk_res); - u_string_list_destroy(&required_instance_ext_list); - u_string_list_destroy(&optional_instance_ext_list); - u_string_list_destroy(&required_device_extension_list); - u_string_list_destroy(&optional_device_extension_list); + u_extension_list_destroy(&required_instance_ext_list); + u_extension_list_destroy(&optional_instance_ext_list); + u_extension_list_destroy(&required_device_extension_list); + u_extension_list_destroy(&optional_device_extension_list); if (!bundle_ret) { return false; diff --git a/src/xrt/compositor/util/comp_vulkan.c b/src/xrt/compositor/util/comp_vulkan.c index c62052754..b62b33f20 100644 --- a/src/xrt/compositor/util/comp_vulkan.c +++ b/src/xrt/compositor/util/comp_vulkan.c @@ -178,7 +178,7 @@ fill_in_results(struct vk_bundle *vk, const struct comp_vulkan_arguments *vk_arg static VkResult create_instance(struct vk_bundle *vk, const struct comp_vulkan_arguments *vk_args) { - struct u_string_list *instance_ext_list = NULL; + struct u_extension_list *instance_ext_list = NULL; VkResult ret; assert(vk_args->required_instance_version != 0); @@ -217,8 +217,8 @@ create_instance(struct vk_bundle *vk, const struct comp_vulkan_arguments *vk_arg VkInstanceCreateInfo instance_info = { .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, .pApplicationInfo = &app_info, - .enabledExtensionCount = u_string_list_get_size(instance_ext_list), - .ppEnabledExtensionNames = u_string_list_get_data(instance_ext_list), + .enabledExtensionCount = u_extension_list_get_size(instance_ext_list), + .ppEnabledExtensionNames = u_extension_list_get_data(instance_ext_list), }; ret = vk->vkCreateInstance(&instance_info, NULL, &vk->instance); @@ -239,7 +239,7 @@ create_instance(struct vk_bundle *vk, const struct comp_vulkan_arguments *vk_arg // Needs to be filled in before getting functions. vk_fill_in_has_instance_extensions(vk, instance_ext_list); - u_string_list_destroy(&instance_ext_list); + u_extension_list_destroy(&instance_ext_list); ret = vk_get_instance_functions(vk); if (ret != VK_SUCCESS) { diff --git a/src/xrt/compositor/util/comp_vulkan.h b/src/xrt/compositor/util/comp_vulkan.h index deaafb45f..979abf2d4 100644 --- a/src/xrt/compositor/util/comp_vulkan.h +++ b/src/xrt/compositor/util/comp_vulkan.h @@ -1,4 +1,5 @@ // Copyright 2019-2021, Collabora, Ltd. +// Copyright 2025-2026, NVIDIA CORPORATION. // SPDX-License-Identifier: BSL-1.0 /*! * @file @@ -11,7 +12,7 @@ #include "xrt/xrt_compositor.h" #include "util/u_logging.h" -#include "util/u_string_list.h" +#include "util/u_extension_list.h" #include "vk/vk_helpers.h" @@ -32,16 +33,16 @@ struct comp_vulkan_arguments PFN_vkGetInstanceProcAddr get_instance_proc_address; //! Extensions that the instance is created with. - struct u_string_list *required_instance_extensions; + struct u_extension_list *required_instance_extensions; //! Extensions that the instance is created with. - struct u_string_list *optional_instance_extensions; + struct u_extension_list *optional_instance_extensions; //! Extensions that the device is created with. - struct u_string_list *required_device_extensions; + struct u_extension_list *required_device_extensions; //! Extensions that the device is created with. - struct u_string_list *optional_device_extensions; + struct u_extension_list *optional_device_extensions; //! Logging level to be set on the @ref vk_bundle. enum u_logging_level log_level; diff --git a/src/xrt/state_trackers/oxr/oxr_vulkan.c b/src/xrt/state_trackers/oxr/oxr_vulkan.c index 5efc7575c..71244264e 100644 --- a/src/xrt/state_trackers/oxr/oxr_vulkan.c +++ b/src/xrt/state_trackers/oxr/oxr_vulkan.c @@ -1,5 +1,5 @@ // Copyright 2018-2024, Collabora, Ltd. -// Copyright 2024-2025, NVIDIA CORPORATION. +// Copyright 2024-2026, NVIDIA CORPORATION. // SPDX-License-Identifier: BSL-1.0 /*! * @file @@ -15,7 +15,7 @@ #include "util/u_misc.h" #include "util/u_debug.h" -#include "util/u_string_list.h" +#include "util/u_extension_list.h" #include "vk/vk_helpers.h" @@ -249,7 +249,7 @@ oxr_vk_create_vulkan_instance(struct oxr_logger *log, return res; } - struct u_string_list *instance_ext_list = u_string_list_create_from_array( + struct u_extension_list_builder *instance_ext_builder = u_extension_list_builder_create_from_array( required_vk_instance_extensions, ARRAY_SIZE(required_vk_instance_extensions)); #if defined(VK_EXT_debug_utils) @@ -263,7 +263,7 @@ oxr_vk_create_vulkan_instance(struct oxr_logger *log, continue; } - u_string_list_append_unique(instance_ext_list, optional_vk_instance_extensions[i]); + u_extension_list_builder_append_unique(instance_ext_builder, optional_vk_instance_extensions[i]); #if defined(VK_EXT_debug_utils) if (strcmp(optional_vk_instance_extensions[i], VK_EXT_DEBUG_UTILS_EXTENSION_NAME) == 0) { @@ -273,13 +273,15 @@ oxr_vk_create_vulkan_instance(struct oxr_logger *log, } for (uint32_t i = 0; i < createInfo->vulkanCreateInfo->enabledExtensionCount; i++) { - u_string_list_append_unique(instance_ext_list, - createInfo->vulkanCreateInfo->ppEnabledExtensionNames[i]); + u_extension_list_builder_append_unique(instance_ext_builder, + createInfo->vulkanCreateInfo->ppEnabledExtensionNames[i]); } + struct u_extension_list *instance_ext_list = u_extension_list_builder_build(&instance_ext_builder); + VkInstanceCreateInfo modified_info = *createInfo->vulkanCreateInfo; - modified_info.ppEnabledExtensionNames = u_string_list_get_data(instance_ext_list); - modified_info.enabledExtensionCount = u_string_list_get_size(instance_ext_list); + modified_info.ppEnabledExtensionNames = u_extension_list_get_data(instance_ext_list); + modified_info.enabledExtensionCount = u_extension_list_get_size(instance_ext_list); *vulkanResult = loaded_CreateInstance(&modified_info, createInfo->vulkanAllocator, vulkanInstance); @@ -305,7 +307,7 @@ oxr_vk_create_vulkan_instance(struct oxr_logger *log, } #endif - u_string_list_destroy(&instance_ext_list); + u_extension_list_destroy(&instance_ext_list); return XR_SUCCESS; } @@ -400,12 +402,12 @@ oxr_vk_create_vulkan_device(struct oxr_logger *log, VkPhysicalDevice physical_device = createInfo->vulkanPhysicalDevice; - struct u_string_list *device_extension_list = - u_string_list_create_from_array(required_vk_device_extensions, ARRAY_SIZE(required_vk_device_extensions)); + struct u_extension_list_builder *device_extension_builder = u_extension_list_builder_create_from_array( + required_vk_device_extensions, ARRAY_SIZE(required_vk_device_extensions)); for (uint32_t i = 0; i < createInfo->vulkanCreateInfo->enabledExtensionCount; i++) { - u_string_list_append_unique(device_extension_list, - createInfo->vulkanCreateInfo->ppEnabledExtensionNames[i]); + u_extension_list_builder_append_unique(device_extension_builder, + createInfo->vulkanCreateInfo->ppEnabledExtensionNames[i]); } @@ -431,7 +433,7 @@ oxr_vk_create_vulkan_device(struct oxr_logger *log, continue; } - u_string_list_append_unique(device_extension_list, optional_device_extensions[i]); + u_extension_list_builder_append_unique(device_extension_builder, optional_device_extensions[i]); #if defined(XRT_GRAPHICS_SYNC_HANDLE_IS_FD) if (strcmp(optional_device_extensions[i], VK_KHR_EXTERNAL_FENCE_FD_EXTENSION_NAME) == 0) { @@ -449,6 +451,7 @@ oxr_vk_create_vulkan_device(struct oxr_logger *log, free(props); + struct u_extension_list *device_extension_list = u_extension_list_builder_build(&device_extension_builder); VkPhysicalDeviceFeatures2KHR physical_device_features = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR, @@ -462,7 +465,7 @@ oxr_vk_create_vulkan_device(struct oxr_logger *log, .timelineSemaphore = VK_FALSE, }; - if (u_string_list_contains(device_extension_list, VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME)) { + if (u_extension_list_contains(device_extension_list, VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME)) { physical_device_features.pNext = &timeline_semaphore_info; } #endif @@ -475,8 +478,8 @@ oxr_vk_create_vulkan_device(struct oxr_logger *log, VkDeviceCreateInfo modified_info = *createInfo->vulkanCreateInfo; - modified_info.ppEnabledExtensionNames = u_string_list_get_data(device_extension_list); - modified_info.enabledExtensionCount = u_string_list_get_size(device_extension_list); + modified_info.ppEnabledExtensionNames = u_extension_list_get_data(device_extension_list); + modified_info.enabledExtensionCount = u_extension_list_get_size(device_extension_list); #ifdef VK_KHR_timeline_semaphore VkPhysicalDeviceTimelineSemaphoreFeatures timeline_semaphore = { @@ -554,7 +557,7 @@ oxr_vk_create_vulkan_device(struct oxr_logger *log, } #endif - u_string_list_destroy(&device_extension_list); + u_extension_list_destroy(&device_extension_list); return XR_SUCCESS; } diff --git a/src/xrt/targets/sdl_test/sdl_compositor.c b/src/xrt/targets/sdl_test/sdl_compositor.c index 7de1139ba..52f4b8a7a 100644 --- a/src/xrt/targets/sdl_test/sdl_compositor.c +++ b/src/xrt/targets/sdl_test/sdl_compositor.c @@ -1,4 +1,5 @@ // Copyright 2019-2023, Collabora, Ltd. +// Copyright 2025-2026, NVIDIA CORPORATION. // SPDX-License-Identifier: BSL-1.0 /*! * @file @@ -133,10 +134,12 @@ static const char *optional_device_extensions[] = { }; static VkResult -select_instances_extensions(struct sdl_compositor *c, struct u_string_list *required, struct u_string_list *optional) +select_instances_extensions(struct sdl_compositor *c, + struct u_extension_list_builder *required_builder, + struct u_extension_list_builder *optional_builder) { #ifdef VK_EXT_display_surface_counter - u_string_list_append(optional, VK_EXT_DISPLAY_SURFACE_COUNTER_EXTENSION_NAME); + u_extension_list_builder_append(optional_builder, VK_EXT_DISPLAY_SURFACE_COUNTER_EXTENSION_NAME); #endif return VK_SUCCESS; @@ -149,25 +152,32 @@ compositor_init_vulkan(struct sdl_compositor *c, enum u_logging_level log_level) VkResult ret; // every backend needs at least the common extensions - struct u_string_list *required_instance_ext_list = - u_string_list_create_from_array(instance_extensions_common, ARRAY_SIZE(instance_extensions_common)); + struct u_extension_list_builder *required_instance_ext_builder = u_extension_list_builder_create_from_array( // + instance_extensions_common, // + ARRAY_SIZE(instance_extensions_common)); // - struct u_string_list *optional_instance_ext_list = u_string_list_create(); + struct u_extension_list_builder *optional_instance_ext_builder = u_extension_list_builder_create(); - ret = select_instances_extensions(c, required_instance_ext_list, optional_instance_ext_list); + ret = select_instances_extensions(c, required_instance_ext_builder, optional_instance_ext_builder); if (ret != VK_SUCCESS) { VK_ERROR(vk, "select_instances_extensions: %s\n\tFailed to select instance extensions.", vk_result_string(ret)); - u_string_list_destroy(&required_instance_ext_list); - u_string_list_destroy(&optional_instance_ext_list); + u_extension_list_builder_destroy(&required_instance_ext_builder); + u_extension_list_builder_destroy(&optional_instance_ext_builder); return ret; } - struct u_string_list *required_device_extension_list = - u_string_list_create_from_array(required_device_extensions, ARRAY_SIZE(required_device_extensions)); + // Consumes the builder and returns a new list. + struct u_extension_list *required_instance_ext_list = + u_extension_list_builder_build(&required_instance_ext_builder); + struct u_extension_list *optional_instance_ext_list = + u_extension_list_builder_build(&optional_instance_ext_builder); - struct u_string_list *optional_device_extension_list = - u_string_list_create_from_array(optional_device_extensions, ARRAY_SIZE(optional_device_extensions)); + struct u_extension_list *required_device_extension_list = + u_extension_list_create_from_array(required_device_extensions, ARRAY_SIZE(required_device_extensions)); + + struct u_extension_list *optional_device_extension_list = + u_extension_list_create_from_array(optional_device_extensions, ARRAY_SIZE(optional_device_extensions)); struct comp_vulkan_arguments vk_args = { .get_instance_proc_address = vkGetInstanceProcAddr, @@ -186,10 +196,10 @@ compositor_init_vulkan(struct sdl_compositor *c, enum u_logging_level log_level) struct comp_vulkan_results vk_res = {0}; bool bundle_ret = comp_vulkan_init_bundle(vk, &vk_args, &vk_res); - u_string_list_destroy(&required_instance_ext_list); - u_string_list_destroy(&optional_instance_ext_list); - u_string_list_destroy(&required_device_extension_list); - u_string_list_destroy(&optional_device_extension_list); + u_extension_list_destroy(&required_instance_ext_list); + u_extension_list_destroy(&optional_instance_ext_list); + u_extension_list_destroy(&required_device_extension_list); + u_extension_list_destroy(&optional_device_extension_list); if (!bundle_ret) { return false; diff --git a/tests/tests_comp_client_vulkan.cpp b/tests/tests_comp_client_vulkan.cpp index f89907bd3..0193fe284 100644 --- a/tests/tests_comp_client_vulkan.cpp +++ b/tests/tests_comp_client_vulkan.cpp @@ -1,4 +1,5 @@ // Copyright 2022-2024, Collabora, Ltd. +// Copyright 2025-2026, NVIDIA CORPORATION. // SPDX-License-Identifier: BSL-1.0 /*! * @file @@ -20,7 +21,7 @@ #include "catch_amalgamated.hpp" #include "util/comp_vulkan.h" #include "util/u_logging.h" -#include "util/u_string_list.h" +#include "util/u_extension_list.h" #include "vk/vk_helpers.h" #include "xrt/xrt_compositor.h" #include @@ -85,7 +86,7 @@ static const char *required_device_extensions[] = { using unique_string_list = - std::unique_ptr>; + std::unique_ptr>; static void xrt_comp_vk_destroy(struct xrt_compositor_vk **ptr_xcvk) @@ -108,14 +109,14 @@ TEST_CASE("client_compositor", "[.][needgpu]") // every backend needs at least the common extensions unique_string_list required_instance_ext_list{ - u_string_list_create_from_array(instance_extensions_common, ARRAY_SIZE(instance_extensions_common))}; + u_extension_list_create_from_array(instance_extensions_common, ARRAY_SIZE(instance_extensions_common))}; - unique_string_list optional_instance_ext_list{u_string_list_create()}; + unique_string_list optional_instance_ext_list{u_extension_list_create()}; unique_string_list required_device_extension_list{ - u_string_list_create_from_array(required_device_extensions, ARRAY_SIZE(required_device_extensions))}; + u_extension_list_create_from_array(required_device_extensions, ARRAY_SIZE(required_device_extensions))}; - unique_string_list optional_device_extension_list{u_string_list_create()}; + unique_string_list optional_device_extension_list{u_extension_list_create()}; comp_vulkan_arguments args{VK_MAKE_VERSION(1, 0, 0), vkGetInstanceProcAddr, diff --git a/tests/vktest_init_bundle.hpp b/tests/vktest_init_bundle.hpp index f770e91f5..eb0f26a53 100644 --- a/tests/vktest_init_bundle.hpp +++ b/tests/vktest_init_bundle.hpp @@ -1,4 +1,5 @@ // Copyright 2019-2022, Collabora, Ltd. +// Copyright 2025-2026, NVIDIA CORPORATION. // SPDX-License-Identifier: BSL-1.0 /*! * @file @@ -18,7 +19,7 @@ #include #include #include -#include +#include static const char *instance_extensions_common[] = { @@ -71,7 +72,7 @@ static const char *optional_device_extensions[] = { #endif }; using unique_string_list = - std::unique_ptr>; + std::unique_ptr>; struct VkBundleDestroyer { @@ -119,15 +120,15 @@ vktest_init_bundle(struct vk_bundle *vk) { // every backend needs at least the common extensions unique_string_list required_instance_ext_list{ - u_string_list_create_from_array(instance_extensions_common, ARRAY_SIZE(instance_extensions_common))}; + u_extension_list_create_from_array(instance_extensions_common, ARRAY_SIZE(instance_extensions_common))}; - unique_string_list optional_instance_ext_list{u_string_list_create()}; + unique_string_list optional_instance_ext_list{u_extension_list_create()}; unique_string_list required_device_extension_list{ - u_string_list_create_from_array(required_device_extensions, ARRAY_SIZE(required_device_extensions))}; + u_extension_list_create_from_array(required_device_extensions, ARRAY_SIZE(required_device_extensions))}; unique_string_list optional_device_extension_list{ - u_string_list_create_from_array(optional_device_extensions, ARRAY_SIZE(optional_device_extensions))}; + u_extension_list_create_from_array(optional_device_extensions, ARRAY_SIZE(optional_device_extensions))}; U_ZERO(vk); comp_vulkan_arguments args{VK_MAKE_VERSION(1, 0, 0),