Add external memory extension

Adds a swapchain create info extension for creating an image using
external memory. This removes the need for modifying the
image_create_info in the display and wayland backends. There is still
code duplication left in these backends though, because swapchain
allocation needs to know the selected format. This will be removed when
the swapchain images will be created from the swapchain image creator.

Moves the definition of drm_format_pair to drm_utils.

Removes two step initialization from swapchain_wsi_allocator.

Change-Id: I0c937a59cc62e7e3134f0af4728fdda3739237bd
Signed-off-by: Iason Paraskevopoulos <iason.paraskevopoulos@arm.com>
This commit is contained in:
Iason Paraskevopoulos 2025-03-27 18:06:44 +00:00
parent b326e4b21f
commit ca3820a0ba
16 changed files with 463 additions and 118 deletions

View file

@ -145,7 +145,8 @@ if(BUILD_WSI_WAYLAND)
wsi/wayland/surface_properties.cpp
wsi/wayland/surface.cpp
wsi/wayland/wl_helpers.cpp
wsi/wayland/swapchain.cpp)
wsi/wayland/swapchain.cpp
wsi/swapchain_image_create_extensions/external_memory_extension.cpp)
if(VULKAN_WSI_LAYER_EXPERIMENTAL)
target_sources(wayland_wsi PRIVATE wsi/wayland/present_timing_handler.cpp)
@ -249,7 +250,8 @@ if (BUILD_WSI_DISPLAY)
wsi/display/drm_display.cpp
wsi/display/surface_properties.cpp
wsi/display/swapchain.cpp
wsi/display/surface.cpp)
wsi/display/surface.cpp
wsi/swapchain_image_create_extensions/external_memory_extension.cpp)
pkg_check_modules(LIBDRM REQUIRED libdrm)
message(STATUS "Using libdrm include directories: ${LIBDRM_INCLUDE_DIRS}")

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2021-2022, 2024 Arm Limited.
* Copyright (c) 2019-2025 Arm Limited.
*
* SPDX-License-Identifier: MIT
*
@ -31,6 +31,15 @@ namespace util
namespace drm
{
/**
* @brief Struct describing a DRM format with modifier.
*/
struct drm_format_pair
{
uint32_t fourcc;
uint64_t modifier;
};
uint32_t vk_to_drm_format(VkFormat vk_format);
VkFormat drm_to_vk_format(uint32_t drm_format);
VkFormat drm_to_vk_srgb_format(uint32_t drm_format);

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2024 Arm Limited.
* Copyright (c) 2024-2025 Arm Limited.
*
* SPDX-License-Identifier: MIT
*
@ -44,7 +44,7 @@ namespace display
const std::string default_dri_device_name{ "/dev/dri/card0" };
drm_display::drm_display(util::fd_owner drm_fd, int crtc_id, drm_connector_owner drm_connector,
util::unique_ptr<util::vector<drm_format_pair>> supported_formats,
util::unique_ptr<util::vector<util::drm::drm_format_pair>> supported_formats,
util::unique_ptr<drm_display_mode> display_modes, size_t num_display_modes, uint32_t max_width,
uint32_t max_height, bool supports_fb_modifiers)
: m_drm_fd(std::move(drm_fd))
@ -147,11 +147,12 @@ static bool find_primary_plane(const util::fd_owner &drm_fd, const drm_plane_res
}
static bool fill_supported_formats(const drm_plane_owner &primary_plane,
util::vector<drm_format_pair> &supported_formats)
util::vector<util::drm::drm_format_pair> &supported_formats)
{
for (uint32_t i = 0; i < primary_plane->count_formats; i++)
{
if (!supported_formats.try_push_back(drm_format_pair{ primary_plane->formats[i], DRM_FORMAT_MOD_LINEAR }))
if (!supported_formats.try_push_back(
util::drm::drm_format_pair{ primary_plane->formats[i], DRM_FORMAT_MOD_LINEAR }))
{
WSI_LOG_ERROR("Out of host memory.");
return false;
@ -163,7 +164,7 @@ static bool fill_supported_formats(const drm_plane_owner &primary_plane,
static bool fill_supported_formats_with_modifiers(uint32_t primary_plane_index, const util::fd_owner &drm_fd,
const drm_plane_resources_owner &plane_res,
util::vector<drm_format_pair> &supported_formats)
util::vector<util::drm::drm_format_pair> &supported_formats)
{
drm_object_properties_owner object_properties{ drmModeObjectGetProperties(
drm_fd.get(), plane_res->planes[primary_plane_index], DRM_MODE_OBJECT_PLANE) };
@ -191,7 +192,7 @@ static bool fill_supported_formats_with_modifiers(uint32_t primary_plane_index,
while (drmModeFormatModifierBlobIterNext(blob.get(), &iter))
{
if (!supported_formats.try_push_back(drm_format_pair{ iter.fmt, iter.mod }))
if (!supported_formats.try_push_back(util::drm::drm_format_pair{ iter.fmt, iter.mod }))
{
return false;
}
@ -317,7 +318,7 @@ std::optional<drm_display> drm_display::make_display(const util::allocator &allo
}
#endif
auto supported_formats = allocator.make_unique<util::vector<drm_format_pair>>(allocator);
auto supported_formats = allocator.make_unique<util::vector<util::drm::drm_format_pair>>(allocator);
if (supports_fb_modifiers)
{
@ -365,12 +366,12 @@ std::optional<drm_display> &drm_display::get_display()
return display;
}
const util::vector<drm_format_pair> *drm_display::get_supported_formats() const
const util::vector<util::drm::drm_format_pair> *drm_display::get_supported_formats() const
{
return m_supported_formats.get();
}
bool drm_display::is_format_supported(const drm_format_pair &format) const
bool drm_display::is_format_supported(const util::drm::drm_format_pair &format) const
{
auto supported_format =
std::find_if(m_supported_formats->begin(), m_supported_formats->end(), [format](const auto &supported_format) {

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2024 Arm Limited.
* Copyright (c) 2024-2025 Arm Limited.
*
* SPDX-License-Identifier: MIT
*
@ -256,7 +256,7 @@ public:
*
* @return Pointer to vector of supported formats.
*/
const util::vector<drm_format_pair> *get_supported_formats() const;
const util::vector<util::drm::drm_format_pair> *get_supported_formats() const;
/**
* @brief Query the display for support for adding framebuffers with format modifiers.
@ -271,7 +271,7 @@ public:
* @param format The format to query support for.
* @return true if the format is supported by the display, otherwise false.
*/
bool is_format_supported(const drm_format_pair &format) const;
bool is_format_supported(const util::drm::drm_format_pair &format) const;
/**
* @brief Returns a CRTC compatible with this display's connector.
@ -297,7 +297,7 @@ private:
* @param allocator The allocator that the display will use.
*/
drm_display(util::fd_owner drm_fd, int crtc_id, drm_connector_owner drm_connector,
util::unique_ptr<util::vector<drm_format_pair>> supported_formats,
util::unique_ptr<util::vector<util::drm::drm_format_pair>> supported_formats,
util::unique_ptr<drm_display_mode> display_modes, size_t num_display_modes, uint32_t max_width,
uint32_t max_height, bool supports_fb_modifiers);
@ -319,7 +319,7 @@ private:
/**
* @brief Vector of supported formats for use with the display.
*/
util::unique_ptr<util::vector<drm_format_pair>> m_supported_formats;
util::unique_ptr<util::vector<util::drm::drm_format_pair>> m_supported_formats;
/**
* @brief Pointer to available display modes for the connected display.

View file

@ -39,6 +39,8 @@
#include "swapchain.hpp"
#include <wsi/swapchain_image_create_extensions/external_memory_extension.hpp>
namespace wsi
{
@ -48,7 +50,7 @@ namespace display
swapchain::swapchain(layer::device_private_data &dev_data, const VkAllocationCallbacks *pAllocator,
surface &wsi_surface)
: wsi::swapchain_base(dev_data, pAllocator)
, m_wsi_allocator()
, m_wsi_allocator(nullptr)
, m_display_mode(wsi_surface.get_display_mode())
, m_image_creation_parameters({}, m_allocator, {}, {})
{
@ -108,7 +110,17 @@ VkResult swapchain::init_platform(VkDevice device, const VkSwapchainCreateInfoKH
UNUSED(swapchain_create_info);
UNUSED(use_presentation_thread);
TRY(m_wsi_allocator.init());
auto wsi_allocator = swapchain_wsialloc_allocator::create();
if (!wsi_allocator.has_value())
{
return VK_ERROR_INITIALIZATION_FAILED;
}
m_wsi_allocator = m_allocator.make_unique<swapchain_wsialloc_allocator>(std::move(*wsi_allocator));
if (m_wsi_allocator == nullptr)
{
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
return VK_SUCCESS;
}
@ -139,7 +151,7 @@ VkResult swapchain::get_surface_compatible_formats(const VkImageCreateInfo &info
for (const auto &prop : drm_format_props)
{
drm_format_pair drm_format{ util::drm::vk_to_drm_format(info.format), prop.drmFormatModifier };
util::drm::drm_format_pair drm_format{ util::drm::vk_to_drm_format(info.format), prop.drmFormatModifier };
if (!display->is_format_supported(drm_format))
{
@ -264,7 +276,7 @@ VkResult swapchain::allocate_wsialloc(VkImageCreateInfo &image_create_info, disp
allocation_params params = { (image_create_info.flags & VK_IMAGE_CREATE_PROTECTED_BIT) != 0,
image_create_info.extent, importable_formats, enable_fixed_rate, avoid_allocation };
wsialloc_allocate_result alloc_result = { {}, { 0 }, { 0 }, { -1 }, false };
TRY(m_wsi_allocator.allocate(params, &alloc_result));
TRY(m_wsi_allocator->allocate(params, &alloc_result));
*allocated_format = alloc_result.format;
@ -298,26 +310,6 @@ VkResult swapchain::allocate_wsialloc(VkImageCreateInfo &image_create_info, disp
return VK_SUCCESS;
}
static VkResult fill_image_create_info(VkImageCreateInfo &image_create_info,
util::vector<VkSubresourceLayout> &image_plane_layouts,
VkImageDrmFormatModifierExplicitCreateInfoEXT &drm_mod_info,
VkExternalMemoryImageCreateInfoKHR &external_info,
display_image_data &image_data, uint64_t modifier)
{
TRY_LOG_CALL(image_data.external_mem.fill_image_plane_layouts(image_plane_layouts));
if (image_data.external_mem.is_disjoint())
{
image_create_info.flags |= VK_IMAGE_CREATE_DISJOINT_BIT;
}
image_data.external_mem.fill_drm_mod_info(image_create_info.pNext, drm_mod_info, image_plane_layouts, modifier);
image_data.external_mem.fill_external_info(external_info, &drm_mod_info);
image_create_info.pNext = &external_info;
image_create_info.tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT;
return VK_SUCCESS;
}
VkResult swapchain::allocate_image(display_image_data *image_data)
{
util::vector<wsialloc_format> importable_formats(util::allocator(m_allocator, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND));
@ -336,7 +328,7 @@ VkResult swapchain::create_framebuffer(const VkImageCreateInfo &image_create_inf
VkResult ret_code = VK_SUCCESS;
std::array<uint32_t, util::MAX_PLANES> strides{ 0, 0, 0, 0 };
std::array<uint64_t, util::MAX_PLANES> modifiers{ 0, 0, 0, 0 };
const drm_format_pair allocated_format{ m_image_creation_parameters.m_allocated_format.fourcc,
const util::drm::drm_format_pair allocated_format{ m_image_creation_parameters.m_allocated_format.fourcc,
m_image_creation_parameters.m_allocated_format.modifier };
auto &display = drm_display::get_display();
@ -427,7 +419,7 @@ VkResult swapchain::create_swapchain_image(VkImageCreateInfo image_create_info,
}
image.data = image_data;
if (m_image_create_info.format == VK_FORMAT_UNDEFINED)
if (m_image_creation_parameters.m_allocated_format.fourcc == DRM_FORMAT_INVALID)
{
util::vector<wsialloc_format> importable_formats(
util::allocator(m_allocator, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND));
@ -459,11 +451,6 @@ VkResult swapchain::create_swapchain_image(VkImageCreateInfo image_create_info,
}
}
TRY_LOG_CALL(fill_image_create_info(
image_create_info, m_image_creation_parameters.m_image_layout, m_image_creation_parameters.m_drm_mod_info,
m_image_creation_parameters.m_external_info, *image_data, allocated_format.modifier));
m_image_create_info = image_create_info;
m_image_creation_parameters.m_allocated_format = allocated_format;
}
@ -640,8 +627,22 @@ VkResult swapchain::get_required_image_creator_extensions(
const VkSwapchainCreateInfoKHR &swapchain_create_info,
util::vector<util::unique_ptr<swapchain_image_create_info_extension>> *extensions)
{
UNUSED(swapchain_create_info);
UNUSED(extensions);
auto compression_control = swapchain_image_create_compression_control::create(
m_device_data.is_swapchain_compression_control_enabled(), swapchain_create_info);
auto &display = drm_display::get_display();
if (!display.has_value())
{
WSI_LOG_ERROR("DRM display not available.");
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
if (!extensions->try_push_back(m_allocator.make_unique<swapchain_image_create_external_memory>(
compression_control, m_wsi_allocator.get(), display->get_supported_formats(), m_device_data.physical_device,
m_allocator)))
{
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
return VK_SUCCESS;
}

View file

@ -146,7 +146,7 @@ private:
*/
VkResult add_required_extensions(VkDevice device, const VkSwapchainCreateInfoKHR *swapchain_create_info) override;
swapchain_wsialloc_allocator m_wsi_allocator;
util::unique_ptr<swapchain_wsialloc_allocator> m_wsi_allocator;
drm_display_mode *m_display_mode;
image_creation_parameters m_image_creation_parameters;

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, 2024 Arm Limited.
* Copyright (c) 2021-2025 Arm Limited.
*
* SPDX-License-Identifier: MIT
*
@ -36,15 +36,6 @@
namespace wsi
{
/**
* @brief Struct describing a DRM format with modifier.
*/
struct drm_format_pair
{
uint32_t fourcc;
uint64_t modifier;
};
/**
* @brief A generic WSI representation of a VkSurface.
*

View file

@ -0,0 +1,235 @@
/*
* 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 external_memory_extension.cpp
*/
#include <cassert>
#include <util/helpers.hpp>
#include <util/format_modifiers.hpp>
#include <layer/private_data.hpp>
#include "external_memory_extension.hpp"
namespace wsi
{
VkResult swapchain_image_create_external_memory::extend_image_create_info(VkImageCreateInfo *image_create_info)
{
assert(image_create_info != nullptr);
util::vector<wsialloc_format> importable_formats(util::allocator(m_allocator, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND));
util::vector<uint64_t> exportable_modifiers(util::allocator(m_allocator, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND));
/* Query supported modifers. */
util::vector<VkDrmFormatModifierPropertiesEXT> drm_format_props(
util::allocator(m_allocator, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND));
TRY_LOG_CALL(
get_surface_compatible_formats(*image_create_info, importable_formats, exportable_modifiers, drm_format_props));
/* TODO: Handle exportable images which use ICD allocated memory in preference to an external allocator. */
if (importable_formats.empty())
{
WSI_LOG_ERROR("Export/Import not supported.");
return VK_ERROR_INITIALIZATION_FAILED;
}
bool enable_fixed_rate = false;
if (m_compression)
{
if (m_compression->get_bitmask_for_image_compression_flags() & VK_IMAGE_COMPRESSION_FIXED_RATE_EXPLICIT_EXT)
{
enable_fixed_rate = true;
}
}
allocation_params params = { (image_create_info->flags & VK_IMAGE_CREATE_PROTECTED_BIT) != 0,
image_create_info->extent, importable_formats, enable_fixed_rate, true };
wsialloc_allocate_result alloc_result = { {}, { 0 }, { 0 }, { -1 }, false };
assert(m_wsi_allocator);
TRY(m_wsi_allocator->allocate(params, &alloc_result));
uint32_t prop_plane_count = 0;
for (auto &prop : drm_format_props)
{
if (prop.drmFormatModifier == alloc_result.format.modifier)
{
prop_plane_count = prop.drmFormatModifierPlaneCount;
}
}
if (alloc_result.is_disjoint)
{
image_create_info->flags |= VK_IMAGE_CREATE_DISJOINT_BIT;
}
const uint32_t format_planes = util::drm::drm_fourcc_format_get_num_planes(alloc_result.format.fourcc);
if (!m_plane_layouts.try_resize(format_planes))
{
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
for (uint32_t plane = 0; plane < format_planes; ++plane)
{
assert(alloc_result.average_row_strides[plane] >= 0);
m_plane_layouts[plane].offset = alloc_result.offsets[plane];
m_plane_layouts[plane].rowPitch = static_cast<uint32_t>(alloc_result.average_row_strides[plane]);
}
m_drm_mod_info.sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_EXPLICIT_CREATE_INFO_EXT;
m_drm_mod_info.pNext = image_create_info->pNext;
m_drm_mod_info.drmFormatModifier = alloc_result.format.modifier;
m_drm_mod_info.drmFormatModifierPlaneCount = prop_plane_count;
m_drm_mod_info.pPlaneLayouts = m_plane_layouts.data();
m_external_info.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_KHR;
m_external_info.pNext = &m_drm_mod_info;
m_external_info.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT;
image_create_info->pNext = &m_external_info;
image_create_info->tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT;
return VK_SUCCESS;
}
VkResult swapchain_image_create_external_memory::get_surface_compatible_formats(
const VkImageCreateInfo &info, util::vector<wsialloc_format> &importable_formats,
util::vector<uint64_t> &exportable_modifers, util::vector<VkDrmFormatModifierPropertiesEXT> &drm_format_props)
{
TRY_LOG(util::get_drm_format_properties(m_physical_device, info.format, drm_format_props),
"Failed to get format properties");
for (const auto &prop : drm_format_props)
{
bool is_supported = true;
util::drm::drm_format_pair drm_format{ util::drm::vk_to_drm_format(info.format), prop.drmFormatModifier };
if (m_surface_formats != nullptr)
{
is_supported = false;
for (const auto &format : *m_surface_formats)
{
if (format.fourcc == drm_format.fourcc && format.modifier == drm_format.modifier)
{
is_supported = true;
break;
}
}
}
if (!is_supported)
{
continue;
}
VkExternalImageFormatPropertiesKHR external_props = {};
external_props.sType = VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES_KHR;
VkImageFormatProperties2KHR format_props = {};
format_props.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHR;
format_props.pNext = &external_props;
VkResult result = VK_SUCCESS;
{
VkPhysicalDeviceExternalImageFormatInfoKHR external_info = {};
external_info.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO_KHR;
external_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT;
VkPhysicalDeviceImageDrmFormatModifierInfoEXT drm_mod_info = {};
drm_mod_info.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_DRM_FORMAT_MODIFIER_INFO_EXT;
drm_mod_info.pNext = &external_info;
drm_mod_info.drmFormatModifier = prop.drmFormatModifier;
drm_mod_info.sharingMode = info.sharingMode;
drm_mod_info.queueFamilyIndexCount = info.queueFamilyIndexCount;
drm_mod_info.pQueueFamilyIndices = info.pQueueFamilyIndices;
VkPhysicalDeviceImageFormatInfo2KHR image_info = {};
image_info.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2_KHR;
image_info.pNext = &drm_mod_info;
image_info.format = info.format;
image_info.type = info.imageType;
image_info.tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT;
image_info.usage = info.usage;
image_info.flags = info.flags;
VkImageCompressionControlEXT compression_control = {};
if (m_compression)
{
compression_control = m_compression->get_compression_control_properties();
compression_control.pNext = image_info.pNext;
image_info.pNext = &compression_control;
}
auto &instance_data = layer::instance_private_data::get(m_physical_device);
result = instance_data.disp.GetPhysicalDeviceImageFormatProperties2KHR(m_physical_device, &image_info,
&format_props);
}
if (result != VK_SUCCESS)
{
continue;
}
if (format_props.imageFormatProperties.maxExtent.width < info.extent.width ||
format_props.imageFormatProperties.maxExtent.height < info.extent.height ||
format_props.imageFormatProperties.maxExtent.depth < info.extent.depth)
{
continue;
}
if (format_props.imageFormatProperties.maxMipLevels < info.mipLevels ||
format_props.imageFormatProperties.maxArrayLayers < info.arrayLayers)
{
continue;
}
if ((format_props.imageFormatProperties.sampleCounts & info.samples) != info.samples)
{
continue;
}
if (external_props.externalMemoryProperties.externalMemoryFeatures &
VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT_KHR)
{
if (!exportable_modifers.try_push_back(drm_format.modifier))
{
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
}
if (external_props.externalMemoryProperties.externalMemoryFeatures &
VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_KHR)
{
uint64_t flags =
(prop.drmFormatModifierTilingFeatures & VK_FORMAT_FEATURE_DISJOINT_BIT) ? 0 : WSIALLOC_FORMAT_NON_DISJOINT;
wsialloc_format import_format{ drm_format.fourcc, drm_format.modifier, flags };
if (!importable_formats.try_push_back(import_format))
{
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
}
}
return VK_SUCCESS;
}
};

View file

@ -0,0 +1,89 @@
/*
* 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 external_memory_extension.hpp
*/
#pragma once
#include <optional>
#include <utility>
#include <util/custom_allocator.hpp>
#include <util/macros.hpp>
#include "util/drm/drm_utils.hpp"
#include <wsi/wsi_alloc_utils.hpp>
#include "swapchain_image_create_info_extension.hpp"
#include "image_compression_control.hpp"
namespace wsi
{
using util::MAX_PLANES;
/**
* @brief
*/
class swapchain_image_create_external_memory : public swapchain_image_create_info_extension
{
public:
swapchain_image_create_external_memory(std::optional<swapchain_image_create_compression_control> compression,
swapchain_wsialloc_allocator *wsi_allocator,
const util::vector<util::drm::drm_format_pair> *surface_formats,
VkPhysicalDevice physical_device, const util::allocator &allocator)
: m_compression(compression)
, m_wsi_allocator(wsi_allocator)
, m_surface_formats(surface_formats)
, m_physical_device(physical_device)
, m_allocator(allocator)
, m_plane_layouts(allocator)
, m_drm_mod_info()
, m_external_info()
, m_allocated_format()
{
}
VkResult extend_image_create_info(VkImageCreateInfo *image_create_info) override;
VkResult get_surface_compatible_formats(const VkImageCreateInfo &info,
util::vector<wsialloc_format> &importable_formats,
util::vector<uint64_t> &exportable_modifers,
util::vector<VkDrmFormatModifierPropertiesEXT> &drm_format_props);
private:
std::optional<swapchain_image_create_compression_control> m_compression;
swapchain_wsialloc_allocator *m_wsi_allocator;
const util::vector<util::drm::drm_format_pair> *m_surface_formats;
VkPhysicalDevice m_physical_device;
const util::allocator &m_allocator;
util::vector<VkSubresourceLayout> m_plane_layouts;
VkImageDrmFormatModifierExplicitCreateInfoEXT m_drm_mod_info;
VkExternalMemoryImageCreateInfoKHR m_external_info;
wsialloc_allocate_result m_allocated_format;
};
} /* namespace wsi */

View file

@ -40,7 +40,7 @@ namespace wayland
struct formats_vector
{
util::vector<drm_format_pair> *formats{ nullptr };
util::vector<util::drm::drm_format_pair> *formats{ nullptr };
bool is_out_of_memory{ false };
};
@ -63,7 +63,7 @@ zwp_linux_dmabuf_v1_modifier_impl(void *data, struct zwp_linux_dmabuf_v1 *dma_bu
UNUSED(dma_buf);
auto *drm_supported_formats = reinterpret_cast<formats_vector *>(data);
drm_format_pair format = {};
util::drm::drm_format_pair format = {};
format.fourcc = drm_format;
format.modifier = (static_cast<uint64_t>(modifier_hi) << 32) | modifier_low;
@ -89,7 +89,7 @@ zwp_linux_dmabuf_v1_modifier_impl(void *data, struct zwp_linux_dmabuf_v1 *dma_bu
*/
static VkResult get_supported_formats_and_modifiers(wl_display *display, wl_event_queue *queue,
zwp_linux_dmabuf_v1 *dmabuf_interface,
util::vector<drm_format_pair> &supported_formats)
util::vector<util::drm::drm_format_pair> &supported_formats)
{
formats_vector drm_supported_formats;
drm_supported_formats.formats = &supported_formats;

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, 2024 Arm Limited.
* Copyright (c) 2021-2025 Arm Limited.
*
* SPDX-License-Identifier: MIT
*
@ -38,6 +38,8 @@
#include "wl_object_owner.hpp"
#include "util/macros.hpp"
#include <util/drm/drm_utils.hpp>
namespace wsi
{
namespace wayland
@ -116,7 +118,7 @@ public:
*
* The reference is valid throughout the lifetime of this surface.
*/
const util::vector<drm_format_pair> &get_formats() const
const util::vector<util::drm::drm_format_pair> &get_formats() const
{
return supported_formats;
}
@ -162,7 +164,7 @@ private:
/** The native Wayland surface */
wl_surface *wayland_surface;
/** A list of DRM formats supported by the Wayland compositor on this surface */
util::vector<drm_format_pair> supported_formats;
util::vector<util::drm::drm_format_pair> supported_formats;
/** Surface properties specific to the Wayland surface. */
surface_properties properties;

View file

@ -118,7 +118,7 @@ VkResult surface_properties::get_surface_capabilities(VkPhysicalDevice physical_
static VkResult surface_format_properties_add_modifier_support(VkPhysicalDevice phys_dev,
surface_format_properties &format_props,
const drm_format_pair &drm_format,
const util::drm::drm_format_pair &drm_format,
bool add_compression = false)
{
VkPhysicalDeviceExternalImageFormatInfoKHR external_info = {};
@ -148,7 +148,7 @@ static VkResult surface_format_properties_add_modifier_support(VkPhysicalDevice
}
static VkResult surface_format_properties_map_add(VkPhysicalDevice phys_dev, surface_format_properties_map &format_map,
VkFormat format, const drm_format_pair &drm_format)
VkFormat format, const util::drm::drm_format_pair &drm_format)
{
surface_format_properties format_props{ format };
VkResult res = surface_format_properties_add_modifier_support(phys_dev, format_props, drm_format);
@ -170,7 +170,7 @@ static VkResult surface_format_properties_map_add(VkPhysicalDevice phys_dev, sur
}
static VkResult surface_format_properties_map_init(VkPhysicalDevice phys_dev, surface_format_properties_map &format_map,
const util::vector<drm_format_pair> &drm_format_list)
const util::vector<util::drm::drm_format_pair> &drm_format_list)
{
for (const auto &drm_format : drm_format_list)
{
@ -189,9 +189,9 @@ static VkResult surface_format_properties_map_init(VkPhysicalDevice phys_dev, su
return VK_SUCCESS;
}
static VkResult surface_format_properties_map_add_compression(VkPhysicalDevice phys_dev,
surface_format_properties_map &format_map,
const util::vector<drm_format_pair> &drm_format_list)
static VkResult surface_format_properties_map_add_compression(
VkPhysicalDevice phys_dev, surface_format_properties_map &format_map,
const util::vector<util::drm::drm_format_pair> &drm_format_list)
{
for (const auto &drm_format : drm_format_list)
{

View file

@ -63,10 +63,9 @@ swapchain::swapchain(layer::device_private_data &dev_data, const VkAllocationCal
, m_surface(wsi_surface.get_wl_surface())
, m_wsi_surface(&wsi_surface)
, m_buffer_queue(nullptr)
, m_wsi_allocator()
, m_wsi_allocator(nullptr)
, m_image_creation_parameters({}, m_allocator, {}, {})
{
m_image_create_info.format = VK_FORMAT_UNDEFINED;
}
swapchain::~swapchain()
@ -147,7 +146,17 @@ VkResult swapchain::init_platform(VkDevice device, const VkSwapchainCreateInfoKH
return VK_ERROR_INITIALIZATION_FAILED;
}
TRY(m_wsi_allocator.init());
auto wsi_allocator = swapchain_wsialloc_allocator::create();
if (!wsi_allocator.has_value())
{
return VK_ERROR_INITIALIZATION_FAILED;
}
m_wsi_allocator = m_allocator.make_unique<swapchain_wsialloc_allocator>(std::move(*wsi_allocator));
if (m_wsi_allocator == nullptr)
{
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
/*
* When VK_PRESENT_MODE_MAILBOX_KHR has been chosen by the application we don't
@ -196,7 +205,7 @@ VkResult swapchain::get_surface_compatible_formats(const VkImageCreateInfo &info
for (const auto &prop : drm_format_props)
{
bool is_supported = false;
drm_format_pair drm_format{ util::drm::vk_to_drm_format(info.format), prop.drmFormatModifier };
util::drm::drm_format_pair drm_format{ util::drm::vk_to_drm_format(info.format), prop.drmFormatModifier };
for (const auto &format : m_wsi_surface->get_formats())
{
@ -326,7 +335,7 @@ VkResult swapchain::allocate_wsialloc(VkImageCreateInfo &image_create_info, wayl
allocation_params params = { (image_create_info.flags & VK_IMAGE_CREATE_PROTECTED_BIT) != 0,
image_create_info.extent, importable_formats, enable_fixed_rate, avoid_allocation };
wsialloc_allocate_result alloc_result = { {}, { 0 }, { 0 }, { -1 }, false };
TRY(m_wsi_allocator.allocate(params, &alloc_result));
TRY(m_wsi_allocator->allocate(params, &alloc_result));
*allocated_format = alloc_result.format;
@ -360,26 +369,6 @@ VkResult swapchain::allocate_wsialloc(VkImageCreateInfo &image_create_info, wayl
return VK_SUCCESS;
}
static VkResult fill_image_create_info(VkImageCreateInfo &image_create_info,
util::vector<VkSubresourceLayout> &image_plane_layouts,
VkImageDrmFormatModifierExplicitCreateInfoEXT &drm_mod_info,
VkExternalMemoryImageCreateInfoKHR &external_info,
wayland_image_data &image_data, uint64_t modifier)
{
TRY_LOG_CALL(image_data.external_mem.fill_image_plane_layouts(image_plane_layouts));
if (image_data.external_mem.is_disjoint())
{
image_create_info.flags |= VK_IMAGE_CREATE_DISJOINT_BIT;
}
image_data.external_mem.fill_drm_mod_info(image_create_info.pNext, drm_mod_info, image_plane_layouts, modifier);
image_data.external_mem.fill_external_info(external_info, &drm_mod_info);
image_create_info.pNext = &external_info;
image_create_info.tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT;
return VK_SUCCESS;
}
VkResult swapchain::allocate_image(wayland_image_data *image_data)
{
util::vector<wsialloc_format> importable_formats(util::allocator(m_allocator, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND));
@ -460,13 +449,13 @@ VkResult swapchain::create_swapchain_image(VkImageCreateInfo image_create_info,
}
image.data = image_data;
if (m_image_create_info.format == VK_FORMAT_UNDEFINED)
if (m_image_creation_parameters.m_allocated_format.fourcc == DRM_FORMAT_INVALID)
{
util::vector<wsialloc_format> importable_formats(
util::allocator(m_allocator, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND));
util::vector<uint64_t> exportable_modifiers(util::allocator(m_allocator, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND));
/* Query supported modifers. */
/* Query supported modifiers. */
util::vector<VkDrmFormatModifierPropertiesEXT> drm_format_props(
util::allocator(m_allocator, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND));
@ -491,11 +480,6 @@ VkResult swapchain::create_swapchain_image(VkImageCreateInfo image_create_info,
}
}
TRY_LOG_CALL(fill_image_create_info(
image_create_info, m_image_creation_parameters.m_image_layout, m_image_creation_parameters.m_drm_mod_info,
m_image_creation_parameters.m_external_info, *image_data, allocated_format.modifier));
m_image_create_info = image_create_info;
m_image_creation_parameters.m_allocated_format = allocated_format;
}
@ -667,8 +651,15 @@ VkResult swapchain::get_required_image_creator_extensions(
const VkSwapchainCreateInfoKHR &swapchain_create_info,
util::vector<util::unique_ptr<swapchain_image_create_info_extension>> *extensions)
{
UNUSED(swapchain_create_info);
UNUSED(extensions);
auto compression_control = swapchain_image_create_compression_control::create(
m_device_data.is_swapchain_compression_control_enabled(), swapchain_create_info);
if (!extensions->try_push_back(m_allocator.make_unique<swapchain_image_create_external_memory>(
compression_control, m_wsi_allocator.get(), &m_wsi_surface->get_formats(), m_device_data.physical_device,
m_allocator)))
{
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
return VK_SUCCESS;
}

View file

@ -222,7 +222,7 @@ private:
/**
* @brief Handle to the WSI allocator.
*/
swapchain_wsialloc_allocator m_wsi_allocator;
util::unique_ptr<swapchain_wsialloc_allocator> m_wsi_allocator;
/**
* @brief Image creation parameters used for all swapchain images.

View file

@ -33,15 +33,16 @@
namespace wsi
{
VkResult swapchain_wsialloc_allocator::init()
std::optional<swapchain_wsialloc_allocator> swapchain_wsialloc_allocator::create()
{
wsialloc_allocator *allocator = nullptr;
WSIALLOC_ASSERT_VERSION();
if (wsialloc_new(&m_allocator) != WSIALLOC_ERROR_NONE)
if (wsialloc_new(&allocator) != WSIALLOC_ERROR_NONE)
{
WSI_LOG_ERROR("Failed to create wsi allocator.");
return VK_ERROR_INITIALIZATION_FAILED;
return std::nullopt;
}
return VK_SUCCESS;
return swapchain_wsialloc_allocator(allocator);
}
VkResult swapchain_wsialloc_allocator::allocate(const allocation_params &input, wsialloc_allocate_result *alloc_result)

View file

@ -30,6 +30,7 @@
#include <optional>
#include "util/wsialloc/wsialloc.h"
#include <util/helpers.hpp>
#include "util/custom_allocator.hpp"
@ -50,13 +51,31 @@ struct allocation_params
class swapchain_wsialloc_allocator
{
public:
swapchain_wsialloc_allocator(wsialloc_allocator *allocator)
: m_allocator(allocator)
static std::optional<swapchain_wsialloc_allocator> create();
swapchain_wsialloc_allocator(swapchain_wsialloc_allocator &other) = delete;
swapchain_wsialloc_allocator &operator=(swapchain_wsialloc_allocator &other) = delete;
swapchain_wsialloc_allocator(swapchain_wsialloc_allocator &&other)
: m_allocator(other.m_allocator)
{
other.m_allocator = nullptr;
}
swapchain_wsialloc_allocator()
: m_allocator(nullptr){};
swapchain_wsialloc_allocator &operator=(swapchain_wsialloc_allocator &&other)
{
if (this == &other)
{
return *this;
}
if (m_allocator != nullptr)
{
wsialloc_delete(m_allocator);
m_allocator = nullptr;
}
std::swap(m_allocator, other.m_allocator);
}
~swapchain_wsialloc_allocator()
{
@ -71,7 +90,6 @@ public:
return m_allocator;
}
VkResult init();
VkResult allocate(const allocation_params &input, wsialloc_allocate_result *alloc_result);
private:
@ -79,6 +97,11 @@ private:
* @brief Handle to the WSI allocator.
*/
wsialloc_allocator *m_allocator;
swapchain_wsialloc_allocator(wsialloc_allocator *allocator)
: m_allocator(allocator)
{
}
};
} /* namespace wsi */