diff --git a/CMakeLists.txt b/CMakeLists.txt index cf16f6b..59ceff1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -327,7 +327,8 @@ add_library(${PROJECT_NAME} SHARED wsi/synchronization.cpp wsi/wsi_factory.cpp wsi/swapchain_image_creator.cpp - wsi/swapchain_image_create_extensions/image_compression_control.cpp) + wsi/swapchain_image_create_extensions/image_compression_control.cpp + wsi/swapchain_image_create_extensions/mutable_format_extension.cpp) if (VULKAN_WSI_LAYER_EXPERIMENTAL) target_sources(${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/layer/present_timing_api.cpp) target_sources(${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/wsi/extensions/present_timing.cpp) diff --git a/README.md b/README.md index 1a16884..4ea6b4c 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,8 @@ implements the following extensions: * VK_EXT_swapchain_maintenance1 * VK_EXT_present_mode_fifo_latest_ready (For Headless and Wayland only) * VK_KHR_present_id2 - + * VK_KHR_swapchain_mutable_format (For Headless and Wayland only) + ## Building ### Dependencies diff --git a/layer/VkLayer_window_system_integration.json b/layer/VkLayer_window_system_integration.json index ac1fe43..41c6e13 100644 --- a/layer/VkLayer_window_system_integration.json +++ b/layer/VkLayer_window_system_integration.json @@ -73,6 +73,10 @@ { "name": "VK_EXT_present_mode_fifo_latest_ready", "spec_version": "1" + }, + { + "name": "VK_KHR_swapchain_mutable_format", + "spec_version": "1" } ], "disable_environment": { diff --git a/util/helpers.hpp b/util/helpers.hpp index e43f534..556ee3f 100644 --- a/util/helpers.hpp +++ b/util/helpers.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2022, 2024 Arm Limited. + * Copyright (c) 2021-2022, 2024-2025 Arm Limited. * * SPDX-License-Identifier: MIT * @@ -77,26 +77,60 @@ namespace util { +/** + * @brief Search a Vulkan pNext chain (read-only) for a specific extension struct. + * + * Use for input pNext chains (application → driver), e.g. VkSwapchainCreateInfoKHR::pNext. + * Walks the chain via VkBaseInStructure and returns the first match. + * + * @tparam T Vulkan struct type to find (e.g., VkImageFormatListCreateInfo) + * @param sType VkStructureType corresponding to T + * @param pNext Head of the pNext chain (may be nullptr) + * @return const T* to the first matching node; nullptr if not found + * + * @note Unknown sTypes are skipped per Vulkan forward-compat rules. + */ template -const T *find_extension(VkStructureType sType, const void *pNext) +inline const T *find_extension(VkStructureType sType, const void *pNext) noexcept { - auto entry = reinterpret_cast(pNext); - while (entry && entry->sType != sType) + auto p = reinterpret_cast(pNext); + while (p) { - entry = entry->pNext; + if (p->sType == sType) + { + return reinterpret_cast(p); + } + p = p->pNext; } - return reinterpret_cast(entry); + return nullptr; } +/** + * @brief Search a Vulkan pNext chain (writable) for a specific extension struct. + * + * Use for output pNext chains (driver → application), e.g. VkPhysicalDeviceFeatures2::pNext. + * Walks the chain via VkBaseOutStructure and returns the first match. + * + * @tparam T Vulkan struct type to find (e.g., VkPhysicalDeviceVulkan11Features) + * @param sType VkStructureType corresponding to T + * @param pNext Head of the pNext chain (may be nullptr) + * @return T* to the first matching node; nullptr if not found + * + * @note Unknown sTypes are skipped per Vulkan forward-compat rules. + */ template -T *find_extension(VkStructureType sType, void *pNext) +inline T *find_extension(VkStructureType sType, void *pNext) noexcept { - auto entry = reinterpret_cast(pNext); - while (entry && entry->sType != sType) + auto p = reinterpret_cast(pNext); + while (p) { - entry = entry->pNext; + if (p->sType == sType) + { + return reinterpret_cast(p); + } + p = p->pNext; } - return reinterpret_cast(entry); + return nullptr; } template diff --git a/wsi/display/swapchain.cpp b/wsi/display/swapchain.cpp index 2f89e63..abd630b 100644 --- a/wsi/display/swapchain.cpp +++ b/wsi/display/swapchain.cpp @@ -142,6 +142,12 @@ VkResult swapchain::init_platform(VkDevice device, const VkSwapchainCreateInfoKH return VK_ERROR_OUT_OF_HOST_MEMORY; } + if (is_mutable_format_enabled()) + { + WSI_LOG_ERROR("Mutable format swapchain is not supported for display backend"); + return VK_ERROR_INITIALIZATION_FAILED; + } + return VK_SUCCESS; } diff --git a/wsi/swapchain_base.cpp b/wsi/swapchain_base.cpp index 8ff05d1..f6d12d2 100644 --- a/wsi/swapchain_base.cpp +++ b/wsi/swapchain_base.cpp @@ -45,6 +45,7 @@ #include "util/helpers.hpp" #include "swapchain_base.hpp" +#include "swapchain_image_create_extensions/mutable_format_extension.hpp" #include "wsi_factory.hpp" #include "extensions/present_timing.hpp" @@ -274,6 +275,14 @@ VkResult swapchain_base::init(VkDevice device, const VkSwapchainCreateInfoKHR *s return VK_ERROR_OUT_OF_HOST_MEMORY; } + const bool want_mutable_format = (swapchain_create_info->flags & VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR) != 0; + + /* Enable mutable-format early so backends can react in init_platform. */ + if (want_mutable_format) + { + enable_mutable_format(); + } + /* We have allocated images, we can call the platform init function if something needs to be done. */ bool use_presentation_thread = true; TRY_LOG_CALL(init_platform(device, swapchain_create_info, use_presentation_thread)); @@ -283,11 +292,11 @@ VkResult swapchain_base::init(VkDevice device, const VkSwapchainCreateInfoKHR *s TRY_LOG_CALL(init_page_flip_thread()); } + /* Initialize the image creator, and attach any required extensions */ TRY_LOG_CALL(image_creator_init(*swapchain_create_info)); m_image_create_info = m_image_creator.get_image_create_info(); - VkResult result = m_free_image_semaphore.init(m_swapchain_images.size()); - if (result != VK_SUCCESS) + if (VkResult result = m_free_image_semaphore.init(m_swapchain_images.size()); result != VK_SUCCESS) { assert(result == VK_ERROR_OUT_OF_HOST_MEMORY); return result; @@ -566,7 +575,11 @@ VkResult swapchain_base::get_swapchain_images(uint32_t *swapchain_image_count, V VkResult swapchain_base::create_aliased_image_handle(VkImage *image) { - return m_device_data.disp.CreateImage(m_device, &m_image_create_info, get_allocation_callbacks(), image); + /* Build an alias VkImageCreateInfo compatible with swapchain memory binding. */ + VkImageCreateInfo alias_info = m_image_create_info; + alias_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + + return m_device_data.disp.CreateImage(m_device, &alias_info, get_allocation_callbacks(), image); } VkResult swapchain_base::get_swapchain_status() @@ -832,8 +845,26 @@ VkResult swapchain_base::image_creator_init(const VkSwapchainCreateInfoKHR &swap { m_image_creator.init(swapchain_create_info); - util::vector> extensions(m_allocator); + util::vector> extensions( + util::allocator(m_allocator, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT)); + TRY_LOG_CALL(get_required_image_creator_extensions(swapchain_create_info, &extensions)); + + if (is_mutable_format_enabled()) + { + const auto *image_format_list = util::find_extension( + VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO, swapchain_create_info.pNext); + auto mutable_format_uptr = swapchain_image_create_mutable_format::create_unique(image_format_list, m_allocator); + if (!mutable_format_uptr) + { + return VK_ERROR_OUT_OF_HOST_MEMORY; + } + if (!extensions.try_push_back(std::move(mutable_format_uptr))) + { + return VK_ERROR_OUT_OF_HOST_MEMORY; + } + } + TRY_LOG_CALL(m_image_creator.add_extensions(extensions)); return VK_SUCCESS; diff --git a/wsi/swapchain_base.hpp b/wsi/swapchain_base.hpp index 2cf57d2..25b4ca3 100644 --- a/wsi/swapchain_base.hpp +++ b/wsi/swapchain_base.hpp @@ -316,6 +316,24 @@ public: bool add_swapchain_extension(util::unique_ptr extension); + /** + * @brief Enable mutable format swapchain support. + */ + void enable_mutable_format() + { + m_mutable_format_enabled = true; + } + + /** + * @brief Check if mutable format swapchain support is enabled. + * + * @return true if mutable format swapchain support is enabled, false otherwise. + */ + bool is_mutable_format_enabled() const + { + return m_mutable_format_enabled; + } + protected: layer::device_private_data &m_device_data; @@ -677,6 +695,11 @@ private: */ swapchain_image_creator m_image_creator; + /** + * @brief Flag indicating if mutable format swapchain support is enabled. + */ + bool m_mutable_format_enabled{ false }; + /** @brief Signal semaphores for queue submit. */ std::array signal_semaphores; diff --git a/wsi/swapchain_image_create_extensions/external_memory_extension.cpp b/wsi/swapchain_image_create_extensions/external_memory_extension.cpp index 524c69f..5090cf2 100644 --- a/wsi/swapchain_image_create_extensions/external_memory_extension.cpp +++ b/wsi/swapchain_image_create_extensions/external_memory_extension.cpp @@ -232,4 +232,5 @@ VkResult swapchain_image_create_external_memory::get_surface_compatible_formats( return VK_SUCCESS; } -}; + +} /* namespace wsi */ diff --git a/wsi/swapchain_image_create_extensions/image_compression_control.cpp b/wsi/swapchain_image_create_extensions/image_compression_control.cpp index 72c4157..8d7f5f9 100644 --- a/wsi/swapchain_image_create_extensions/image_compression_control.cpp +++ b/wsi/swapchain_image_create_extensions/image_compression_control.cpp @@ -85,4 +85,4 @@ VkResult swapchain_image_create_compression_control::extend_image_create_info(Vk return VK_SUCCESS; } -}; +} /* namespace wsi */ diff --git a/wsi/swapchain_image_create_extensions/mutable_format_extension.cpp b/wsi/swapchain_image_create_extensions/mutable_format_extension.cpp new file mode 100644 index 0000000..77682b8 --- /dev/null +++ b/wsi/swapchain_image_create_extensions/mutable_format_extension.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2025 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/** + * @file mutable_format_extension.cpp + */ + +#include "mutable_format_extension.hpp" + +namespace wsi +{ + +util::unique_ptr swapchain_image_create_mutable_format::create_unique( + const VkImageFormatListCreateInfo *image_format_list, util::allocator allocator) +{ + /** Allocate the object directly using the allocator so we don't need to move. */ + auto ptr = allocator.make_unique(allocator); + if (!ptr) + { + return nullptr; + } + + ptr->m_format_list.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO; + ptr->m_format_list.pNext = nullptr; + ptr->m_format_list.viewFormatCount = 0; + ptr->m_format_list.pViewFormats = nullptr; + + if (image_format_list && image_format_list->viewFormatCount > 0) + { + if (!ptr->m_view_formats.try_resize(image_format_list->viewFormatCount)) + { + return nullptr; + } + for (uint32_t i = 0; i < image_format_list->viewFormatCount; ++i) + { + ptr->m_view_formats[i] = image_format_list->pViewFormats[i]; + } + ptr->m_format_list.viewFormatCount = image_format_list->viewFormatCount; + ptr->m_format_list.pViewFormats = ptr->m_view_formats.data(); + } + + return ptr; +} + +VkResult swapchain_image_create_mutable_format::extend_image_create_info(VkImageCreateInfo *image_create_info) +{ + /* If this extension is present, mutable format support is requested. */ + image_create_info->flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT | VK_IMAGE_CREATE_EXTENDED_USAGE_BIT_KHR; + m_format_list.pNext = image_create_info->pNext; + image_create_info->pNext = &m_format_list; + return VK_SUCCESS; +} + +} /* namespace wsi */ diff --git a/wsi/swapchain_image_create_extensions/mutable_format_extension.hpp b/wsi/swapchain_image_create_extensions/mutable_format_extension.hpp new file mode 100644 index 0000000..a9f3af1 --- /dev/null +++ b/wsi/swapchain_image_create_extensions/mutable_format_extension.hpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2025 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/** + * @file mutable_format_extension.hpp + * + * @brief Swapchain image create info extension for VK_KHR_swapchain_mutable_format handling. + */ + +#pragma once + +#include +#include +#include "swapchain_image_create_info_extension.hpp" + +namespace wsi +{ + +class swapchain_image_create_mutable_format : public swapchain_image_create_info_extension +{ +public: + swapchain_image_create_mutable_format() = delete; + swapchain_image_create_mutable_format(const swapchain_image_create_mutable_format &) = delete; + swapchain_image_create_mutable_format &operator=(const swapchain_image_create_mutable_format &) = delete; + /** + * @brief Create and return a unique_ptr-managed instance using allocator. + * + * Allocates the object with the provided allocator and initializes its + * internal state from the optional VkImageFormatListCreateInfo. + * + * @return util::unique_ptr owning the instance, or nullptr on failure. + */ + static util::unique_ptr create_unique( + const VkImageFormatListCreateInfo *image_format_list, util::allocator allocator); + /** + * @brief Extend image_create_info pNext with extension specific data. + * A swapchain image create info extension will use this function to add its + * extension specific data to pNext of image_create_info. + * @param[in, out] image_create_info VkImageCreateInfo for creating swapchain images + * @return VkResult indicating success or failure + */ + VkResult extend_image_create_info(VkImageCreateInfo *image_create_info) override; + +private: + friend class util::allocator; + /** + * @brief Construct the extension. + * @param allocator Allocator used to own copied view formats. + */ + swapchain_image_create_mutable_format(util::allocator &allocator) + : m_view_formats(allocator) + { + } + + util::vector m_view_formats; + VkImageFormatListCreateInfo m_format_list{}; +}; + +} /* namespace wsi */ diff --git a/wsi/wayland/swapchain.cpp b/wsi/wayland/swapchain.cpp index a1727a8..3961b68 100644 --- a/wsi/wayland/swapchain.cpp +++ b/wsi/wayland/swapchain.cpp @@ -285,8 +285,10 @@ VkResult swapchain::get_surface_compatible_formats(const VkImageCreateInfo &info VkResult result = VK_SUCCESS; { + /* Build pNext chain for the format query. */ VkPhysicalDeviceExternalImageFormatInfoKHR external_info = {}; external_info.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO_KHR; + external_info.pNext = nullptr; external_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT; VkPhysicalDeviceImageDrmFormatModifierInfoEXT drm_mod_info = {}; @@ -306,8 +308,20 @@ VkResult swapchain::get_surface_compatible_formats(const VkImageCreateInfo &info image_info.usage = info.usage; image_info.flags = info.flags; - VkImageCompressionControlEXT compression_control = {}; + /* Attach view format list (if any) from the swapchain image creator + * to the image_info chain, as required by the spec. + */ + const auto *image_format_list = util::find_extension( + VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO, m_image_create_info.pNext); + VkImageFormatListCreateInfo image_format_list_copy = {}; + if (image_format_list) + { + image_format_list_copy = util::shallow_copy_extension(image_format_list); + image_format_list_copy.pNext = image_info.pNext; + image_info.pNext = &image_format_list_copy; + } + VkImageCompressionControlEXT compression_control = {}; if (m_device_data.is_swapchain_compression_control_enabled()) { auto *ext = get_swapchain_extension(); @@ -544,7 +558,7 @@ VkResult swapchain::create_swapchain_image(VkImageCreateInfo image_create_info, m_image_creation_parameters.m_allocated_format = allocated_format; } - return m_device_data.disp.CreateImage(m_device, &m_image_create_info, get_allocation_callbacks(), &image.image); + return m_device_data.disp.CreateImage(m_device, &image_create_info, get_allocation_callbacks(), &image.image); } void swapchain::present_image(const pending_present_request &pending_present)