Decouple swapchain images and their memory from swapchain class

Decouples the swapchain images and their memory binder/allocator logic into separate classes. This allows us to reduce code duplication across different backends that use the same type of allocation logic and also allows us to make use of RAII to release the resources for swapchain images.

The swapchain_base and other swapchain classes have been refactored to handle the new swapchain images.

The patch makes the following adjustments:

* Introduces a new swapchain_image class that holds all swapchain image resources
* Introduces a swapchain image factory class that constructs swapchain images
* Introduces a Vulkan image handle class that is responsible for constructing VkImage handles
* Introduces a new interface that describes how swapchain backing memory is being allocated
* As part of this backing memory interface, introduces device and external (wsialloc) backing memory classes
* Introduces a new interface that holds swapchain image data like wl_buffers
* Refactors lots of swapchain image parts out of the base swapchain class and moves it into the respective backends to simplify the swapchain classes.
This commit is contained in:
Normunds Rieksts 2025-11-20 13:55:45 +00:00 committed by Rosen Zhelev
parent 72a21a9e87
commit 2d4cbd5afa
43 changed files with 2340 additions and 2009 deletions

View file

@ -151,7 +151,8 @@ if(BUILD_WSI_WAYLAND)
wsi/wayland/wl_helpers.cpp
wsi/wayland/swapchain.cpp
wsi/wayland/present_wait_wayland.cpp
wsi/swapchain_image_create_extensions/external_memory_extension.cpp)
wsi/extensions/external_memory_extension.cpp
wsi/image_backing_memory_external.cpp)
if(VULKAN_WSI_LAYER_EXPERIMENTAL)
target_sources(wayland_wsi PRIVATE wsi/wayland/present_id_wayland.cpp)
@ -239,7 +240,8 @@ if(BUILD_WSI_HEADLESS)
wsi/headless/surface_properties.cpp
wsi/headless/surface.cpp
wsi/headless/swapchain.cpp
wsi/headless/present_wait_headless.cpp)
wsi/headless/present_wait_headless.cpp
wsi/image_backing_memory_device.cpp)
if(VULKAN_WSI_LAYER_EXPERIMENTAL)
target_sources(wsi_headless PRIVATE wsi/headless/present_timing_handler.cpp)
@ -264,8 +266,9 @@ if (BUILD_WSI_DISPLAY)
wsi/display/surface_properties.cpp
wsi/display/swapchain.cpp
wsi/display/surface.cpp
wsi/swapchain_image_create_extensions/external_memory_extension.cpp
wsi/display/present_wait_display.cpp)
wsi/extensions/external_memory_extension.cpp
wsi/display/present_wait_display.cpp
wsi/image_backing_memory_external.cpp)
pkg_check_modules(LIBDRM REQUIRED libdrm)
message(STATUS "Using libdrm include directories: ${LIBDRM_INCLUDE_DIRS}")
@ -313,15 +316,16 @@ add_library(${PROJECT_NAME} SHARED
wsi/extensions/present_id.cpp
wsi/extensions/present_wait.cpp
wsi/extensions/frame_boundary.cpp
wsi/extensions/wsi_extension.cpp
wsi/extensions/mutable_format_extension.cpp
util/wsi_extension.cpp
wsi/extensions/swapchain_maintenance.cpp
wsi/surface_properties.cpp
wsi/swapchain_base.cpp
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/mutable_format_extension.cpp)
wsi/vulkan_image_handle_creator.cpp
wsi/swapchain_image_factory.cpp
wsi/swapchain_image.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)

View file

@ -429,9 +429,8 @@ wsi_layer_vkBindImageMemory2(VkDevice device, uint32_t bindInfoCount,
else
{
auto sc = reinterpret_cast<wsi::swapchain_base *>(bind_sc_info->swapchain);
TRY_LOG(sc->is_bind_allowed(bind_sc_info->imageIndex),
"Bind is not allowed on images that haven't been acquired first.");
result = sc->bind_swapchain_image(device, &pBindInfos[i], bind_sc_info);
result = sc->bind_swapchain_image(&pBindInfos[i], bind_sc_info);
error_message = "Failed to bind an image to the swapchain";
}

View file

@ -32,7 +32,7 @@
#include <util/log.hpp>
namespace wsi
namespace util
{
wsi_ext_maintainer::wsi_ext_maintainer(const util::allocator &allocator)
@ -62,4 +62,4 @@ bool wsi_ext_maintainer::add_extension(util::unique_ptr<wsi_ext> extension)
return false;
}
} /* namespace wsi */
} /* namespace util */

View file

@ -34,7 +34,7 @@
#include "util/custom_allocator.hpp"
namespace wsi
namespace util
{
#define WSI_DEFINE_EXTENSION(x) \
@ -144,4 +144,4 @@ public:
*/
bool add_extension(util::unique_ptr<wsi_ext> extension);
};
} /* namespace wsi */
} /* namespace util */

View file

@ -28,11 +28,10 @@
* @brief Contains the class implementation for a display swapchain.
*/
#include <vulkan/vk_icd.h>
#include <vulkan/vulkan.h>
#include <errno.h>
#include <util/macros.hpp>
#include <wsi/extensions/external_memory_extension.hpp>
#include <wsi/extensions/image_compression_control.hpp>
#include <wsi/extensions/present_id.hpp>
#include <wsi/swapchain_base.hpp>
@ -40,22 +39,45 @@
#include "swapchain.hpp"
#include "present_wait_display.hpp"
#include <wsi/swapchain_image_create_extensions/external_memory_extension.hpp>
namespace wsi
{
namespace display
{
class display_image_data : public swapchain_image_data
{
public:
display_image_data(int drm_fd, uint32_t fb_id)
: m_drm_fd(drm_fd)
, m_fb_id(fb_id)
{
assert(drm_fd != -1);
}
~display_image_data()
{
int result = drmModeRmFB(m_drm_fd, m_fb_id);
assert(result == 0);
UNUSED(result);
}
uint32_t get_fb_id()
{
return m_fb_id;
}
private:
uint32_t m_drm_fd;
uint32_t m_fb_id;
};
swapchain::swapchain(layer::device_private_data &dev_data, const VkAllocationCallbacks *pAllocator,
surface &wsi_surface)
: wsi::swapchain_base(dev_data, pAllocator)
, m_wsi_allocator(nullptr)
, m_display_mode(wsi_surface.get_display_mode())
, m_image_creation_parameters({}, m_allocator, {}, {})
, m_image_factory(m_allocator, m_device_data)
{
m_image_create_info.format = VK_FORMAT_UNDEFINED;
}
swapchain::~swapchain()
@ -74,21 +96,9 @@ static void page_flip_event(int fd, unsigned int sequence, unsigned int tv_sec,
*done = true;
}
uint64_t swapchain::get_modifier()
{
return m_image_creation_parameters.m_allocated_format.modifier;
}
VkResult swapchain::add_required_extensions(VkDevice device, const VkSwapchainCreateInfoKHR *swapchain_create_info)
{
auto compression_control = wsi_ext_image_compression_control::create(device, swapchain_create_info);
if (compression_control)
{
if (!add_swapchain_extension(m_allocator.make_unique<wsi_ext_image_compression_control>(*compression_control)))
{
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
}
UNUSED(device);
if (m_device_data.is_present_id_enabled() ||
(swapchain_create_info->flags & VK_SWAPCHAIN_CREATE_PRESENT_ID_2_BIT_KHR))
@ -149,214 +159,65 @@ VkResult swapchain::init_platform(VkDevice device, const VkSwapchainCreateInfoKH
return VK_ERROR_INITIALIZATION_FAILED;
}
return VK_SUCCESS;
return init_image_factory(*swapchain_create_info);
}
VkResult swapchain::bind_swapchain_image(VkDevice &device, const VkBindImageMemoryInfo *bind_image_mem_info,
const VkBindImageMemorySwapchainInfoKHR *bind_sc_info)
VkResult swapchain::init_image_factory(const VkSwapchainCreateInfoKHR &swapchain_create_info)
{
UNUSED(device);
const wsi::swapchain_image &swapchain_image = m_swapchain_images[bind_sc_info->imageIndex];
auto image_data = reinterpret_cast<display_image_data *>(swapchain_image.data);
return image_data->external_mem.bind_swapchain_image_memory(bind_image_mem_info->image);
}
VkResult swapchain::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_device_data.physical_device, info.format, drm_format_props),
"Failed to get format properties");
auto &display = drm_display::get_display();
if (!display.has_value())
{
WSI_LOG_ERROR("DRM display not available.");
return VK_ERROR_OUT_OF_HOST_MEMORY;
return VK_ERROR_INITIALIZATION_FAILED;
}
for (const auto &prop : drm_format_props)
std::variant<VkResult, util::unique_ptr<vulkan_image_handle_creator>> image_handle_creator_result =
create_image_creator(swapchain_create_info);
if (auto error = std::get_if<VkResult>(&image_handle_creator_result))
{
util::drm::drm_format_pair drm_format{ util::drm::vk_to_drm_format(info.format), prop.drmFormatModifier };
if (!display->is_format_supported(drm_format))
{
continue;
return *error;
}
VkExternalImageFormatPropertiesKHR external_props = {};
external_props.sType = VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES_KHR;
auto image_handle_creator =
std::get<util::unique_ptr<vulkan_image_handle_creator>>(std::move(image_handle_creator_result));
VkImageFormatProperties2KHR format_props = {};
format_props.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHR;
format_props.pNext = &external_props;
VkResult result = VK_SUCCESS;
auto compression_control = image_create_compression_control::create(m_device, &swapchain_create_info);
auto sc_img_create_ext_mem_result = swapchain_image_create_external_memory::create(
image_handle_creator->get_image_create_info(), compression_control, *m_wsi_allocator,
*display->get_supported_formats(), m_device_data.physical_device, m_allocator);
if (auto error = std::get_if<VkResult>(&sc_img_create_ext_mem_result))
{
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_device_data.is_swapchain_compression_control_enabled())
{
auto *ext = get_swapchain_extension<wsi_ext_image_compression_control>();
/* For Image compression control, additional requirements to be satisfied such as
existence of VkImageCompressionControlEXT in swaphain_create_info for
the ext to be added to the list. so we check whether we got a valid pointer
and proceed if yes. */
if (ext)
{
compression_control = ext->get_compression_control_properties();
compression_control.pNext = image_info.pNext;
image_info.pNext = &compression_control;
}
}
result = m_device_data.instance_data.disp.GetPhysicalDeviceImageFormatProperties2KHR(
m_device_data.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;
return *error;
}
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;
}
}
auto sc_img_create_ext_mem =
std::get<util::unique_ptr<swapchain_image_create_external_memory>>(std::move(sc_img_create_ext_mem_result));
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;
}
}
}
auto external_image_create_info = sc_img_create_ext_mem->get_external_image_create_info();
TRY_LOG_CALL(image_handle_creator->add_extension(std::move(sc_img_create_ext_mem)));
wsialloc_create_info_args wsialloc_args = { external_image_create_info.selected_format,
external_image_create_info.flags, external_image_create_info.extent,
external_image_create_info.explicit_compression };
auto backing_memory_creator =
m_allocator.make_unique<external_image_backing_memory_creator>(m_device_data, *m_wsi_allocator, wsialloc_args);
m_image_factory.init(std::move(image_handle_creator), std::move(backing_memory_creator), true, true);
return VK_SUCCESS;
}
VkResult swapchain::allocate_wsialloc(VkImageCreateInfo &image_create_info, display_image_data *image_data,
util::vector<wsialloc_format> &importable_formats,
wsialloc_format *allocated_format, bool avoid_allocation)
{
bool enable_fixed_rate = false;
if (m_device_data.is_swapchain_compression_control_enabled())
{
auto *ext = get_swapchain_extension<wsi_ext_image_compression_control>();
/* For Image compression control, additional requirements to be satisfied such as
existence of VkImageCompressionControlEXT in swaphain_create_info for
the ext to be added to the list. so we check whether we got a valid pointer
and proceed if yes. */
if (ext)
{
if (ext->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, avoid_allocation };
wsialloc_allocate_result alloc_result = { {}, { 0 }, { 0 }, { -1 }, false };
TRY(m_wsi_allocator->allocate(params, &alloc_result));
*allocated_format = alloc_result.format;
auto &external_memory = image_data->external_mem;
external_memory.set_strides(alloc_result.average_row_strides);
external_memory.set_buffer_fds(alloc_result.buffer_fds);
external_memory.set_offsets(alloc_result.offsets);
uint32_t num_planes = util::drm::drm_fourcc_format_get_num_planes(alloc_result.format.fourcc);
if (!avoid_allocation)
{
uint32_t num_memory_planes = 0;
for (uint32_t i = 0; i < num_planes; ++i)
{
auto it = std::find(std::begin(alloc_result.buffer_fds) + i + 1, std::end(alloc_result.buffer_fds),
alloc_result.buffer_fds[i]);
if (it == std::end(alloc_result.buffer_fds))
{
num_memory_planes++;
}
}
assert(alloc_result.is_disjoint == (num_memory_planes > 1));
external_memory.set_num_memories(num_memory_planes);
}
external_memory.set_format_info(alloc_result.is_disjoint, num_planes);
external_memory.set_memory_handle_type(VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_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));
auto &m_allocated_format = m_image_creation_parameters.m_allocated_format;
if (!importable_formats.try_push_back(m_allocated_format))
{
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
TRY_LOG_CALL(allocate_wsialloc(m_image_create_info, image_data, importable_formats, &m_allocated_format, false));
return VK_SUCCESS;
}
VkResult swapchain::create_framebuffer(const VkImageCreateInfo &image_create_info, display_image_data *image_data)
VkResult swapchain::create_framebuffer(image_backing_memory_external &image_external_memory, uint32_t &out_fb_id)
{
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 util::drm::drm_format_pair allocated_format{ m_image_creation_parameters.m_allocated_format.fourcc,
m_image_creation_parameters.m_allocated_format.modifier };
wsialloc_create_info_args img_create_info = image_external_memory.get_image_create_info();
external_memory &ext_memory = image_external_memory.get_external_memory();
const util::drm::drm_format_pair allocated_format{ img_create_info.selected_format.fourcc,
img_create_info.selected_format.modifier };
auto &display = drm_display::get_display();
if (!display.has_value())
@ -366,12 +227,12 @@ VkResult swapchain::create_framebuffer(const VkImageCreateInfo &image_create_inf
drm_gem_handle_array<util::MAX_PLANES> buffer_handles{ display->get_drm_fd() };
const auto &buffer_fds = image_data->external_mem.get_buffer_fds();
const auto &buffer_fds = ext_memory.get_buffer_fds();
for (uint32_t plane = 0; plane < image_data->external_mem.get_num_planes(); plane++)
for (uint32_t plane = 0; plane < ext_memory.get_num_planes(); plane++)
{
assert(image_data->external_mem.get_strides()[plane] > 0);
strides[plane] = image_data->external_mem.get_strides()[plane];
assert(ext_memory.get_strides()[plane] > 0);
strides[plane] = ext_memory.get_strides()[plane];
modifiers[plane] = allocated_format.modifier;
if (drmPrimeFDToHandle(display->get_drm_fd(), buffer_fds[plane], &buffer_handles[plane]) != 0)
{
@ -389,16 +250,16 @@ VkResult swapchain::create_framebuffer(const VkImageCreateInfo &image_create_inf
int error = 0;
if (display->supports_fb_modifiers())
{
error = drmModeAddFB2WithModifiers(
display->get_drm_fd(), image_create_info.extent.width, image_create_info.extent.height,
allocated_format.fourcc, buffer_handles.data(), strides.data(), image_data->external_mem.get_offsets().data(),
modifiers.data(), &image_data->fb_id, DRM_MODE_FB_MODIFIERS);
error = drmModeAddFB2WithModifiers(display->get_drm_fd(), img_create_info.extent.width,
img_create_info.extent.height, allocated_format.fourcc, buffer_handles.data(),
strides.data(), ext_memory.get_offsets().data(), modifiers.data(), &out_fb_id,
DRM_MODE_FB_MODIFIERS);
}
else
{
error = drmModeAddFB2(display->get_drm_fd(), image_create_info.extent.width, image_create_info.extent.height,
error = drmModeAddFB2(display->get_drm_fd(), img_create_info.extent.width, img_create_info.extent.height,
allocated_format.fourcc, buffer_handles.data(), strides.data(),
image_data->external_mem.get_offsets().data(), &image_data->fb_id, 0);
ext_memory.get_offsets().data(), &out_fb_id, 0);
}
if (error != 0)
@ -410,7 +271,7 @@ VkResult swapchain::create_framebuffer(const VkImageCreateInfo &image_create_inf
return ret_code;
}
VkResult swapchain::allocate_and_bind_swapchain_image(VkImageCreateInfo image_create_info, swapchain_image &image)
VkResult swapchain::allocate_and_bind_swapchain_image(swapchain_image &image)
{
util::unique_lock<util::recursive_mutex> image_status_lock(m_image_status_mutex);
if (!image_status_lock)
@ -418,82 +279,41 @@ VkResult swapchain::allocate_and_bind_swapchain_image(VkImageCreateInfo image_cr
WSI_LOG_ERROR("Failed to acquire image status lock in allocate_and_bind_swapchain_image.");
return VK_ERROR_INITIALIZATION_FAILED;
}
image.status = swapchain_image::FREE;
assert(image.data != nullptr);
auto image_data = static_cast<display_image_data *>(image.data);
TRY_LOG(allocate_image(image_data), "Failed to allocate image");
image_status_lock.unlock();
TRY_LOG(create_framebuffer(image_create_info, image_data), "Failed to create framebuffer");
TRY_LOG(image_data->external_mem.import_memory_and_bind_swapchain_image(image.image),
"Failed to import memory and bind swapchain image");
/* Initialize presentation fence. */
auto present_fence = sync_fd_fence_sync::create(m_device_data);
if (!present_fence.has_value())
auto &display = drm_display::get_display();
if (!display.has_value())
{
return VK_ERROR_INITIALIZATION_FAILED;
}
auto &backing_memory = swapchain_image_factory::get_backing_memory_from_image<image_backing_memory_external>(image);
TRY_LOG_CALL(backing_memory.allocate());
uint32_t fb_id = 0;
TRY_LOG(create_framebuffer(backing_memory, fb_id), "Failed to create framebuffer");
TRY_LOG_CALL(backing_memory.import_and_bind(image.get_image()));
auto image_data = m_allocator.make_unique<display_image_data>(display->get_drm_fd(), fb_id);
if (image_data == nullptr)
{
int result = drmModeRmFB(display->get_drm_fd(), fb_id);
assert(result == 0);
UNUSED(result);
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
image_data->present_fence = std::move(present_fence.value());
image.set_data(std::move(image_data));
return VK_SUCCESS;
}
VkResult swapchain::create_swapchain_image(VkImageCreateInfo image_create_info, swapchain_image &image)
{
/* Create image_data */
auto image_data = m_allocator.create<display_image_data>(1, m_device, m_allocator);
if (image_data == nullptr)
{
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
image.data = image_data;
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. */
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;
}
wsialloc_format allocated_format = { .fourcc = 0, .modifier = 0, .flags = 0 };
TRY_LOG_CALL(allocate_wsialloc(image_create_info, image_data, importable_formats, &allocated_format, true));
for (auto &prop : drm_format_props)
{
if (prop.drmFormatModifier == allocated_format.modifier)
{
image_data->external_mem.set_num_memories(prop.drmFormatModifierPlaneCount);
}
}
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);
}
void swapchain::present_image(const pending_present_request &pending_present)
{
int drm_res = 0;
display_image_data *image_data =
reinterpret_cast<display_image_data *>(m_swapchain_images[pending_present.image_index].data);
auto &image = m_swapchain_images[pending_present.image_index];
auto image_data = image.get_data<display_image_data>();
const auto &display = drm_display::get_display();
if (!display.has_value())
{
@ -501,14 +321,15 @@ void swapchain::present_image(const pending_present_request &pending_present)
return;
}
int drm_res = 0;
if (m_first_present)
{
/* Now we can set the mode of the new swapchain. */
drmModeModeInfo modeInfo = m_display_mode->get_drm_mode();
uint32_t connector_id = display->get_connector_id();
drm_res = drmModeSetCrtc(display->get_drm_fd(), display->get_crtc_id(), image_data->fb_id, 0, 0, &connector_id, 1,
&modeInfo);
drm_res = drmModeSetCrtc(display->get_drm_fd(), display->get_crtc_id(), image_data->get_fb_id(), 0, 0,
&connector_id, 1, &modeInfo);
if (drm_res != 0)
{
@ -523,7 +344,7 @@ void swapchain::present_image(const pending_present_request &pending_present)
bool page_flip_complete = false;
drm_res = drmModePageFlip(display->get_drm_fd(), display->get_crtc_id(), image_data->fb_id,
drm_res = drmModePageFlip(display->get_drm_fd(), display->get_crtc_id(), image_data->get_fb_id(),
DRM_MODE_PAGE_FLIP_EVENT, (void *)&page_flip_complete);
if (drm_res != 0)
@ -578,7 +399,7 @@ void swapchain::present_image(const pending_present_request &pending_present)
{
for (uint32_t i = 0; i < m_swapchain_images.size(); ++i)
{
if (m_swapchain_images[i].status == swapchain_image::PRESENTED)
if (m_swapchain_images[i].get_status() == swapchain_image::PRESENTED)
{
presented_index = i;
break;
@ -588,7 +409,7 @@ void swapchain::present_image(const pending_present_request &pending_present)
assert(presented_index < m_swapchain_images.size());
}
/* The image is on screen, change the image status to PRESENTED. */
m_swapchain_images[pending_present.image_index].status = swapchain_image::PRESENTED;
m_swapchain_images[pending_present.image_index].set_status(swapchain_image::PRESENTED);
auto *ext = get_swapchain_extension<wsi_ext_present_id>();
if (ext != nullptr)
@ -605,83 +426,23 @@ void swapchain::present_image(const pending_present_request &pending_present)
return;
}
VkResult swapchain::image_set_present_payload(swapchain_image &image, VkQueue queue,
const queue_submit_semaphores &semaphores, const void *submission_pnext)
std::variant<VkResult, util::unique_ptr<vulkan_image_handle_creator>> swapchain::create_image_creator(
const VkSwapchainCreateInfoKHR &swapchain_create_info)
{
auto image_data = reinterpret_cast<display_image_data *>(image.data);
return image_data->present_fence.set_payload(queue, semaphores, submission_pnext);
}
UNUSED(swapchain_create_info);
VkResult swapchain::image_wait_present(swapchain_image &image, uint64_t timeout)
{
auto data = reinterpret_cast<display_image_data *>(image.data);
return data->present_fence.wait_payload(timeout);
}
void swapchain::destroy_image(swapchain_image &image)
{
util::unique_lock<util::recursive_mutex> image_status_lock(m_image_status_mutex);
if (!image_status_lock)
{
WSI_LOG_ERROR("Failed to acquire image status lock in destroy_image.");
abort();
}
if (image.status != swapchain_image::INVALID)
{
if (image.image != VK_NULL_HANDLE)
{
m_device_data.disp.DestroyImage(m_device, image.image, get_allocation_callbacks());
image.image = VK_NULL_HANDLE;
}
image.status = swapchain_image::INVALID;
}
image_status_lock.unlock();
if (image.data != nullptr)
{
auto image_data = reinterpret_cast<display_image_data *>(image.data);
auto &display = drm_display::get_display();
if (!display.has_value())
{
return;
}
if (image_data->fb_id != std::numeric_limits<uint32_t>::max())
{
int result = drmModeRmFB(display->get_drm_fd(), image_data->fb_id);
assert(result == 0);
UNUSED(result);
}
m_allocator.destroy(1, image_data);
image.data = nullptr;
}
}
VkResult swapchain::get_required_image_creator_extensions(
const VkSwapchainCreateInfoKHR &swapchain_create_info,
util::vector<util::unique_ptr<swapchain_image_create_info_extension>> *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)))
auto image_handle_creator = m_allocator.make_unique<vulkan_image_handle_creator>(m_allocator, swapchain_create_info);
if (image_handle_creator == nullptr)
{
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
return VK_SUCCESS;
return image_handle_creator;
}
swapchain_image_factory &swapchain::get_image_factory()
{
return m_image_factory;
}
} /* namespace display */

View file

@ -30,14 +30,16 @@
#pragma once
extern "C" {
#include <vulkan/vk_icd.h>
#include <vulkan/vulkan.h>
}
#include "drm_display.hpp"
#include "surface.hpp"
#include <util/wsialloc/wsialloc.h>
#include <wsi/external_memory.hpp>
#include <wsi/image_backing_memory_external.hpp>
#include <wsi/wsi_alloc_utils.hpp>
namespace wsi
@ -46,37 +48,6 @@ namespace wsi
namespace display
{
struct display_image_data
{
display_image_data(const VkDevice &device, const util::allocator &allocator)
: external_mem(device, allocator)
, fb_id(std::numeric_limits<uint32_t>::max())
{
}
external_memory external_mem;
uint32_t fb_id;
sync_fd_fence_sync present_fence;
};
struct image_creation_parameters
{
wsialloc_format m_allocated_format;
util::vector<VkSubresourceLayout> m_image_layout;
VkExternalMemoryImageCreateInfoKHR m_external_info;
VkImageDrmFormatModifierExplicitCreateInfoEXT m_drm_mod_info;
image_creation_parameters(wsialloc_format allocated_format, util::allocator allocator,
VkExternalMemoryImageCreateInfoKHR external_info,
VkImageDrmFormatModifierExplicitCreateInfoEXT drm_mod_info)
: m_allocated_format(allocated_format)
, m_image_layout(allocator)
, m_external_info(external_info)
, m_drm_mod_info(drm_mod_info)
{
}
};
/**
* @brief Display swapchain class.
*/
@ -87,15 +58,14 @@ public:
virtual ~swapchain();
virtual VkResult init_platform(VkDevice device, const VkSwapchainCreateInfoKHR *swapchain_create_info,
bool &use_presentation_thread) override;
virtual VkResult bind_swapchain_image(VkDevice &device, const VkBindImageMemoryInfo *bind_image_mem_info,
const VkBindImageMemorySwapchainInfoKHR *bind_sc_info) override;
VkResult allocate_and_bind_swapchain_image(VkImageCreateInfo image_create_info, swapchain_image &image) override;
virtual VkResult create_swapchain_image(VkImageCreateInfo image_create_info, swapchain_image &image) override;
/**
* @brief Allocates and binds a new swapchain image.
*
* @param swapchain_image Swapchain image.
*
* @return Returns VK_SUCCESS on success, otherwise an appropriate error code.
*/
VkResult allocate_and_bind_swapchain_image(swapchain_image &image) override;
/**
* @brief Method to present and image
@ -106,40 +76,45 @@ public:
*/
void present_image(const pending_present_request &pending_present) override;
virtual VkResult image_set_present_payload(swapchain_image &image, VkQueue queue,
const queue_submit_semaphores &semaphores,
const void *submission_pnext) override;
virtual VkResult image_wait_present(swapchain_image &image, uint64_t timeout) override;
void destroy_image(swapchain_image &image) override;
protected:
/**
* @brief Get backend specific image create info extensions.
* @brief Get the image factory used for creating swapchain images.
*
* @param swapchain_create_info Swapchain create info.
* @param[out] extensions Backend specific swapchain image create info extensions.
* @return Swapchain image factory.
*/
VkResult get_required_image_creator_extensions(
const VkSwapchainCreateInfoKHR &swapchain_create_info,
util::vector<util::unique_ptr<swapchain_image_create_info_extension>> *extensions) override;
uint64_t get_modifier() override;
swapchain_image_factory &get_image_factory() override;
private:
VkResult allocate_image(display_image_data *image_data);
/**
* @brief Platform specific initialization
*
* @param device VkDevice object.
* @param swapchain_create_info Pointer to the swapchain create info struct.
* @param[out] use_presentation_thread Flag indicating if image presentation
* must happen in a separate thread.
*
* @return VK_SUCCESS on success or an error code otherwise.
*/
virtual VkResult init_platform(VkDevice device, const VkSwapchainCreateInfoKHR *swapchain_create_info,
bool &use_presentation_thread) override;
VkResult allocate_wsialloc(VkImageCreateInfo &image_create_info, display_image_data *image_data,
util::vector<wsialloc_format> &importable_formats, wsialloc_format *allocated_format,
bool avoid_allocation);
/**
* @brief Initalize backend specific image factory.
*
* @param swapchain_create_info Swapchain create info.
* @param image_factory Image factory to initalize.
* @return Vulkan result code.
*/
VkResult init_image_factory(const VkSwapchainCreateInfoKHR &swapchain_create_info);
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);
VkResult create_framebuffer(const VkImageCreateInfo &image_create_info, display_image_data *image_data);
/**
* @brief Create a framebuffer for display
*
* @param image_external_memory Image external memory
* @param out_fb_id The framebuffer ID will be written here
* @return Vulkan result code
*/
VkResult create_framebuffer(image_backing_memory_external &image_external_memory, uint32_t &out_fb_id);
/**
* @brief Adds required extensions to the extension list of the swapchain
@ -150,10 +125,26 @@ private:
*/
VkResult add_required_extensions(VkDevice device, const VkSwapchainCreateInfoKHR *swapchain_create_info) override;
util::unique_ptr<swapchain_wsialloc_allocator> m_wsi_allocator;
/**
* @brief Create the image creator with required extensions.
*
* @param swapchain_create_info VkSwapchainCreateInfoKHR passed by the application.
* @return If error occurred, returns VkResult, vulkan_image_handle_creator handle otherwise.
*/
std::variant<VkResult, util::unique_ptr<vulkan_image_handle_creator>> create_image_creator(
const VkSwapchainCreateInfoKHR &swapchain_create_info);
drm_display_mode *m_display_mode;
image_creation_parameters m_image_creation_parameters;
/**
* @brief WSIAllocator instance.
*/
util::unique_ptr<swapchain_wsialloc_allocator> m_wsi_allocator;
/**
* @brief Image factory that is used to create swapchain images.
*/
swapchain_image_factory m_image_factory;
};
} /* namespace display */

View file

@ -0,0 +1,343 @@
/*
* 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
{
/**
* @brief Finds what formats are compatible with the requested swapchain image Vulkan Device and backend surface.
*
* @param[in] info The Swapchain image creation info.
* @param[in] physical_device Vulkan physical device.
* @param[in] compression Optional compression control parameters for the swapchain image.
* @param[in] surface_formats Supported surface formats from the presentation engine.
* @param[out] importable_formats A list of formats that can be imported to the Vulkan Device.
* @param[out] exportable_formats A list of formats that can be exported from the Vulkan Device.
* @param[out] drm_format_props A list of all device supported VkDrmFormatModifierPropertiesEXT.
*
* @return VK_SUCCESS for success, otherwise other error code.
*/
VkResult get_surface_compatible_formats(const VkImageCreateInfo &image_create_info, VkPhysicalDevice physical_device,
std::optional<image_create_compression_control> &compression,
const util::vector<util::drm::drm_format_pair> &surface_formats,
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(physical_device, image_create_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(image_create_info.format),
prop.drmFormatModifier };
for (const auto &format : surface_formats)
{
is_supported = false;
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 = image_create_info.sharingMode;
drm_mod_info.queueFamilyIndexCount = image_create_info.queueFamilyIndexCount;
drm_mod_info.pQueueFamilyIndices = image_create_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 = image_create_info.format;
image_info.type = image_create_info.imageType;
image_info.tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT;
image_info.usage = image_create_info.usage;
image_info.flags = image_create_info.flags;
/* Attach view format list (if any) to the image_info chain, as required by the spec. */
const auto *image_format_list = util::find_extension<VkImageFormatListCreateInfo>(
VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO, 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 (compression.has_value())
{
compression_control = compression->get_compression_control_properties();
compression_control.pNext = image_info.pNext;
image_info.pNext = &compression_control;
}
auto &instance_data = layer::instance_private_data::get(physical_device);
result =
instance_data.disp.GetPhysicalDeviceImageFormatProperties2KHR(physical_device, &image_info, &format_props);
}
if (result != VK_SUCCESS)
{
continue;
}
if (format_props.imageFormatProperties.maxExtent.width < image_create_info.extent.width ||
format_props.imageFormatProperties.maxExtent.height < image_create_info.extent.height ||
format_props.imageFormatProperties.maxExtent.depth < image_create_info.extent.depth)
{
continue;
}
if (format_props.imageFormatProperties.maxMipLevels < image_create_info.mipLevels ||
format_props.imageFormatProperties.maxArrayLayers < image_create_info.arrayLayers)
{
continue;
}
if ((format_props.imageFormatProperties.sampleCounts & image_create_info.samples) != image_create_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;
}
/**
* @brief Queries WSIAlloc for preferred format for allocation.
*
* @param image_create_info The Swapchain image creation info.
* @param compression Optional compression control parameters for the swapchain image.
* @param wsi_allocator WSIAlloc allocator.
* @param in_importable_formats A list of formats that can be imported to the Vulkan Device.
* @param out_plane_layouts Plane layouts description for each plane.
* @return VkResult in case of failure, wsialloc_allocate_result otherwise.
*/
std::variant<VkResult, wsialloc_allocate_result> query_wsialloc_preferred_format(
const VkImageCreateInfo &image_create_info, std::optional<image_create_compression_control> &compression,
swapchain_wsialloc_allocator &wsi_allocator, util::vector<wsialloc_format> &in_importable_formats,
util::vector<VkSubresourceLayout> &out_plane_layouts)
{
bool use_fixed_rate_compression = false;
if (compression)
{
if (compression->get_bitmask_for_image_compression_flags() & VK_IMAGE_COMPRESSION_FIXED_RATE_EXPLICIT_EXT)
{
use_fixed_rate_compression = true;
}
}
allocation_params params = { (image_create_info.flags & VK_IMAGE_CREATE_PROTECTED_BIT) != 0,
image_create_info.extent,
in_importable_formats.data(),
in_importable_formats.size(),
use_fixed_rate_compression,
true };
wsialloc_allocate_result alloc_result = { {}, { 0 }, { 0 }, { -1 }, false };
TRY(wsi_allocator.allocate(params, &alloc_result));
const uint32_t format_planes = util::drm::drm_fourcc_format_get_num_planes(alloc_result.format.fourcc);
if (!out_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);
out_plane_layouts[plane].offset = alloc_result.offsets[plane];
out_plane_layouts[plane].rowPitch = static_cast<uint32_t>(alloc_result.average_row_strides[plane]);
}
return alloc_result;
}
std::variant<VkResult, util::unique_ptr<swapchain_image_create_external_memory>> swapchain_image_create_external_memory::
create(const VkImageCreateInfo &image_create_info, std::optional<image_create_compression_control> compression,
swapchain_wsialloc_allocator &wsi_allocator, const util::vector<util::drm::drm_format_pair> &surface_formats,
VkPhysicalDevice physical_device, util::allocator allocator)
{
util::vector<wsialloc_format> importable_formats(util::allocator(allocator, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND));
util::vector<uint64_t> exportable_modifiers(util::allocator(allocator, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND));
/* Query supported modifiers. */
util::vector<VkDrmFormatModifierPropertiesEXT> drm_format_props(
util::allocator(allocator, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND));
TRY_LOG_CALL(get_surface_compatible_formats(image_create_info, physical_device, compression, surface_formats,
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;
}
util::vector<VkSubresourceLayout> plane_layouts(allocator);
auto wsialloc_preferred_format_result =
query_wsialloc_preferred_format(image_create_info, compression, wsi_allocator, importable_formats, plane_layouts);
if (auto error = std::get_if<VkResult>(&wsialloc_preferred_format_result))
{
return *error;
}
auto wsialloc_preferred_format = std::get<wsialloc_allocate_result>(wsialloc_preferred_format_result);
auto ext_memory = allocator.make_unique<swapchain_image_create_external_memory>(
image_create_info, compression, wsialloc_preferred_format, std::move(plane_layouts), drm_format_props);
if (ext_memory == nullptr)
{
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
return std::move(ext_memory);
}
swapchain_image_create_external_memory::swapchain_image_create_external_memory(
const VkImageCreateInfo &image_create_info, std::optional<image_create_compression_control> &compression,
wsialloc_allocate_result wsialloc_selected_format, util::vector<VkSubresourceLayout> plane_layouts,
const util::vector<VkDrmFormatModifierPropertiesEXT> &drm_format_props)
: m_wsialloc_selected_format(wsialloc_selected_format)
, m_create_flags()
, m_create_extent()
, m_use_fixed_rate_compression(false)
, m_prop_plane_count()
, m_plane_layouts(std::move(plane_layouts))
, m_drm_mod_info()
, m_external_info()
{
if (compression)
{
if (compression->get_bitmask_for_image_compression_flags() & VK_IMAGE_COMPRESSION_FIXED_RATE_EXPLICIT_EXT)
{
m_use_fixed_rate_compression = true;
}
}
for (auto &prop : drm_format_props)
{
if (prop.drmFormatModifier == wsialloc_selected_format.format.modifier)
{
m_prop_plane_count = prop.drmFormatModifierPlaneCount;
}
}
m_create_extent = image_create_info.extent;
m_create_flags = image_create_info.flags;
if (m_wsialloc_selected_format.is_disjoint)
{
m_create_flags |= VK_IMAGE_CREATE_DISJOINT_BIT;
}
}
VkResult swapchain_image_create_external_memory::extend_image_create_info(VkImageCreateInfo *image_create_info)
{
assert(image_create_info != nullptr);
if (m_wsialloc_selected_format.is_disjoint)
{
image_create_info->flags |= VK_IMAGE_CREATE_DISJOINT_BIT;
}
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 = m_wsialloc_selected_format.format.modifier;
m_drm_mod_info.drmFormatModifierPlaneCount = m_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;
}
external_image_create_info swapchain_image_create_external_memory::get_external_image_create_info() const
{
assert(m_wsialloc_selected_format.format.fourcc != 0);
external_image_create_info image_info = { m_wsialloc_selected_format.format, m_create_flags, m_create_extent,
m_use_fixed_rate_compression };
return image_info;
}
} /* namespace wsi */

View file

@ -0,0 +1,120 @@
/*
* 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/image_backing_memory_external.hpp>
#include <wsi/wsi_alloc_utils.hpp>
#include "image_create_info_extension.hpp"
#include "image_compression_control.hpp"
namespace wsi
{
struct external_image_create_info
{
wsialloc_format selected_format;
VkImageCreateFlags flags;
VkExtent3D extent;
bool explicit_compression;
};
/**
* @brief Class used to extend VkImageCreateInfo for external memory handles.
*/
class swapchain_image_create_external_memory : public image_create_info_extension
{
public:
/**
* @brief Construct a new swapchain image create external memory::swapchain image create external memory object
*
* @param image_create_info The image creation info.
* @param compression Optional compression control parameters for the swapchain image.
* @param wsialloc_selected_format Selected format for allocation by WSIAlloc.
* @param plane_layouts Plane layouts description for each plane.
* @param drm_format_props A list of all device supported VkDrmFormatModifierPropertiesEXT.
*/
swapchain_image_create_external_memory(const VkImageCreateInfo &image_create_info,
std::optional<image_create_compression_control> &compression,
wsialloc_allocate_result wsialloc_selected_format,
util::vector<VkSubresourceLayout> plane_layouts,
const util::vector<VkDrmFormatModifierPropertiesEXT> &drm_format_props);
/**
* @brief Create swapchain_image_create_external_memory object.
*
* @param image_create_info The image creation info.
* @param compression Optional compression control parameters for the swapchain image.
* @param wsi_allocator WSIAlloc allocator.
* @param surface_formats Supported surface formats from the presentation engine.
* @param physical_device Vulkan physical device handle.
* @param allocator User provided allocation callbacks.
* @return VkResult in case of error, util::unique_ptr<swapchain_image_create_external_memory> otherwise.
*/
static std::variant<VkResult, util::unique_ptr<swapchain_image_create_external_memory>> create(
const VkImageCreateInfo &image_create_info, std::optional<image_create_compression_control> compression,
swapchain_wsialloc_allocator &wsi_allocator, const util::vector<util::drm::drm_format_pair> &surface_formats,
VkPhysicalDevice physical_device, util::allocator allocator);
/**
* @brief Extend VkImageCreateInfo
*
* @param image_create_info VkImageCreateInfo structure to extend.
* @return VkResult error code.
*/
VkResult extend_image_create_info(VkImageCreateInfo *image_create_info) override;
/**
* @brief Get the external image create information.
*
* @return external_image_create_info structure.
*/
external_image_create_info get_external_image_create_info() const;
private:
wsialloc_allocate_result m_wsialloc_selected_format;
VkImageCreateFlags m_create_flags;
VkExtent3D m_create_extent;
bool m_use_fixed_rate_compression;
uint32_t m_prop_plane_count;
util::vector<VkSubresourceLayout> m_plane_layouts;
VkImageDrmFormatModifierExplicitCreateInfoEXT m_drm_mod_info;
VkExternalMemoryImageCreateInfoKHR m_external_info;
};
} /* namespace wsi */

View file

@ -35,11 +35,10 @@
#include <util/custom_allocator.hpp>
#include <util/macros.hpp>
#include <util/wsi_extension.hpp>
#include <optional>
#include "wsi_extension.hpp"
namespace wsi
{
@ -49,7 +48,7 @@ namespace wsi
* This class defines the frame boundary extension
* features.
*/
class wsi_ext_frame_boundary : public wsi_ext
class wsi_ext_frame_boundary : public util::wsi_ext
{
public:
/**

View file

@ -36,7 +36,7 @@
namespace wsi
{
wsi_ext_image_compression_control::wsi_ext_image_compression_control(const VkImageCompressionControlEXT &extension)
image_create_compression_control::image_create_compression_control(const VkImageCompressionControlEXT &extension)
: m_compression_control{ VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT, nullptr, extension.flags,
extension.compressionControlPlaneCount, m_array_fixed_rate_flags }
{
@ -46,22 +46,22 @@ wsi_ext_image_compression_control::wsi_ext_image_compression_control(const VkIma
}
}
wsi_ext_image_compression_control::wsi_ext_image_compression_control(const wsi_ext_image_compression_control &extension)
: wsi_ext_image_compression_control(extension.m_compression_control)
image_create_compression_control::image_create_compression_control(const image_create_compression_control &extension)
: image_create_compression_control(extension.m_compression_control)
{
}
VkImageCompressionControlEXT wsi_ext_image_compression_control::get_compression_control_properties()
VkImageCompressionControlEXT image_create_compression_control::get_compression_control_properties()
{
return m_compression_control;
}
VkImageCompressionFlagsEXT wsi_ext_image_compression_control::get_bitmask_for_image_compression_flags()
VkImageCompressionFlagsEXT image_create_compression_control::get_bitmask_for_image_compression_flags()
{
return m_compression_control.flags;
}
std::optional<wsi_ext_image_compression_control> wsi_ext_image_compression_control::create(
std::optional<image_create_compression_control> image_create_compression_control::create(
VkDevice device, const VkSwapchainCreateInfoKHR *swapchain_create_info)
{
const auto *image_compression_control = util::find_extension<VkImageCompressionControlEXT>(
@ -70,10 +70,20 @@ std::optional<wsi_ext_image_compression_control> wsi_ext_image_compression_contr
layer::device_private_data &device_data = layer::device_private_data::get(device);
if (device_data.is_swapchain_compression_control_enabled() && image_compression_control != nullptr)
{
return wsi_ext_image_compression_control{ *image_compression_control };
return image_create_compression_control{ *image_compression_control };
}
return std::nullopt;
}
VkResult image_create_compression_control::extend_image_create_info(VkImageCreateInfo *image_create_info)
{
assert(image_create_info != nullptr);
m_compression_control.pNext = image_create_info->pNext;
image_create_info->pNext = &m_compression_control;
return VK_SUCCESS;
}
};

View file

@ -36,8 +36,9 @@
#include <util/custom_allocator.hpp>
#include <util/macros.hpp>
#include <util/wsi_extension.hpp>
#include "wsi_extension.hpp"
#include "image_create_info_extension.hpp"
namespace wsi
{
@ -50,31 +51,26 @@ using util::MAX_PLANES;
* Backends needing additional features will create its own local
* copy and inherit this class.
*/
class wsi_ext_image_compression_control : public wsi_ext
class image_create_compression_control : public image_create_info_extension
{
public:
/**
* @brief The name of the extension.
*/
WSI_DEFINE_EXTENSION(VK_EXT_IMAGE_COMPRESSION_CONTROL_EXTENSION_NAME);
/**
* @brief Constructor for the wsi_ext_image_compression_control class.
* @brief Constructor for the image_create_compression_control class.
*
* @param extension Reference to VkImageCompressionControlEXT structure.
*/
wsi_ext_image_compression_control(const VkImageCompressionControlEXT &extension);
image_create_compression_control(const VkImageCompressionControlEXT &extension);
wsi_ext_image_compression_control(const wsi_ext_image_compression_control &extension);
image_create_compression_control(const image_create_compression_control &extension);
wsi_ext_image_compression_control &operator=(const wsi_ext_image_compression_control &extension)
image_create_compression_control &operator=(const image_create_compression_control &extension)
{
if (this == &extension)
{
return *this;
}
auto compression_control = wsi_ext_image_compression_control(extension);
auto compression_control = image_create_compression_control(extension);
std::swap(m_compression_control, compression_control.m_compression_control);
for (uint32_t i = 0; i < compression_control.m_compression_control.compressionControlPlaneCount; i++)
{
@ -85,15 +81,15 @@ public:
}
/**
* @brief Create wsi_ext_image_compression_control class if deemed necessary.
* @brief Create image_create_compression_control class if deemed necessary.
*
* @param device The Vulkan device
* @param swapchain_create_info Swapchain create info
* @return Valid wsi_ext_image_compression_control if requested by application,
* @return Valid image_create_compression_control if requested by application,
* otherwise - an empty optional.
*/
static std::optional<wsi_ext_image_compression_control> create(
VkDevice device, const VkSwapchainCreateInfoKHR *swapchain_create_info);
static std::optional<image_create_compression_control> create(VkDevice device,
const VkSwapchainCreateInfoKHR *swapchain_create_info);
/**
* @brief This API is used to get the compression control properties of an image.
@ -109,6 +105,8 @@ public:
*/
VkImageCompressionFlagsEXT get_bitmask_for_image_compression_flags();
VkResult extend_image_create_info(VkImageCreateInfo *image_create_info) override;
private:
/**
* @brief Array to hold the pFixedRateFlags.

View file

@ -23,7 +23,7 @@
*/
/**
* @file swapchain_image_create_info_extension.hpp
* @file image_create_info_extension.hpp
*
* @brief Base class for swapchain image create info extensions.
*/
@ -31,11 +31,16 @@
#pragma once
#include <vulkan/vulkan.h>
#include <util/wsi_extension.hpp>
namespace wsi
{
class swapchain_image_create_info_extension
/**
* @brief This class should be used with the vulkan_image_handle_creator class
* to expand the image create properties that are passed to the ICD.
*/
class image_create_info_extension
{
public:
/**
@ -48,7 +53,7 @@ public:
*/
virtual VkResult extend_image_create_info(VkImageCreateInfo *image_create_info) = 0;
virtual ~swapchain_image_create_info_extension() = default;
virtual ~image_create_info_extension() = default;
};
} /* namespace wsi */

View file

@ -32,12 +32,12 @@
#include <vulkan/vulkan.h>
#include <util/custom_allocator.hpp>
#include "swapchain_image_create_info_extension.hpp"
#include "image_create_info_extension.hpp"
namespace wsi
{
class swapchain_image_create_mutable_format : public swapchain_image_create_info_extension
class swapchain_image_create_mutable_format : public image_create_info_extension
{
public:
swapchain_image_create_mutable_format() = delete;

View file

@ -30,14 +30,14 @@
#pragma once
#include <atomic>
#include <condition_variable>
#include <util/custom_allocator.hpp>
#include <util/custom_mutex.hpp>
#include <util/macros.hpp>
#include <util/log.hpp>
#include <atomic>
#include <condition_variable>
#include "wsi_extension.hpp"
#include <util/wsi_extension.hpp>
namespace wsi
{
@ -48,7 +48,7 @@ namespace wsi
* This class defines the present ID extension
* features.
*/
class wsi_ext_present_id : public wsi_ext
class wsi_ext_present_id : public util::wsi_ext
{
public:
/**

View file

@ -31,13 +31,6 @@
#pragma once
#include <layer/wsi_layer_experimental.hpp>
#include <layer/private_data.hpp>
#include <wsi/swapchain_base.hpp>
#include <util/custom_allocator.hpp>
#include <util/custom_mutex.hpp>
#include <util/macros.hpp>
#include <atomic>
#include <iterator>
#include <type_traits>
@ -48,7 +41,13 @@
#include <cassert>
#include <variant>
#include "wsi_extension.hpp"
#include <layer/wsi_layer_experimental.hpp>
#include <layer/private_data.hpp>
#include <util/custom_allocator.hpp>
#include <util/custom_mutex.hpp>
#include <util/macros.hpp>
#include <util/wsi_extension.hpp>
#include <wsi/swapchain_base.hpp>
#if VULKAN_WSI_LAYER_EXPERIMENTAL
namespace wsi
@ -424,7 +423,7 @@ struct scheduled_present_target
* This class implements or act as a base class for the present timing extension
* features.
*/
class wsi_ext_present_timing : public wsi_ext
class wsi_ext_present_timing : public util::wsi_ext
{
public:
/**

View file

@ -30,9 +30,7 @@
#pragma once
#include "wsi_extension.hpp"
#include "present_id.hpp"
#include <vulkan/vulkan.h>
namespace wsi
@ -44,7 +42,7 @@ namespace wsi
* This class defines the present wait extension
* implementation.
*/
class wsi_ext_present_wait : public wsi_ext
class wsi_ext_present_wait : public util::wsi_ext
{
public:
/**

View file

@ -31,8 +31,7 @@
#include <layer/private_data.hpp>
#include <util/custom_allocator.hpp>
#include "wsi_extension.hpp"
#include <util/wsi_extension.hpp>
namespace wsi
{
@ -43,7 +42,7 @@ namespace wsi
* This class defines the VK_EXT_swapchain_maintenance1 extension
* features.
*/
class wsi_ext_swapchain_maintenance1 : public wsi_ext
class wsi_ext_swapchain_maintenance1 : public util::wsi_ext
{
public:
/**

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022-2024 Arm Limited.
* Copyright (c) 2022-2025 Arm Limited.
*
* SPDX-License-Identifier: MIT
*
@ -36,7 +36,7 @@
namespace wsi
{
external_memory::external_memory(const VkDevice &device, const util::allocator &allocator)
external_memory::external_memory(const VkDevice &device, util::allocator allocator)
: m_device(device)
, m_allocator(allocator)
{

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022-2024 Arm Limited.
* Copyright (c) 2022-2025 Arm Limited.
*
* SPDX-License-Identifier: MIT
*
@ -47,7 +47,7 @@ using util::MAX_PLANES;
class external_memory
{
public:
external_memory(const VkDevice &device, const util::allocator &allocator);
external_memory(const VkDevice &device, util::allocator allocator);
~external_memory();
/**
@ -228,7 +228,7 @@ private:
uint32_t m_num_memories{ 0 };
VkExternalMemoryHandleTypeFlagBits m_handle_type{ VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT };
const VkDevice &m_device;
const util::allocator &m_allocator;
util::allocator m_allocator;
};
} // namespace wsi

View file

@ -35,16 +35,17 @@
#include <util/custom_allocator.hpp>
#include <util/timed_semaphore.hpp>
#include <util/macros.hpp>
#include <wsi/extensions/present_id.hpp>
#include <wsi/extensions/swapchain_maintenance.hpp>
#include "util/macros.hpp"
#include <wsi/extensions/image_compression_control.hpp>
#include <wsi/extensions/mutable_format_extension.hpp>
#include <wsi/image_backing_memory_device.hpp>
#include "present_timing_handler.hpp"
#include "present_wait_headless.hpp"
#include <wsi/swapchain_image_create_extensions/image_compression_control.hpp>
namespace wsi
{
namespace headless
@ -59,6 +60,7 @@ struct image_data
swapchain::swapchain(layer::device_private_data &dev_data, const VkAllocationCallbacks *pAllocator)
: wsi::swapchain_base(dev_data, pAllocator)
, m_image_factory(m_allocator, m_device_data)
{
}
@ -139,86 +141,36 @@ VkResult swapchain::init_platform(VkDevice device, const VkSwapchainCreateInfoKH
use_presentation_thread = true;
}
return init_image_factory(*swapchain_create_info);
}
VkResult swapchain::init_image_factory(const VkSwapchainCreateInfoKHR &swapchain_create_info)
{
std::variant<VkResult, util::unique_ptr<vulkan_image_handle_creator>> image_handle_creator_result =
create_image_creator(swapchain_create_info);
if (auto error = std::get_if<VkResult>(&image_handle_creator_result))
{
return *error;
}
auto image_handle_creator =
std::get<util::unique_ptr<vulkan_image_handle_creator>>(std::move(image_handle_creator_result));
auto backing_memory_creator = m_allocator.make_unique<device_backing_memory_creator>(m_device_data);
m_image_factory.init(std::move(image_handle_creator), std::move(backing_memory_creator), false, true);
return VK_SUCCESS;
}
uint64_t swapchain::get_modifier()
VkResult swapchain::allocate_and_bind_swapchain_image(swapchain_image &image)
{
return 0;
}
VkResult swapchain::allocate_and_bind_swapchain_image(VkImageCreateInfo image_create, swapchain_image &image)
{
UNUSED(image_create);
VkResult res = VK_SUCCESS;
const util::unique_lock<util::recursive_mutex> lock(m_image_status_mutex);
if (!lock)
{
return VK_ERROR_INITIALIZATION_FAILED;
}
VkMemoryRequirements memory_requirements = {};
m_device_data.disp.GetImageMemoryRequirements(m_device, image.image, &memory_requirements);
/* Find a memory type */
size_t mem_type_idx = 0;
for (; mem_type_idx < 8 * sizeof(memory_requirements.memoryTypeBits); ++mem_type_idx)
{
if (memory_requirements.memoryTypeBits & (1u << mem_type_idx))
{
break;
}
}
assert(mem_type_idx <= 8 * sizeof(memory_requirements.memoryTypeBits) - 1);
VkMemoryAllocateInfo mem_info = {};
mem_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
mem_info.allocationSize = memory_requirements.size;
mem_info.memoryTypeIndex = mem_type_idx;
image_data *data = nullptr;
/* Create image_data */
data = m_allocator.create<image_data>(1);
if (data == nullptr)
{
m_device_data.disp.DestroyImage(m_device, image.image, get_allocation_callbacks());
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
image.data = reinterpret_cast<void *>(data);
image.status = wsi::swapchain_image::FREE;
res = m_device_data.disp.AllocateMemory(m_device, &mem_info, get_allocation_callbacks(), &data->memory);
assert(VK_SUCCESS == res);
if (res != VK_SUCCESS)
{
destroy_image(image);
return res;
}
res = m_device_data.disp.BindImageMemory(m_device, image.image, data->memory, 0);
assert(VK_SUCCESS == res);
if (res != VK_SUCCESS)
{
destroy_image(image);
return res;
}
/* Initialize presentation fence. */
auto present_fence = fence_sync::create(m_device_data);
if (!present_fence.has_value())
{
destroy_image(image);
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
data->present_fence = std::move(present_fence.value());
return res;
}
VkResult swapchain::create_swapchain_image(VkImageCreateInfo image_create_info, swapchain_image &image)
{
return m_device_data.disp.CreateImage(m_device, &image_create_info, get_allocation_callbacks(), &image.image);
auto &backing_memory = swapchain_image_factory::get_backing_memory_from_image<image_backing_memory_device>(image);
return backing_memory.allocate_and_bind(image.get_image());
}
void swapchain::present_image(const pending_present_request &pending_present)
@ -296,82 +248,45 @@ void swapchain::present_image(const pending_present_request &pending_present)
unpresent_image(pending_present.image_index);
}
void swapchain::destroy_image(wsi::swapchain_image &image)
std::variant<VkResult, util::unique_ptr<vulkan_image_handle_creator>> swapchain::create_image_creator(
const VkSwapchainCreateInfoKHR &swapchain_create_info)
{
util::unique_lock<util::recursive_mutex> image_status_lock(m_image_status_mutex);
if (!image_status_lock)
{
WSI_LOG_ERROR("Failed to acquire image status lock in destroy_image.");
abort();
}
if (image.status != wsi::swapchain_image::INVALID)
{
if (image.image != VK_NULL_HANDLE)
{
m_device_data.disp.DestroyImage(m_device, image.image, get_allocation_callbacks());
image.image = VK_NULL_HANDLE;
}
image.status = wsi::swapchain_image::INVALID;
}
image_status_lock.unlock();
if (image.data != nullptr)
{
auto *data = reinterpret_cast<image_data *>(image.data);
if (data->memory != VK_NULL_HANDLE)
{
m_device_data.disp.FreeMemory(m_device, data->memory, get_allocation_callbacks());
data->memory = VK_NULL_HANDLE;
}
m_allocator.destroy(1, data);
image.data = nullptr;
}
}
VkResult swapchain::image_set_present_payload(swapchain_image &image, VkQueue queue,
const queue_submit_semaphores &semaphores, const void *submission_pnext)
{
auto data = reinterpret_cast<image_data *>(image.data);
return data->present_fence.set_payload(queue, semaphores, submission_pnext);
}
VkResult swapchain::image_wait_present(swapchain_image &image, uint64_t timeout)
{
auto data = reinterpret_cast<image_data *>(image.data);
return data->present_fence.wait_payload(timeout);
}
VkResult swapchain::bind_swapchain_image(VkDevice &device, const VkBindImageMemoryInfo *bind_image_mem_info,
const VkBindImageMemorySwapchainInfoKHR *bind_sc_info)
{
auto &device_data = layer::device_private_data::get(device);
const wsi::swapchain_image &swapchain_image = m_swapchain_images[bind_sc_info->imageIndex];
VkDeviceMemory memory = reinterpret_cast<image_data *>(swapchain_image.data)->memory;
return device_data.disp.BindImageMemory(device, bind_image_mem_info->image, memory, 0);
}
VkResult swapchain::get_required_image_creator_extensions(
const VkSwapchainCreateInfoKHR &swapchain_create_info,
util::vector<util::unique_ptr<swapchain_image_create_info_extension>> *extensions)
{
assert(extensions != nullptr);
auto compression_control = swapchain_image_create_compression_control::create(
m_device_data.is_swapchain_compression_control_enabled(), swapchain_create_info);
if (compression_control)
{
if (!extensions->try_push_back(
m_allocator.make_unique<swapchain_image_create_compression_control>(*compression_control)))
auto image_handle_creator = m_allocator.make_unique<vulkan_image_handle_creator>(m_allocator, swapchain_create_info);
if (image_handle_creator == nullptr)
{
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
auto compression_control = image_create_compression_control::create(m_device, &swapchain_create_info);
if (compression_control.has_value())
{
auto sc_compresson_control =
m_allocator.make_unique<image_create_compression_control>(compression_control.value());
if (sc_compresson_control == nullptr)
{
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
TRY_LOG_CALL(image_handle_creator->add_extension(std::move(sc_compresson_control)));
}
return VK_SUCCESS;
if (is_mutable_format_enabled())
{
const auto *image_format_list = util::find_extension<VkImageFormatListCreateInfo>(
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;
}
TRY_LOG_CALL(image_handle_creator->add_extension(std::move(mutable_format_uptr)));
}
return image_handle_creator;
}
swapchain_image_factory &swapchain::get_image_factory()
{
return m_image_factory;
}
} /* namespace headless */

View file

@ -30,8 +30,9 @@
#pragma once
extern "C" {
#include <vulkan/vk_icd.h>
#include <vulkan/vulkan.h>
}
#include <wsi/swapchain_base.hpp>
@ -59,27 +60,22 @@ protected:
VkResult init_platform(VkDevice device, const VkSwapchainCreateInfoKHR *swapchain_create_info,
bool &use_presentation_thread) override;
/**
* @brief Initalize backend specific image factory.
*
* @param swapchain_create_info Swapchain create info.
* @return Vulkan result code.
*/
VkResult init_image_factory(const VkSwapchainCreateInfoKHR &swapchain_create_info);
/**
* @brief Allocates and binds a new swapchain image.
*
* @param image_create_info Data to be used to create the image.
* @param image Handle to the image.
* @param swapchain_image Swapchain image.
*
* @return Returns VK_SUCCESS on success, otherwise an appropriate error code.
*/
VkResult allocate_and_bind_swapchain_image(VkImageCreateInfo image_create_info, swapchain_image &image) override;
/**
* @brief Creates a new swapchain image.
*
* @param image_create_info Data to be used to create the image.
* @param image Handle to the image.
*
* @return If image creation is successful returns VK_SUCCESS, otherwise
* will return VK_ERROR_OUT_OF_DEVICE_MEMORY or VK_ERROR_INITIALIZATION_FAILED
* depending on the error that occurred.
*/
VkResult create_swapchain_image(VkImageCreateInfo image_create_info, swapchain_image &image) override;
VkResult allocate_and_bind_swapchain_image(swapchain_image &image) override;
/**
* @brief Method to present and image
@ -91,51 +87,11 @@ protected:
void present_image(const pending_present_request &pending_present) override;
/**
* @brief Method to release a swapchain image
* @brief Get the image factory used for creating swapchain images.
*
* @param image Handle to the image about to be released.
* @return Swapchain image factory.
*/
void destroy_image(wsi::swapchain_image &image) override;
/**
* @brief Sets the present payload for a swapchain image.
*
* @param[in] image The swapchain image for which to set a present payload.
* @param queue A Vulkan queue that can be used for any Vulkan commands needed.
* @param[in] sem_payload Array of Vulkan semaphores that constitute the payload.
* @param[in] submission_pnext Chain of pointers to attach to the payload submission.
*
* @return VK_SUCCESS on success or an error code otherwise.
*/
VkResult image_set_present_payload(swapchain_image &image, VkQueue queue, const queue_submit_semaphores &semaphores,
const void *submission_pnext) override;
VkResult image_wait_present(swapchain_image &image, uint64_t timeout) override;
uint64_t get_modifier() override;
/**
* @brief Bind image to a swapchain
*
* @param device is the logical device that owns the images and memory.
* @param bind_image_mem_info details the image we want to bind.
* @param bind_sc_info describes the swapchain memory to bind to.
*
* @return VK_SUCCESS on success, otherwise on failure VK_ERROR_OUT_OF_HOST_MEMORY or VK_ERROR_OUT_OF_DEVICE_MEMORY
* can be returned.
*/
VkResult bind_swapchain_image(VkDevice &device, const VkBindImageMemoryInfo *bind_image_mem_info,
const VkBindImageMemorySwapchainInfoKHR *bind_sc_info) override;
/**
* @brief Get backend specific image create info extensions.
*
* @param swapchain_create_info Swapchain create info.
* @param[out] extensions Backend specific swapchain image create info extensions.
*/
VkResult get_required_image_creator_extensions(
const VkSwapchainCreateInfoKHR &swapchain_create_info,
util::vector<util::unique_ptr<swapchain_image_create_info_extension>> *extensions) override;
swapchain_image_factory &get_image_factory() override;
private:
/**
@ -146,6 +102,20 @@ private:
* @return VK_SUCCESS on success, other result codes on failure
*/
VkResult add_required_extensions(VkDevice device, const VkSwapchainCreateInfoKHR *swapchain_create_info) override;
/**
* @brief Create the image creator with required extensions.
*
* @param swapchain_create_info VkSwapchainCreateInfoKHR passed by the application.
* @return If error occurred, returns VkResult, vulkan_image_handle_creator handle otherwise.
*/
std::variant<VkResult, util::unique_ptr<vulkan_image_handle_creator>> create_image_creator(
const VkSwapchainCreateInfoKHR &swapchain_create_info);
/**
* @brief Image factory that is used to create swapchain images.
*/
swapchain_image_factory m_image_factory;
};
} /* namespace headless */

View file

@ -0,0 +1,95 @@
/*
* 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 image_backing_memory_device.cpp
*
* @brief Contains the implementatation for the image_backing_memory_device class.
*/
#include "image_backing_memory_device.hpp"
#include <util/helpers.hpp>
namespace wsi
{
image_backing_memory_device::image_backing_memory_device(const layer::device_private_data &device_data,
util::allocator allocator)
: m_device_data(device_data)
, m_allocator(allocator)
{
}
image_backing_memory_device::~image_backing_memory_device()
{
if (m_device_memory != VK_NULL_HANDLE)
{
m_device_data.disp.FreeMemory(m_device_data.device, m_device_memory, m_allocator.get_original_callbacks());
m_device_memory = VK_NULL_HANDLE;
}
}
VkResult image_backing_memory_device::allocate_and_bind(VkImage image)
{
const auto &disp_table = m_device_data.disp;
VkMemoryRequirements memory_requirements = {};
disp_table.GetImageMemoryRequirements(m_device_data.device, image, &memory_requirements);
/* Find a memory type */
size_t mem_type_idx = 0;
for (; mem_type_idx < 8 * sizeof(memory_requirements.memoryTypeBits); ++mem_type_idx)
{
if (memory_requirements.memoryTypeBits & (1u << mem_type_idx))
{
break;
}
}
assert(mem_type_idx <= 8 * sizeof(memory_requirements.memoryTypeBits) - 1);
VkMemoryAllocateInfo mem_info = {};
mem_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
mem_info.allocationSize = memory_requirements.size;
mem_info.memoryTypeIndex = mem_type_idx;
TRY(disp_table.AllocateMemory(m_device_data.device, &mem_info, m_allocator.get_original_callbacks(),
&m_device_memory));
TRY(disp_table.BindImageMemory(m_device_data.device, image, m_device_memory, 0));
return VK_SUCCESS;
}
VkResult image_backing_memory_device::bind(const VkBindImageMemoryInfo *bind_image_mem_info)
{
return m_device_data.disp.BindImageMemory(m_device_data.device, bind_image_mem_info->image, m_device_memory, 0);
}
uint64_t image_backing_memory_device::get_modifier() const
{
/* The driver choses the modifier, we have no control over it. */
return 0;
}
}

View file

@ -0,0 +1,98 @@
/*
* 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 image_backing_memory_device.hpp
*
* @brief Contains the class declaration for the image_backing_memory_device
* for swapchain device memory.
*/
#pragma once
#include <vulkan/vulkan.h>
#include <util/custom_allocator.hpp>
#include <wsi/swapchain_image.hpp>
#include <layer/private_data.hpp>
namespace wsi
{
class image_backing_memory_device : public image_backing_memory
{
public:
image_backing_memory_device(const layer::device_private_data &device_data, util::allocator allocator);
virtual ~image_backing_memory_device();
/**
* @brief Allocates memory and binds it to Vulkan image.
*
* @param image Vulkan image handle.
*
* @return Returns VK_SUCCESS on success, otherwise an appropriate error code.
*/
VkResult allocate_and_bind(VkImage image);
/**
* @brief Bind Vulkan image
*
* @param bind_image_mem_info Bind info
* @return VkResult VK_SUCCESS if successful, otherwise an error.
*/
VkResult bind(const VkBindImageMemoryInfo *bind_image_mem_info) override;
/**
* @brief Get the modifier used for the image
*
* @return uint64_t DRM format modifier
*/
uint64_t get_modifier() const override;
private:
VkDeviceMemory m_device_memory{ VK_NULL_HANDLE };
const layer::device_private_data &m_device_data;
const util::allocator m_allocator;
};
class device_backing_memory_creator : public image_backing_memory_creator
{
public:
device_backing_memory_creator(layer::device_private_data &device_data)
: m_device_data(device_data)
{
}
virtual ~device_backing_memory_creator() = default;
util::unique_ptr<image_backing_memory> create_image_backing_memory(util::allocator &allocator) override
{
return allocator.make_unique<image_backing_memory_device>(m_device_data, allocator);
}
private:
layer::device_private_data &m_device_data;
};
} // namespace wsi

View file

@ -0,0 +1,103 @@
/*
* 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
*
* @brief Contains the implementation for external memory management for swapchain images.
*/
#include "image_backing_memory_external.hpp"
using util::MAX_PLANES;
namespace wsi
{
VkResult image_backing_memory_external::allocate()
{
allocation_params params = { (m_create_info.flags & VK_IMAGE_CREATE_PROTECTED_BIT) != 0,
m_create_info.extent,
&m_create_info.selected_format,
1,
m_create_info.explicit_compression,
false };
wsialloc_allocate_result alloc_result = { {}, { 0 }, { 0 }, { -1 }, false };
TRY(m_wsialloc_allocator.allocate(params, &alloc_result));
m_external_mem.set_strides(alloc_result.average_row_strides);
m_external_mem.set_buffer_fds(alloc_result.buffer_fds);
m_external_mem.set_offsets(alloc_result.offsets);
uint32_t num_planes = util::drm::drm_fourcc_format_get_num_planes(alloc_result.format.fourcc);
uint32_t num_memory_planes = 0;
for (uint32_t i = 0; i < num_planes; ++i)
{
auto it = std::find(std::begin(alloc_result.buffer_fds) + i + 1, std::end(alloc_result.buffer_fds),
alloc_result.buffer_fds[i]);
if (it == std::end(alloc_result.buffer_fds))
{
num_memory_planes++;
}
}
assert(alloc_result.is_disjoint == (num_memory_planes > 1));
m_external_mem.set_num_memories(num_memory_planes);
m_external_mem.set_format_info(alloc_result.is_disjoint, num_planes);
m_external_mem.set_memory_handle_type(VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT);
return VK_SUCCESS;
}
VkResult image_backing_memory_external::import_and_bind(VkImage image)
{
TRY_LOG(m_external_mem.import_memory_and_bind_swapchain_image(image),
"Failed to import memory and bind swapchain image");
return VK_SUCCESS;
}
VkResult image_backing_memory_external::bind(const VkBindImageMemoryInfo *bind_image_mem_info)
{
return m_external_mem.bind_swapchain_image_memory(bind_image_mem_info->image);
}
external_memory &image_backing_memory_external::get_external_memory()
{
return m_external_mem;
}
wsialloc_create_info_args image_backing_memory_external::get_image_create_info()
{
return m_create_info;
}
uint64_t image_backing_memory_external::get_modifier() const
{
return m_create_info.selected_format.modifier;
}
}

View file

@ -0,0 +1,153 @@
/*
* 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
*
* @brief Contains the header definitoins for external memory management for swapchain images.
*/
#pragma once
#include <memory>
#include <array>
#include <vulkan/vulkan.h>
#include <layer/private_data.hpp>
#include <util/custom_allocator.hpp>
#include <util/drm/drm_utils.hpp>
#include <wsi/external_memory.hpp>
#include <wsi/swapchain_image.hpp>
#include <wsi/wsi_alloc_utils.hpp>
namespace wsi
{
struct wsialloc_create_info_args
{
wsialloc_format selected_format;
VkImageCreateFlags flags;
VkExtent3D extent;
bool explicit_compression;
};
class image_backing_memory_external : public image_backing_memory
{
public:
image_backing_memory_external(const layer::device_private_data *device_data,
swapchain_wsialloc_allocator &wsi_allocator, util::allocator allocator,
wsialloc_create_info_args create_info)
: m_device_data(device_data)
, m_external_mem(m_device_data->device, allocator)
, m_allocator(allocator)
, m_wsialloc_allocator(wsi_allocator)
, m_create_info(create_info)
{
}
virtual ~image_backing_memory_external() = default;
/**
* @brief Allocate external memory for the image.
*
* @return VK_SUCCESS if successful, otherwise an error.
*/
VkResult allocate();
/**
* @brief Import external memory and bind it to the image.
* Note: The backing memory must be allocated before calling this function.
* Note: The backing memory file descriptors will be released after this call.
* @param image Vulkan image to bind the external memory to.
* @return VkResult VK_SUCCESS if successful, otherwise an error.
*/
VkResult import_and_bind(VkImage image);
/**
* @brief Bind Vulkan image
*
* @param bind_image_mem_info Bind info
* @return VkResult VK_SUCCESS if successful, otherwise an error.
*/
VkResult bind(const VkBindImageMemoryInfo *bind_image_mem_info) override;
/**
* @brief Get the external memory that belongs to the swapchain image
*
* @return External memory that belongs to the swapchain image
*/
external_memory &get_external_memory();
/**
* @brief Get the image create information
*
* @return Image create information
*/
wsialloc_create_info_args get_image_create_info();
/**
* @brief Get the modifier used for the image
*
* @return uint64_t DRM format modifier
*/
uint64_t get_modifier() const override;
private:
const layer::device_private_data *m_device_data;
external_memory m_external_mem;
util::allocator m_allocator;
swapchain_wsialloc_allocator &m_wsialloc_allocator;
wsialloc_create_info_args m_create_info;
};
class external_image_backing_memory_creator : public image_backing_memory_creator
{
public:
external_image_backing_memory_creator(layer::device_private_data &device_data,
swapchain_wsialloc_allocator &wsi_allocator,
wsialloc_create_info_args create_info)
: m_device_data(device_data)
, m_wsialloc_allocator(wsi_allocator)
, m_create_info(create_info)
{
}
virtual ~external_image_backing_memory_creator() = default;
util::unique_ptr<image_backing_memory> create_image_backing_memory(util::allocator &allocator) override
{
return allocator.make_unique<image_backing_memory_external>(&m_device_data, m_wsialloc_allocator, allocator,
m_create_info);
}
private:
layer::device_private_data &m_device_data;
swapchain_wsialloc_allocator &m_wsialloc_allocator;
wsialloc_create_info_args m_create_info;
};
}

View file

@ -37,6 +37,7 @@
#include <cstdio>
#include <cstdlib>
#include <system_error>
#include <algorithm>
#include <unistd.h>
#include <vulkan/vulkan.h>
@ -45,7 +46,6 @@
#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"
@ -116,7 +116,7 @@ void swapchain_base::page_flip_thread()
}
/* We may need to wait for the payload of the present sync of the oldest pending image to be finished. */
while ((vk_res = image_wait_present(sc_images[submit_info.image_index], timeout)) == VK_TIMEOUT)
while ((vk_res = sc_images[submit_info.image_index].wait_present(timeout)) == VK_TIMEOUT)
{
WSI_LOG_WARNING("Timeout waiting for image's present fences, retrying..");
}
@ -202,11 +202,11 @@ void swapchain_base::unpresent_image(uint32_t presented_index)
if (m_present_mode == VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR ||
m_present_mode == VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR)
{
m_swapchain_images[presented_index].status = swapchain_image::ACQUIRED;
m_swapchain_images[presented_index].set_status(swapchain_image::ACQUIRED);
}
else
{
m_swapchain_images[presented_index].status = swapchain_image::FREE;
m_swapchain_images[presented_index].set_status(swapchain_image::FREE);
}
image_status_lock.unlock();
@ -234,12 +234,10 @@ swapchain_base::swapchain_base(layer::device_private_data &dev_data, const VkAll
, m_ancestor(VK_NULL_HANDLE)
, m_device(VK_NULL_HANDLE)
, m_queue(VK_NULL_HANDLE)
, m_image_create_info()
, m_image_acquire_lock()
, m_error_state(VK_NOT_READY)
, m_started_presenting(false)
, m_extensions(m_allocator)
, m_image_creator(m_allocator)
{
}
@ -269,12 +267,6 @@ VkResult swapchain_base::init(VkDevice device, const VkSwapchainCreateInfoKHR *s
TRY_LOG_CALL(ext->handle_scaling_create_info(device, swapchain_create_info, m_surface));
}
/* Init image to invalid values. */
if (!m_swapchain_images.try_resize(swapchain_create_info->minImageCount))
{
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. */
@ -283,7 +275,6 @@ VkResult swapchain_base::init(VkDevice device, const VkSwapchainCreateInfoKHR *s
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));
@ -292,9 +283,29 @@ 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();
const bool image_deferred_allocation =
swapchain_create_info->flags & VK_SWAPCHAIN_CREATE_DEFERRED_MEMORY_ALLOCATION_BIT_EXT;
for (size_t i = 0; i < swapchain_create_info->minImageCount; i++)
{
std::variant<VkResult, swapchain_image> swapchain_image_result = get_image_factory().create_swapchain_image();
if (auto error = std::get_if<VkResult>(&swapchain_image_result))
{
return *error;
}
swapchain_image image = std::move(std::get<swapchain_image>(swapchain_image_result));
if (!image_deferred_allocation)
{
TRY_LOG_CALL(allocate_and_bind_swapchain_image(image));
image.set_status(swapchain_image::FREE);
}
bool success = m_swapchain_images.try_push_back(std::move(image));
if (!success)
{
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
}
if (VkResult result = m_free_image_semaphore.init(m_swapchain_images.size()); result != VK_SUCCESS)
{
@ -302,30 +313,6 @@ VkResult swapchain_base::init(VkDevice device, const VkSwapchainCreateInfoKHR *s
return result;
}
const bool image_deferred_allocation =
swapchain_create_info->flags & VK_SWAPCHAIN_CREATE_DEFERRED_MEMORY_ALLOCATION_BIT_EXT;
for (auto &img : m_swapchain_images)
{
TRY(create_swapchain_image(m_image_create_info, img));
if (image_deferred_allocation)
{
img.status = swapchain_image::UNALLOCATED;
}
else
{
TRY_LOG_CALL(allocate_and_bind_swapchain_image(m_image_create_info, img));
}
VkSemaphoreCreateInfo semaphore_info = {};
semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
TRY_LOG_CALL(m_device_data.disp.CreateSemaphore(m_device, &semaphore_info, get_allocation_callbacks(),
&img.present_semaphore));
TRY_LOG_CALL(m_device_data.disp.CreateSemaphore(m_device, &semaphore_info, get_allocation_callbacks(),
&img.present_fence_wait));
}
if (!external_sync_supported(m_device_data))
{
/*
@ -426,15 +413,6 @@ void swapchain_base::teardown()
auto *sc = reinterpret_cast<swapchain_base *>(m_ancestor);
sc->clear_descendant();
}
/* Release the images array. */
for (auto &img : m_swapchain_images)
{
/* Call implementation specific release */
destroy_image(img);
m_device_data.disp.DestroySemaphore(m_device, img.present_semaphore, get_allocation_callbacks());
m_device_data.disp.DestroySemaphore(m_device, img.present_fence_wait, get_allocation_callbacks());
}
}
VkResult swapchain_base::acquire_next_image(uint64_t timeout, VkSemaphore semaphore, VkFence fence,
@ -463,19 +441,15 @@ VkResult swapchain_base::acquire_next_image(uint64_t timeout, VkSemaphore semaph
size_t i;
for (i = 0; i < m_swapchain_images.size(); ++i)
{
if (m_swapchain_images[i].status == swapchain_image::UNALLOCATED)
if (m_swapchain_images[i].get_status() == swapchain_image::UNALLOCATED)
{
auto res = allocate_and_bind_swapchain_image(m_image_create_info, m_swapchain_images[i]);
if (res != VK_SUCCESS)
{
WSI_LOG_ERROR("Failed to allocate swapchain image.");
return res != VK_ERROR_INITIALIZATION_FAILED ? res : VK_ERROR_OUT_OF_HOST_MEMORY;
}
TRY_LOG_CALL(allocate_and_bind_swapchain_image(m_swapchain_images[i]));
m_swapchain_images[i].set_status(swapchain_image::FREE);
}
if (m_swapchain_images[i].status == swapchain_image::FREE)
if (m_swapchain_images[i].get_status() == swapchain_image::FREE)
{
m_swapchain_images[i].status = swapchain_image::ACQUIRED;
m_swapchain_images[i].set_status(swapchain_image::ACQUIRED);
*image_index = i;
break;
}
@ -552,7 +526,7 @@ VkResult swapchain_base::get_swapchain_images(uint32_t *swapchain_image_count, V
do
{
swapchain_images[current_image] = m_swapchain_images[current_image].image;
swapchain_images[current_image] = m_swapchain_images[current_image].get_image();
current_image++;
@ -575,10 +549,10 @@ VkResult swapchain_base::get_swapchain_images(uint32_t *swapchain_image_count, V
VkResult swapchain_base::create_aliased_image_handle(VkImage *image)
{
/* Build an alias VkImageCreateInfo compatible with swapchain memory binding. */
VkImageCreateInfo alias_info = m_image_create_info;
alias_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
auto image_create_info = get_image_factory().get_image_handle_creator().get_image_create_info();
VkImageCreateInfo alias_info = image_create_info;
alias_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
return m_device_data.disp.CreateImage(m_device, &alias_info, get_allocation_callbacks(), image);
}
@ -601,12 +575,12 @@ VkResult swapchain_base::notify_presentation_engine(const pending_present_reques
const bool descendant_started_presenting = has_descendant_started_presenting();
if (descendant_started_presenting)
{
m_swapchain_images[pending_present.image_index].status = swapchain_image::FREE;
m_swapchain_images[pending_present.image_index].set_status(swapchain_image::FREE);
m_free_image_semaphore.post();
return VK_ERROR_OUT_OF_DATE_KHR;
}
m_swapchain_images[pending_present.image_index].status = swapchain_image::PENDING;
m_swapchain_images[pending_present.image_index].set_status(swapchain_image::PENDING);
m_started_presenting = true;
if (m_page_flip_thread_run)
@ -637,7 +611,8 @@ VkResult swapchain_base::queue_present(VkQueue queue, const VkPresentInfoKHR *pr
}
}
const VkSemaphore *wait_semaphores = &m_swapchain_images[submit_info.pending_present.image_index].present_semaphore;
VkSemaphore present_semaphore = m_swapchain_images[submit_info.pending_present.image_index].get_present_semaphore();
const VkSemaphore *wait_semaphores = &present_semaphore;
uint32_t sem_count = 1;
if (!submit_info.use_image_present_semaphore)
{
@ -649,8 +624,7 @@ VkResult swapchain_base::queue_present(VkQueue queue, const VkPresentInfoKHR *pr
{
/* If the page flip thread is not running, we need to wait for any present payload here, before setting a new present payload. */
constexpr uint64_t WAIT_PRESENT_TIMEOUT = 1000000000; /* 1 second */
TRY_LOG_CALL(
image_wait_present(m_swapchain_images[submit_info.pending_present.image_index], WAIT_PRESENT_TIMEOUT));
TRY_LOG_CALL(m_swapchain_images[submit_info.pending_present.image_index].wait_present(WAIT_PRESENT_TIMEOUT));
}
void *submission_pnext = nullptr;
@ -660,8 +634,9 @@ VkResult swapchain_base::queue_present(VkQueue queue, const VkPresentInfoKHR *pr
if (submit_info.handle_present_frame_boundary_event)
{
auto *ext = get_swapchain_extension<wsi::wsi_ext_frame_boundary>();
frame_boundary = handle_frame_boundary_event(
present_info, &m_swapchain_images[submit_info.pending_present.image_index].image, ext);
VkImage image = m_swapchain_images[submit_info.pending_present.image_index].get_image();
frame_boundary = handle_frame_boundary_event(present_info, &image, ext);
if (frame_boundary)
{
@ -671,7 +646,7 @@ VkResult swapchain_base::queue_present(VkQueue queue, const VkPresentInfoKHR *pr
if (submit_info.present_fence != VK_NULL_HANDLE)
{
signal_semaphores[count_signal_semaphores++] =
m_swapchain_images[submit_info.pending_present.image_index].present_fence_wait;
m_swapchain_images[submit_info.pending_present.image_index].get_present_fence_wait_semaphore();
}
#if VULKAN_WSI_LAYER_EXPERIMENTAL
const VkPresentTimingInfoEXT *present_timing_info = nullptr;
@ -695,14 +670,15 @@ VkResult swapchain_base::queue_present(VkQueue queue, const VkPresentInfoKHR *pr
count_signal_semaphores > 0 ? signal_semaphores.data() : nullptr,
count_signal_semaphores,
};
TRY_LOG_CALL(image_set_present_payload(m_swapchain_images[submit_info.pending_present.image_index], queue,
semaphores, submission_pnext));
TRY_LOG_CALL(m_swapchain_images[submit_info.pending_present.image_index].set_present_payload(queue, semaphores,
submission_pnext));
if (submit_info.present_fence != VK_NULL_HANDLE)
{
const queue_submit_semaphores wait_semaphores = {
&m_swapchain_images[submit_info.pending_present.image_index].present_fence_wait, 1, nullptr, 0
};
VkSemaphore present_fence_wait_sem =
m_swapchain_images[submit_info.pending_present.image_index].get_present_fence_wait_semaphore();
const queue_submit_semaphores wait_semaphores = { &present_fence_wait_sem, 1, nullptr, 0 };
/*
* Here we chain wait_semaphores with present_fence through present_fence_wait.
*/
@ -724,13 +700,10 @@ VkResult swapchain_base::queue_present(VkQueue queue, const VkPresentInfoKHR *pr
void swapchain_base::deprecate(VkSwapchainKHR descendant)
{
for (auto &img : m_swapchain_images)
{
if (img.status == swapchain_image::FREE)
{
destroy_image(img);
}
}
/* Remove any images that have not been acquried or presented */
m_swapchain_images.erase(std::remove_if(m_swapchain_images.begin(), m_swapchain_images.end(),
[](auto &image) { return image.get_status() == swapchain_image::FREE; }),
m_swapchain_images.end());
/* Set its descendant. */
m_descendant = descendant;
@ -755,8 +728,7 @@ void swapchain_base::wait_for_pending_buffers()
* and then subsequent calls might block if the semaphore value
* has reached to zero.
*/
if ((img.status == swapchain_image::ACQUIRED) || (img.status == swapchain_image::FREE) ||
(img.status == swapchain_image::INVALID))
if ((img.get_status() == swapchain_image::ACQUIRED) || (img.get_status() == swapchain_image::FREE))
{
/* If the image is acquired or free, it is not pending. */
non_pending_images++;
@ -825,49 +797,30 @@ void swapchain_base::release_images(uint32_t image_count, const uint32_t *indice
uint32_t index = indices[i];
assert(index < m_swapchain_images.size());
/* Applications can only pass acquired images that the device doesn't own */
assert(m_swapchain_images[index].status == swapchain_image::ACQUIRED);
assert(m_swapchain_images[index].get_status() == swapchain_image::ACQUIRED);
unpresent_image(index);
}
}
VkResult swapchain_base::is_bind_allowed(uint32_t image_index) const
{
return m_swapchain_images[image_index].status != swapchain_image::UNALLOCATED ? VK_SUCCESS :
return m_swapchain_images[image_index].get_status() != swapchain_image::UNALLOCATED ? VK_SUCCESS :
VK_ERROR_OUT_OF_HOST_MEMORY;
}
bool swapchain_base::add_swapchain_extension(util::unique_ptr<wsi_ext> extension)
bool swapchain_base::add_swapchain_extension(util::unique_ptr<util::wsi_ext> extension)
{
return m_extensions.add_extension(std::move(extension));
}
VkResult swapchain_base::image_creator_init(const VkSwapchainCreateInfoKHR &swapchain_create_info)
VkResult swapchain_base::bind_swapchain_image(const VkBindImageMemoryInfo *bind_image_mem_info,
const VkBindImageMemorySwapchainInfoKHR *bind_sc_info)
{
m_image_creator.init(swapchain_create_info);
TRY_LOG(is_bind_allowed(bind_sc_info->imageIndex),
"Bind is not allowed on images that haven't been acquired first.");
util::vector<util::unique_ptr<swapchain_image_create_info_extension>> 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<VkImageFormatListCreateInfo>(
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;
wsi::swapchain_image &swapchain_image = m_swapchain_images[bind_sc_info->imageIndex];
return swapchain_image.bind(bind_image_mem_info);
}
} /* namespace wsi */

View file

@ -35,6 +35,7 @@
#include <vulkan/vulkan.h>
#include <thread>
#include <array>
#include <variant>
#include <util/custom_allocator.hpp>
#include <util/custom_mutex.hpp>
@ -43,42 +44,22 @@
#include <util/timed_semaphore.hpp>
#include <util/log.hpp>
#include <util/macros.hpp>
#include <util/wsi_extension.hpp>
#include <layer/private_data.hpp>
#include "surface_properties.hpp"
#include "synchronization.hpp"
#include "swapchain_image_creator.hpp"
#include "vulkan_image_handle_creator.hpp"
#include "swapchain_image.hpp"
#include "swapchain_image_factory.hpp"
#include "extensions/frame_boundary.hpp"
#include "extensions/wsi_extension.hpp"
#include "extensions/present_id.hpp"
namespace wsi
{
using util::MAX_PLANES;
struct swapchain_image
{
enum status
{
INVALID,
ACQUIRED,
PENDING,
PRESENTED,
FREE,
UNALLOCATED,
};
/* Implementation specific data */
void *data{ nullptr };
VkImage image{ VK_NULL_HANDLE };
status status{ swapchain_image::INVALID };
VkSemaphore present_semaphore{ VK_NULL_HANDLE };
VkSemaphore present_fence_wait{ VK_NULL_HANDLE };
};
struct pending_present_request
{
/* The index of the pending image to use for present. */
@ -242,24 +223,14 @@ public:
* VkBindImageMemorySwapchainInfoKHR struct has been provided in the vkBindImageMemory2
* function.
*
* @param device is the logical device that owns the images and memory.
* @param bind_image_mem_info details the image we want to bind.
* @param bind_sc_info describes the swapchain memory to bind to.
*
* @return VK_SUCCESS on success, otherwise on failure VK_ERROR_OUT_OF_HOST_MEMORY or VK_ERROR_OUT_OF_DEVICE_MEMORY
* can be returned.
*/
virtual VkResult bind_swapchain_image(VkDevice &device, const VkBindImageMemoryInfo *bind_image_mem_info,
const VkBindImageMemorySwapchainInfoKHR *bind_sc_info) = 0;
/**
* @brief Get the DRM modifier of all swapchain images.
*
* Retrieves the DRM modifier used to create swapchain images.
*
* @return The DRM modifier used for the images.
*/
virtual uint64_t get_modifier() = 0;
VkResult bind_swapchain_image(const VkBindImageMemoryInfo *bind_image_mem_info,
const VkBindImageMemorySwapchainInfoKHR *bind_sc_info);
/**
* @brief Get image's present semaphore
@ -270,7 +241,7 @@ public:
*/
VkSemaphore get_image_present_semaphore(uint32_t image_index)
{
return m_swapchain_images[image_index].present_semaphore;
return m_swapchain_images[image_index].get_present_semaphore();
}
/**
@ -324,7 +295,7 @@ public:
* @return Returns true on success and false otherwise.
*/
bool add_swapchain_extension(util::unique_ptr<wsi_ext> extension);
bool add_swapchain_extension(util::unique_ptr<util::wsi_ext> extension);
/**
* @brief Enable mutable format swapchain support.
@ -344,6 +315,16 @@ public:
return m_mutable_format_enabled;
}
/**
* @brief Get the modifier used for the images in the swapchain
*
* @return uint64_t DRM format modifier
*/
uint64_t get_modifier() const
{
return m_swapchain_images.empty() ? 0 : m_swapchain_images[0].get_modifier();
}
protected:
layer::device_private_data &m_device_data;
@ -450,11 +431,6 @@ protected:
*/
VkQueue m_queue;
/**
* @brief Image creation info used for all swapchain images.
*/
VkImageCreateInfo m_image_create_info;
/**
* @brief Return the VkAllocationCallbacks passed in this object constructor.
*/
@ -520,14 +496,11 @@ protected:
}
/**
* @brief Get backend specific image create info extensions.
* @brief Get the image factory used for creating swapchain images.
*
* @param swapchain_create_info Swapchain create info.
* @param[in,out] extensions Swapchain image create info extensions filled by the backend.
* @return Swapchain image factory.
*/
virtual VkResult get_required_image_creator_extensions(
const VkSwapchainCreateInfoKHR &swapchain_create_info,
util::vector<util::unique_ptr<swapchain_image_create_info_extension>> *extensions) = 0;
virtual swapchain_image_factory &get_image_factory() = 0;
/**
* @brief Base swapchain teardown.
@ -544,24 +517,11 @@ protected:
/**
* @brief Allocates and binds a new swapchain image.
*
* @param image_create_info Data to be used to create the image.
* @param image Handle to the image.
* @param swapchain_image Swapchain image.
*
* @return Returns VK_SUCCESS on success, otherwise an appropriate error code.
*/
virtual VkResult allocate_and_bind_swapchain_image(VkImageCreateInfo image_create_info, swapchain_image &image) = 0;
/**
* @brief Creates a new swapchain image.
*
* @param image_create_info Data to be used to create the image.
* @param image Handle to the image.
*
* @return If image creation is successful returns VK_SUCCESS, otherwise
* will return VK_ERROR_OUT_OF_DEVICE_MEMORY or VK_ERROR_INITIALIZATION_FAILED
* depending on the error that occurred.
*/
virtual VkResult create_swapchain_image(VkImageCreateInfo image_create_info, swapchain_image &image) = 0;
virtual VkResult allocate_and_bind_swapchain_image(swapchain_image &swapchain_image) = 0;
/**
* @brief Method to present and image
@ -581,15 +541,6 @@ protected:
*/
void unpresent_image(uint32_t presented_index);
/**
* @brief Method to release a swapchain image
*
* Releases resources stored in the data member of a swapchain_image.
*
* @param image Handle to the image about to be released.
*/
virtual void destroy_image([[maybe_unused]] swapchain_image &image){};
/**
* @brief Hook for any actions to free up a buffer for acquire
*
@ -609,34 +560,6 @@ protected:
return VK_SUCCESS;
}
/**
* @brief Sets the present payload for a swapchain image.
*
* @param[in] image The swapchain image for which to set a present payload.
* @param queue A Vulkan queue that can be used for any Vulkan commands needed.
* @param[in] semaphores The wait and signal semaphores and their number of elements.
* @param[in] submission_pnext Chain of pointers to attach to the payload submission.
*
* @return VK_SUCCESS on success or an error code otherwise.
*/
virtual VkResult image_set_present_payload(swapchain_image &image, VkQueue queue,
const queue_submit_semaphores &semaphores,
const void *submission_pnext = nullptr) = 0;
/**
* @brief Waits for the present payload of an image if necessary.
*
* If the page flip thread needs to wait for the image present synchronization payload the WSI implemention can block
* and wait in this call. Otherwise the function should return successfully without blocking.
*
* @param[in] image The swapchain image for which the function may need to wait until the presentat payload has
* finished.
* @param timeout Timeout for any wait in nanoseconds.
*
* @return VK_SUCCESS if waiting was successful or unnecessary. An error code otherwise.
*/
virtual VkResult image_wait_present(swapchain_image &image, uint64_t timeout) = 0;
/**
* @brief Returns true if an error has occurred.
*/
@ -698,12 +621,7 @@ private:
/**
* @brief Holds the swapchain extensions and related functionalities.
*/
wsi_ext_maintainer m_extensions;
/**
* @brief Holds the VkImageCreateInfo and backend specific image create info extensions.
*/
swapchain_image_creator m_image_creator;
util::wsi_ext_maintainer m_extensions;
/**
* @brief Flag indicating if mutable format swapchain support is enabled.
@ -781,13 +699,6 @@ private:
* @return VK_SUCCESS on success or an error code otherwise.
*/
VkResult notify_presentation_engine(const pending_present_request &submit_info);
/**
* @brief Initialize m_image_creator.
*
* @param swapchain_create_info Swapchain create info.
*/
VkResult image_creator_init(const VkSwapchainCreateInfoKHR &swapchain_create_info);
};
} /* namespace wsi */

159
wsi/swapchain_image.cpp Normal file
View file

@ -0,0 +1,159 @@
/*
* 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
*
* @brief Contains the implementation for swapchain images.
*/
#include "swapchain_image.hpp"
namespace wsi
{
std::variant<VkResult, swapchain_image> swapchain_image::create(create_args &create_args)
{
VkSemaphoreCreateInfo semaphore_info = {};
semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
VkDevice device = create_args.m_device_data->device;
auto *device_data = create_args.m_device_data;
VkSemaphore present_semaphore = VK_NULL_HANDLE;
TRY_LOG_CALL(device_data->disp.CreateSemaphore(
device, &semaphore_info, create_args.m_allocator.get_original_callbacks(), &present_semaphore));
VkSemaphore present_fence_wait = VK_NULL_HANDLE;
VkResult result = device_data->disp.CreateSemaphore(
device, &semaphore_info, create_args.m_allocator.get_original_callbacks(), &present_fence_wait);
if (result != VK_SUCCESS)
{
device_data->disp.DestroySemaphore(device, present_semaphore, create_args.m_allocator.get_original_callbacks());
return result;
}
util::unique_ptr<fence_sync> present_fence;
if (create_args.m_exportable_fence)
{
auto present_fence_opt = sync_fd_fence_sync::create(*device_data);
if (!present_fence_opt.has_value())
{
device_data->disp.DestroySemaphore(device, present_semaphore,
create_args.m_allocator.get_original_callbacks());
device_data->disp.DestroySemaphore(device, present_fence_wait,
create_args.m_allocator.get_original_callbacks());
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
present_fence = create_args.m_allocator.make_unique<sync_fd_fence_sync>(std::move(present_fence_opt.value()));
}
else
{
auto present_fence_opt = fence_sync::create(*device_data);
if (!present_fence_opt.has_value())
{
device_data->disp.DestroySemaphore(device, present_semaphore,
create_args.m_allocator.get_original_callbacks());
device_data->disp.DestroySemaphore(device, present_fence_wait,
create_args.m_allocator.get_original_callbacks());
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
present_fence = create_args.m_allocator.make_unique<fence_sync>(std::move(present_fence_opt.value()));
}
if (present_fence == nullptr)
{
device_data->disp.DestroySemaphore(device, present_semaphore, create_args.m_allocator.get_original_callbacks());
device_data->disp.DestroySemaphore(device, present_fence_wait, create_args.m_allocator.get_original_callbacks());
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
return swapchain_image(create_args.m_image_handle, present_semaphore, present_fence_wait, std::move(present_fence),
create_args.m_wait_on_present_fence, create_args.m_device_data, create_args.m_allocator,
std::move(create_args.m_backing_memory));
}
void swapchain_image::destroy()
{
/* Set UNALLOCATED state for debugging purposes in case there are any uses of image after it has been destroyed
* as we don't have hold error state anymore. */
set_status(swapchain_image::UNALLOCATED);
if (m_present_semaphore != VK_NULL_HANDLE)
{
m_device_data->disp.DestroySemaphore(m_device_data->device, m_present_semaphore,
m_allocator.get_original_callbacks());
m_present_semaphore = VK_NULL_HANDLE;
}
if (m_present_fence_wait_semaphore != VK_NULL_HANDLE)
{
m_device_data->disp.DestroySemaphore(m_device_data->device, m_present_fence_wait_semaphore,
m_allocator.get_original_callbacks());
m_present_fence_wait_semaphore = VK_NULL_HANDLE;
}
if (m_image != VK_NULL_HANDLE)
{
m_device_data->disp.DestroyImage(m_device_data->device, m_image, m_allocator.get_original_callbacks());
m_image = VK_NULL_HANDLE;
}
if (m_present_fence)
{
m_present_fence.reset();
}
if (m_image_memory)
{
m_image_memory.reset();
}
if (m_data)
{
m_data.reset();
}
}
VkResult swapchain_image::bind(const VkBindImageMemoryInfo *bind_image_mem_info)
{
return m_image_memory->bind(bind_image_mem_info);
}
VkResult swapchain_image::set_present_payload(VkQueue queue, const queue_submit_semaphores &semaphores,
const void *submission_pnext)
{
return m_present_fence->set_payload(queue, semaphores, submission_pnext);
}
VkResult swapchain_image::wait_present(uint64_t timeout_ns)
{
if (m_wait_on_present_fence)
{
return m_present_fence->wait_payload(timeout_ns);
}
return VK_SUCCESS;
}
}

381
wsi/swapchain_image.hpp Normal file
View file

@ -0,0 +1,381 @@
/*
* 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
*
* @brief Contains the class header for swapchain images.
*/
#pragma once
#include <memory>
#include <array>
#include <utility>
#include <variant>
#include <functional>
#include <vulkan/vulkan.h>
#include <util/custom_allocator.hpp>
#include <util/drm/drm_utils.hpp>
#include <layer/private_data.hpp>
#include "synchronization.hpp"
namespace wsi
{
class swapchain_image_factory;
/**
* @brief Base class describing swapchain image memory allocator/binder
*/
class image_backing_memory : private util::noncopyable
{
public:
virtual ~image_backing_memory() = default;
/**
* @brief Bind Vulkan image
*
* @param bind_image_mem_info Bind info
* @return Vulkan result code
*/
virtual VkResult bind(const VkBindImageMemoryInfo *bind_image_mem_info) = 0;
/**
* @brief Get the modifier used for the image
*
* @return uint64_t DRM format modifier
*/
virtual uint64_t get_modifier() const = 0;
};
/**
* @brief Base class describing swapchain image data
*/
class swapchain_image_data : private util::noncopyable
{
public:
virtual ~swapchain_image_data() = default;
};
/**
* @brief Class describing a swapchain image, its syncronisation objects
* and any additional data attached to it
*/
class swapchain_image
{
public:
/**
* @brief Swapchain state
*/
enum status
{
ACQUIRED, // Image has been acquired through vkAcquireNextImage operation
PENDING, // Image has been submitted for presentation is waiting to be presented
PRESENTED, // Image has been presented
FREE, // Image is free to be acquired
UNALLOCATED, // Image has yet to have memory and other resources allocated for it
};
~swapchain_image()
{
destroy();
}
swapchain_image(swapchain_image &&other) noexcept
: m_present_semaphore(std::exchange(other.m_present_semaphore, VK_NULL_HANDLE))
, m_present_fence_wait_semaphore(std::exchange(other.m_present_fence_wait_semaphore, VK_NULL_HANDLE))
, m_present_fence(std::move(other.m_present_fence))
, m_wait_on_present_fence(other.m_wait_on_present_fence)
, m_image(std::exchange(other.m_image, VK_NULL_HANDLE))
, m_status(std::exchange(other.m_status, swapchain_image::UNALLOCATED))
, m_allocator(other.m_allocator)
, m_device_data(std::exchange(other.m_device_data, nullptr))
, m_image_memory(std::move(other.m_image_memory))
, m_data(std::move(other.m_data))
{
}
swapchain_image &operator=(swapchain_image &&other) noexcept
{
if (this == &other)
{
return *this;
}
std::swap(m_present_semaphore, other.m_present_semaphore);
std::swap(m_present_fence_wait_semaphore, other.m_present_fence_wait_semaphore);
std::swap(m_present_fence, other.m_present_fence);
std::swap(m_wait_on_present_fence, other.m_wait_on_present_fence);
std::swap(m_image, other.m_image);
std::swap(m_status, other.m_status);
std::swap(m_allocator, other.m_allocator);
std::swap(m_device_data, other.m_device_data);
std::swap(m_image_memory, other.m_image_memory);
std::swap(m_data, other.m_data);
return *this;
}
/**
* @brief Get the image object
*
* @return VkImage
*/
VkImage get_image() const
{
return m_image;
}
/**
* @brief Get the status of the swapchain image
*
* @return Status of the image
*/
status get_status() const
{
return m_status;
}
/**
* @brief Set the status of the swapchain image
*
* @param new_status New status
*/
void set_status(status new_status)
{
m_status = new_status;
}
/**
* @brief Get the present semaphore of the image
*
* @return VkSemaphore
*/
VkSemaphore get_present_semaphore() const
{
return m_present_semaphore;
}
/**
* @brief Get the present fence wait semaphore of the image
*
* @return VkSemaphore
*/
VkSemaphore get_present_fence_wait_semaphore() const
{
return m_present_fence_wait_semaphore;
}
/**
* @brief Get the present fence synchronisation object
*
* @return Fence sync object. Note that this can be nullptr if the image has been destroyed.
*/
fence_sync *get_present_fence() const
{
return m_present_fence.get();
}
/**
* @brief Bind Vulkan image
*
* @param bind_image_mem_info Bind info
* @return Vulkan result code
*/
VkResult bind(const VkBindImageMemoryInfo *bind_image_mem_info);
/**
* @brief Sets the present payload for a swapchain image.
*
* @param[in] image The swapchain image for which to set a present payload.
* @param queue A Vulkan queue that can be used for any Vulkan commands needed.
* @param[in] semaphores The wait and signal semaphores and their number of elements.
* @param[in] submission_pnext Chain of pointers to attach to the payload submission.
*
* @return VK_SUCCESS on success or an error code otherwise.
*/
VkResult set_present_payload(VkQueue queue, const queue_submit_semaphores &semaphores, const void *submission_pnext);
/**
* @brief Wait on the present fence
*
* @param timeout_ns Timeout in nanoseconds
* @return Vulkan result code
*/
VkResult wait_present(uint64_t timeout_ns);
/**
* @brief Get the image data object
*
* @tparam T The type of the data object
* @return The image data object
*/
template <typename T>
T *get_data()
{
return static_cast<T *>(m_data.get());
}
/**
* @brief Set the image data object
*
* @param data Data object
*/
void set_data(util::unique_ptr<swapchain_image_data> data)
{
m_data = std::move(data);
}
uint64_t get_modifier() const
{
return m_image_memory->get_modifier();
}
private:
/* Only allow swapchain image factory to create this class and acquire backing memory from it */
friend swapchain_image_factory;
/**
* @brief Construct a new swapchain image object
*
* @param image_handle Vulkan image to use for this swapchain image
* @param present_semaphore Present semaphore
* @param present_fence_wait_semaphore Present fence wait semaphore
* @param present_fence Present fence synchronisation object
* @param wait_on_present_fence Whether wait_present will wait on the @p present_fence
* @param device_data Device data
* @param allocator Allocator
* @param image_memory_creator Image memory binder/allocator
*/
swapchain_image(VkImage image_handle, VkSemaphore present_semaphore, VkSemaphore present_fence_wait_semaphore,
util::unique_ptr<fence_sync> present_fence, bool wait_on_present_fence,
const layer::device_private_data *device_data, util::allocator allocator,
util::unique_ptr<image_backing_memory> image_memory)
: m_present_semaphore(present_semaphore)
, m_present_fence_wait_semaphore(present_fence_wait_semaphore)
, m_present_fence(std::move(present_fence))
, m_wait_on_present_fence(wait_on_present_fence)
, m_image(image_handle)
, m_status(swapchain_image::UNALLOCATED)
, m_allocator(allocator)
, m_device_data(device_data)
, m_image_memory(std::move(image_memory))
, m_data(nullptr)
{
assert(m_image_memory != nullptr);
};
struct create_args
{
/**
* @brief Arguments that describe how the swapchain image will be created
*
* @param device_data Device data
* @param allocator Allocator
* @param image_handle Vulkan image handle that will be associated with this swapchain image
* @param image_memory Swapchain image backing memory
* @param exportable_fence Whether swapchain image needs an exportable fence
* @param wait_on_present_fence Whether swapchain image should wait on the exportable fence
* when asked to wait on present.
*/
create_args(layer::device_private_data *device_data, util::allocator allocator, VkImage image_handle,
util::unique_ptr<image_backing_memory> image_memory, bool exportable_fence,
bool wait_on_present_fence)
: m_device_data(device_data)
, m_allocator(allocator)
, m_image_handle(image_handle)
, m_backing_memory(std::move(image_memory))
, m_exportable_fence(exportable_fence)
, m_wait_on_present_fence(wait_on_present_fence)
{
}
layer::device_private_data *m_device_data;
const util::allocator m_allocator;
VkImage m_image_handle;
util::unique_ptr<image_backing_memory> m_backing_memory;
bool m_exportable_fence;
bool m_wait_on_present_fence;
};
/**
* @brief Create a swapchain image.
*
* @param create_args Arguments that describe how to create the swapchain image.
* @return VkResult on failure or the created swapchain image on success.
*/
static std::variant<VkResult, swapchain_image> create(create_args &create_args);
/**
* @brief Get the image backing memory object
*
* @tparam T The type of the backing memory object
* @return The image backing memory object
*/
template <typename T>
T *get_backing_memory()
{
return static_cast<T *>(m_image_memory.get());
}
/**
* @brief Method to release a swapchain image and its resources
*/
void destroy();
VkSemaphore m_present_semaphore;
VkSemaphore m_present_fence_wait_semaphore;
util::unique_ptr<fence_sync> m_present_fence;
bool m_wait_on_present_fence;
VkImage m_image;
status m_status;
util::allocator m_allocator;
const layer::device_private_data *m_device_data;
util::unique_ptr<image_backing_memory> m_image_memory;
util::unique_ptr<swapchain_image_data> m_data;
};
/**
* @brief Base class describing swapchain image backing memory creator
*/
class image_backing_memory_creator
{
public:
virtual ~image_backing_memory_creator() = default;
/**
* @brief Create a image backing memory object
*
* @param allocator Allocator to use for creating the backing memory object
* @return Image backing memory object or nullptr on failure
*/
virtual util::unique_ptr<image_backing_memory> create_image_backing_memory(util::allocator &allocator) = 0;
};
}

View file

@ -1,236 +0,0 @@
/*
* 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;
}
} /* namespace wsi */

View file

@ -1,107 +0,0 @@
/*
* 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
{
/**
* @brief
*/
class swapchain_image_create_external_memory : public swapchain_image_create_info_extension
{
public:
/**
* @brief Constructs a swapchain_image_create_external_memory object.
*
* @param[in] compression Optional compression control parameters for the swapchain image.
* @param[in] wsi_allocator wsialloc allocator pointer.
* @param[in] surface_formats Supported surface formats from the presentation engine.
* @param[in] physical_device Vulkan physical device handle.
* @param[in] allocator User provided allocation callbacks.
*/
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;
/**
* @brief Finds what formats are compatible with the requested swapchain image Vulkan Device and backend surface.
*
* @param info The Swapchain image creation info.
* @param[out] importable_formats A list of formats that can be imported to the Vulkan Device.
* @param[out] exportable_formats A list of formats that can be exported from the Vulkan Device.
* @param[out] drm_format_props A list of all device supported VkDrmFormatModifierPropertiesEXT.
*
* @return VK_SUCCESS or VK_ERROR_OUT_OF_HOST_MEMORY
*/
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

@ -1,88 +0,0 @@
/*
* 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 image_compression_control.cpp
*
* @brief Contains the implementation for VK_EXT_image_compression_control extension.
*/
#include <cassert>
#include <util/helpers.hpp>
#include "image_compression_control.hpp"
namespace wsi
{
swapchain_image_create_compression_control::swapchain_image_create_compression_control(
const VkImageCompressionControlEXT &extension)
: m_compression_control{ VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT, nullptr, extension.flags,
extension.compressionControlPlaneCount, m_array_fixed_rate_flags }
{
for (uint32_t i = 0; i < extension.compressionControlPlaneCount; i++)
{
m_compression_control.pFixedRateFlags[i] = extension.pFixedRateFlags[i];
}
}
swapchain_image_create_compression_control::swapchain_image_create_compression_control(
const swapchain_image_create_compression_control &extension)
: swapchain_image_create_compression_control(extension.m_compression_control)
{
}
VkImageCompressionControlEXT swapchain_image_create_compression_control::get_compression_control_properties()
{
return m_compression_control;
}
VkImageCompressionFlagsEXT swapchain_image_create_compression_control::get_bitmask_for_image_compression_flags()
{
return m_compression_control.flags;
}
std::optional<swapchain_image_create_compression_control> swapchain_image_create_compression_control::create(
bool compression_enabled, const VkSwapchainCreateInfoKHR &swapchain_create_info)
{
const auto *image_compression_control = util::find_extension<VkImageCompressionControlEXT>(
VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT, swapchain_create_info.pNext);
if (compression_enabled && image_compression_control != nullptr)
{
return swapchain_image_create_compression_control{ *image_compression_control };
}
return std::nullopt;
}
VkResult swapchain_image_create_compression_control::extend_image_create_info(VkImageCreateInfo *image_create_info)
{
assert(image_create_info != nullptr);
m_compression_control.pNext = image_create_info->pNext;
image_create_info->pNext = &m_compression_control;
return VK_SUCCESS;
}
} /* namespace wsi */

View file

@ -1,121 +0,0 @@
/*
* 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 image_compression_control.hpp
*
* @brief Contains the implementation for VK_EXT_image_compression_control extension.
*
*/
#pragma once
#include <optional>
#include <utility>
#include <util/custom_allocator.hpp>
#include <util/macros.hpp>
#include "swapchain_image_create_info_extension.hpp"
namespace wsi
{
using util::MAX_PLANES;
/**
* @brief Image compresssion control extension class
*
* This class implements the image compression control features.
* Backends needing additional features will create its own local
* copy and inherit this class.
*/
class swapchain_image_create_compression_control : public swapchain_image_create_info_extension
{
public:
swapchain_image_create_compression_control(const swapchain_image_create_compression_control &extension);
swapchain_image_create_compression_control &operator=(const swapchain_image_create_compression_control &extension)
{
if (this == &extension)
{
return *this;
}
auto compression_control = swapchain_image_create_compression_control(extension);
std::swap(m_compression_control, compression_control.m_compression_control);
for (uint32_t i = 0; i < compression_control.m_compression_control.compressionControlPlaneCount; i++)
{
m_compression_control.pFixedRateFlags[i] = compression_control.m_compression_control.pFixedRateFlags[i];
}
return *this;
}
/**
* @brief Create swapchain_image_create_compression_control class if deemed necessary.
*
* @param device The Vulkan device
* @param swapchain_create_info Swapchain create info
* @return Valid swapchain_image_create_compression_control if requested by application,
* otherwise - an empty optional.
*/
static std::optional<swapchain_image_create_compression_control> create(
bool compression_enabled, const VkSwapchainCreateInfoKHR &swapchain_create_info);
/**
* @brief This API is used to get the compression control properties of an image.
*
* @return The image compression control properties.
*/
VkImageCompressionControlEXT get_compression_control_properties();
/**
* @brief This API is used to get the bitmask for image compression flags.
*
* @return The bitmask for image compression flags.
*/
VkImageCompressionFlagsEXT get_bitmask_for_image_compression_flags();
VkResult extend_image_create_info(VkImageCreateInfo *image_create_info) override;
private:
/**
* @brief Constructor for the swapchain_image_create_compression_control class.
*
* @param extension Reference to VkImageCompressionControlEXT structure.
*/
swapchain_image_create_compression_control(const VkImageCompressionControlEXT &extension);
/**
* @brief Array to hold the pFixedRateFlags.
*/
VkImageCompressionFixedRateFlagsEXT m_array_fixed_rate_flags[MAX_PLANES];
/**
* @brief Image compression control properties.
*/
VkImageCompressionControlEXT m_compression_control;
};
} /* namespace wsi */

View file

@ -0,0 +1,101 @@
/*
* 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 swapchain_image_factory.cpp
*
* @brief Contains the implementation for the swapchain image factory that
* is used to create swapchain images.
*/
#include "swapchain_image_factory.hpp"
#include <util/helpers.hpp>
namespace wsi
{
swapchain_image_factory::swapchain_image_factory(util::allocator allocator, layer::device_private_data &device_data)
: m_allocator(allocator)
, m_device_data(device_data)
, m_image_handle_creator(nullptr)
{
}
void swapchain_image_factory::init(util::unique_ptr<vulkan_image_handle_creator> image_handle_creator,
util::unique_ptr<image_backing_memory_creator> image_memory_creator,
bool exportable_fence, bool wait_on_present_fence)
{
m_image_handle_creator = std::move(image_handle_creator);
m_image_backing_memory_creator = std::move(image_memory_creator);
m_exportable_fence = exportable_fence;
m_wait_on_present_fence = wait_on_present_fence;
}
vulkan_image_handle_creator &swapchain_image_factory::get_image_handle_creator()
{
return *m_image_handle_creator;
}
std::variant<VkResult, VkImage> swapchain_image_factory::create_image_handle()
{
assert(m_image_handle_creator != nullptr);
VkImage image_handle = VK_NULL_HANDLE;
TRY_LOG_CALL(m_image_handle_creator->create_image(m_device_data, m_allocator, image_handle));
return image_handle;
}
std::variant<VkResult, swapchain_image> swapchain_image_factory::create_swapchain_image()
{
assert(m_image_backing_memory_creator != nullptr);
util::unique_ptr<image_backing_memory> backing_memory =
m_image_backing_memory_creator->create_image_backing_memory(m_allocator);
if (backing_memory == nullptr)
{
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
std::variant<VkResult, VkImage> handle_result = create_image_handle();
if (auto error = std::get_if<VkResult>(&handle_result))
{
return *error;
}
VkImage image_handle = std::get<VkImage>(handle_result);
swapchain_image::create_args args{ &m_device_data, m_allocator,
image_handle, std::move(backing_memory),
m_exportable_fence, m_wait_on_present_fence };
auto swapchain_image = swapchain_image::create(args);
if (auto error = std::get_if<VkResult>(&swapchain_image))
{
m_device_data.disp.DestroyImage(m_device_data.device, image_handle, m_allocator.get_original_callbacks());
return *error;
}
return swapchain_image;
}
} /* namespace wsi */

View file

@ -0,0 +1,124 @@
/*
* 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 swapchain_image_factory.hpp
*
* @brief Contains the implementation for the swapchain image factory that
* is used to create swapchain images.
*/
#pragma once
#include <variant>
#include <functional>
#include <vulkan/vulkan.h>
#include <util/custom_allocator.hpp>
#include <wsi/extensions/image_create_info_extension.hpp>
#include "swapchain_image.hpp"
#include "vulkan_image_handle_creator.hpp"
namespace wsi
{
class swapchain_image_factory
{
public:
swapchain_image_factory(util::allocator allocator, layer::device_private_data &device_data);
/**
* @brief Get the image backing memory object
*
* @tparam T The type of the backing memory object
* @return The image backing memory object
*/
template <typename T>
static T &get_backing_memory_from_image(swapchain_image &image)
{
assert(image.get_backing_memory<T>() != nullptr);
return *image.get_backing_memory<T>();
}
/**
* @brief Initalise image factory.
*
* @param image_handle_creator Vulkan image handle creator.
* @param image_memory_creator Backing image memory creator.
* @param exportable_fence Whether swapchain image needs an exportable fence
* @param wait_on_present_fence Whether swapchain image should wait on the exportable fence
* when asked to wait on present.
*/
void init(util::unique_ptr<vulkan_image_handle_creator> image_handle_creator,
util::unique_ptr<image_backing_memory_creator> image_memory_creator, bool exportable_fence,
bool wait_on_present_fence);
/**
* @brief Create a swapchain image.
*
* @return If error occurred, returns VkResult, swapchain_image object otherwise.
*/
std::variant<VkResult, swapchain_image> create_swapchain_image();
/**
* @brief Get the image handle creator.
*
* @return Image handle creator.
*/
vulkan_image_handle_creator &get_image_handle_creator();
private:
/**
* @brief Create a Vulkan image handle
*
* @return If error occurred, returns VkResult, VkImage handle otherwise.
*/
std::variant<VkResult, VkImage> create_image_handle();
util::allocator m_allocator;
layer::device_private_data &m_device_data;
/**
* @brief Creates backing memory for swapchain images
*/
util::unique_ptr<image_backing_memory_creator> m_image_backing_memory_creator;
/**
* @brief Holds the VkImageCreateInfo and backend specific image create info extensions.
*/
util::unique_ptr<vulkan_image_handle_creator> m_image_handle_creator;
/**
* @brief Whether swapchain image needs an exportable fence.
*/
bool m_exportable_fence;
/**
* @brief Whether swapchain image should wait on the exportable fence when asked to wait on present.
*/
bool m_wait_on_present_fence;
};
} /* namespace wsi */

View file

@ -23,17 +23,20 @@
*/
/**
* @file swapchain_image_creator.cpp
* @file vulkan_image_handle_creator.cpp
*/
#include "swapchain_image_creator.hpp"
#include "swapchain_image_create_extensions/swapchain_image_create_info_extension.hpp"
#include "vulkan_image_handle_creator.hpp"
#include "extensions/image_create_info_extension.hpp"
#include "util/helpers.hpp"
namespace wsi
{
void swapchain_image_creator::init(const VkSwapchainCreateInfoKHR &swapchain_create_info)
vulkan_image_handle_creator::vulkan_image_handle_creator(util::allocator allocator,
const VkSwapchainCreateInfoKHR &swapchain_create_info)
: m_image_create_info()
, m_extensions(allocator)
{
m_image_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
m_image_create_info.pNext = nullptr;
@ -53,20 +56,17 @@ void swapchain_image_creator::init(const VkSwapchainCreateInfoKHR &swapchain_cre
m_image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
}
VkResult swapchain_image_creator::add_extensions(
util::vector<util::unique_ptr<swapchain_image_create_info_extension>> &extensions)
VkResult vulkan_image_handle_creator::create_image(layer::device_private_data &device_data,
const util::allocator &allocator, VkImage &out_image_handle)
{
for (auto &extension : extensions)
return device_data.disp.CreateImage(device_data.device, &m_image_create_info, allocator.get_original_callbacks(),
&out_image_handle);
}
VkResult vulkan_image_handle_creator::add_extension(util::unique_ptr<image_create_info_extension> extension)
{
TRY(extension->extend_image_create_info(&m_image_create_info));
if (!m_extensions.try_push_back(std::move(extension)))
{
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
}
return VK_SUCCESS;
return m_extensions.try_push_back(std::move(extension)) ? VK_SUCCESS : VK_ERROR_OUT_OF_HOST_MEMORY;
}
} /* namespace wsi */

View file

@ -23,49 +23,54 @@
*/
/**
* @file swapchain_image_creator.hpp
* @file vulkan_image_handle_creator.hpp
*/
#pragma once
#include <vulkan/vulkan.h>
#include <util/custom_allocator.hpp>
#include <wsi/swapchain_image_create_extensions/swapchain_image_create_info_extension.hpp>
#include <wsi/extensions/image_create_info_extension.hpp>
#include "swapchain_image.hpp"
namespace wsi
{
/**
* @brief This class is responsible for owning and extending a swapchain's image
* creation info.
* creation info and creating Vulkan image handles.
*/
class swapchain_image_creator
class vulkan_image_handle_creator
{
public:
swapchain_image_creator(const util::allocator &allocator)
: m_image_create_info()
, m_extensions(allocator)
{
m_image_create_info.format = VK_FORMAT_UNDEFINED;
};
/**
* @brief Construct a new vulkan image handle creator
*
* @param allocator Allocator
* @param swapchain_create_info Swapchain create info passed by application.
*/
vulkan_image_handle_creator(util::allocator allocator, const VkSwapchainCreateInfoKHR &swapchain_create_info);
/**
* @brief Create image create info.
* @brief Create Vulkan swapchain image handle
*
* @param swapchain_create_info Swapchain create info.
* @param device_data Device data
* @param allocator Allocator
* @param out_image_handle If successful, Vulkan image handle
* @return Vulkan result code.
*/
void init(const VkSwapchainCreateInfoKHR &swapchain_create_info);
VkResult create_image(layer::device_private_data &device_data, const util::allocator &allocator,
VkImage &out_image_handle);
/**
* @brief Extend create info with extension data.
*
* @param extensions Swapchain image create info extensions.
* @param extension Swapchain image create info extension.
*
* @return VK_SUCCESS on success, an appropriate error code on failure.
*
* @note The extensions will be moved out of the vector and will become nullptr.
*/
VkResult add_extensions(util::vector<util::unique_ptr<swapchain_image_create_info_extension>> &extensions);
VkResult add_extension(util::unique_ptr<image_create_info_extension> extension);
VkImageCreateInfo get_image_create_info() const
{
@ -81,7 +86,7 @@ private:
/**
* @brief Swapchain image extensions needed for extending image create info.
*/
util::vector<util::unique_ptr<swapchain_image_create_info_extension>> m_extensions;
util::vector<util::unique_ptr<image_create_info_extension>> m_extensions;
};
} /* namespace wsi */

View file

@ -43,11 +43,11 @@
#include "util/macros.hpp"
#include "wl_helpers.hpp"
#include <wsi/extensions/external_memory_extension.hpp>
#include <wsi/extensions/image_compression_control.hpp>
#include <wsi/extensions/swapchain_maintenance.hpp>
#include <wsi/extensions/present_id.hpp>
#include <wsi/swapchain_image_create_extensions/external_memory_extension.hpp>
#include <wsi/extensions/mutable_format_extension.hpp>
#include "present_timing_handler.hpp"
#include "present_id_wayland.hpp"
@ -59,6 +59,26 @@ namespace wsi
namespace wayland
{
class wayland_image_data : public swapchain_image_data
{
public:
wayland_image_data(wayland::wayland_owner<wl_buffer> buffer)
: m_buffer(std::move(buffer))
{
assert(m_buffer != nullptr);
}
~wayland_image_data() override = default;
wl_buffer *get_buffer()
{
return m_buffer.get();
}
private:
wayland::wayland_owner<wl_buffer> m_buffer;
};
swapchain::swapchain(layer::device_private_data &dev_data, const VkAllocationCallbacks *pAllocator,
surface &wsi_surface)
: swapchain_base(dev_data, pAllocator)
@ -67,7 +87,7 @@ swapchain::swapchain(layer::device_private_data &dev_data, const VkAllocationCal
, m_wsi_surface(&wsi_surface)
, m_buffer_queue(nullptr)
, m_wsi_allocator(nullptr)
, m_image_creation_parameters({}, m_allocator, {}, {})
, m_image_factory(m_allocator, m_device_data)
{
}
@ -84,14 +104,7 @@ swapchain::~swapchain()
VkResult swapchain::add_required_extensions(VkDevice device, const VkSwapchainCreateInfoKHR *swapchain_create_info)
{
auto compression_control = wsi_ext_image_compression_control::create(device, swapchain_create_info);
if (compression_control)
{
if (!add_swapchain_extension(m_allocator.make_unique<wsi_ext_image_compression_control>(*compression_control)))
{
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
}
UNUSED(device);
if (m_device_data.is_present_id_enabled() ||
(swapchain_create_info->flags & VK_SWAPCHAIN_CREATE_PRESENT_ID_2_BIT_KHR))
@ -184,18 +197,6 @@ VkResult swapchain::init_platform(VkDevice device, const VkSwapchainCreateInfoKH
return VK_ERROR_INITIALIZATION_FAILED;
}
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 or VK_PRESENT_MODE_FIFO_LATEST_READY_EXT has
* been chosen by the application we don't initialize the page flip thread
@ -218,6 +219,56 @@ VkResult swapchain::init_platform(VkDevice device, const VkSwapchainCreateInfoKH
}
#endif
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.value()));
if (m_wsi_allocator == nullptr)
{
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
return init_image_factory(*swapchain_create_info);
}
VkResult swapchain::init_image_factory(const VkSwapchainCreateInfoKHR &swapchain_create_info)
{
std::variant<VkResult, util::unique_ptr<vulkan_image_handle_creator>> image_handle_creator_result =
create_image_creator(swapchain_create_info);
if (auto error = std::get_if<VkResult>(&image_handle_creator_result))
{
return *error;
}
auto image_handle_creator =
std::get<util::unique_ptr<vulkan_image_handle_creator>>(std::move(image_handle_creator_result));
auto compression_control = image_create_compression_control::create(m_device, &swapchain_create_info);
auto sc_img_create_ext_mem_result = swapchain_image_create_external_memory::create(
image_handle_creator->get_image_create_info(), compression_control, *m_wsi_allocator,
m_wsi_surface->get_formats(), m_device_data.physical_device, m_allocator);
if (auto error = std::get_if<VkResult>(&sc_img_create_ext_mem_result))
{
return *error;
}
auto sc_img_create_ext_mem =
std::get<util::unique_ptr<swapchain_image_create_external_memory>>(std::move(sc_img_create_ext_mem_result));
auto external_image_create_info = sc_img_create_ext_mem->get_external_image_create_info();
TRY_LOG_CALL(image_handle_creator->add_extension(std::move(sc_img_create_ext_mem)));
wsialloc_create_info_args wsialloc_args = { external_image_create_info.selected_format,
external_image_create_info.flags, external_image_create_info.extent,
external_image_create_info.explicit_compression };
auto backing_memory_creator =
m_allocator.make_unique<external_image_backing_memory_creator>(m_device_data, *m_wsi_allocator, wsialloc_args);
m_image_factory.init(std::move(image_handle_creator), std::move(backing_memory_creator), true, false);
return VK_SUCCESS;
}
@ -232,8 +283,8 @@ void swapchain::release_buffer(struct wl_buffer *wayl_buffer)
uint32_t i;
for (i = 0; i < m_swapchain_images.size(); i++)
{
auto data = reinterpret_cast<wayland_image_data *>(m_swapchain_images[i].data);
if (data && data->buffer == wayl_buffer)
auto data = m_swapchain_images[i].get_data<wayland_image_data>();
if (data && data->get_buffer() == wayl_buffer)
{
#if VULKAN_WSI_LAYER_EXPERIMENTAL
/* Some compositors might not deliver wp_presentation_feedback events if the images are pushed to compositor quick enough
@ -262,245 +313,41 @@ void swapchain::release_buffer(struct wl_buffer *wayl_buffer)
static struct wl_buffer_listener buffer_listener = { buffer_release };
VkResult swapchain::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)
wayland_owner<wl_buffer> swapchain::create_wl_buffer(image_backing_memory_external &image_external_memory)
{
TRY_LOG(util::get_drm_format_properties(m_device_data.physical_device, info.format, drm_format_props),
"Failed to get format properties");
auto image_create_info = get_image_factory().get_image_handle_creator().get_image_create_info();
assert(image_create_info.extent.width <= INT32_MAX);
assert(image_create_info.extent.height <= INT32_MAX);
for (const auto &prop : drm_format_props)
{
bool is_supported = false;
util::drm::drm_format_pair drm_format{ util::drm::vk_to_drm_format(info.format), prop.drmFormatModifier };
external_memory &ext_memory = image_external_memory.get_external_memory();
for (const auto &format : m_wsi_surface->get_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;
{
/* 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 = {};
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;
/* 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<VkImageFormatListCreateInfo>(
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<wsi_ext_image_compression_control>();
if (ext)
{
compression_control = ext->get_compression_control_properties();
compression_control.pNext = image_info.pNext;
image_info.pNext = &compression_control;
}
}
result = m_device_data.instance_data.disp.GetPhysicalDeviceImageFormatProperties2KHR(
m_device_data.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;
}
VkResult swapchain::allocate_wsialloc(VkImageCreateInfo &image_create_info, wayland_image_data *image_data,
util::vector<wsialloc_format> &importable_formats,
wsialloc_format *allocated_format, bool avoid_allocation)
{
bool enable_fixed_rate = false;
if (m_device_data.is_swapchain_compression_control_enabled())
{
auto *ext = get_swapchain_extension<wsi_ext_image_compression_control>();
/* For Image compression control, additional requirements to be satisfied such as
existence of VkImageCompressionControlEXT in swaphain_create_info for
the ext to be added to the list. so we check whether we got a valid pointer
and proceed if yes. */
if (ext)
{
if (ext->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, avoid_allocation };
wsialloc_allocate_result alloc_result = { {}, { 0 }, { 0 }, { -1 }, false };
TRY(m_wsi_allocator->allocate(params, &alloc_result));
*allocated_format = alloc_result.format;
auto &external_memory = image_data->external_mem;
external_memory.set_strides(alloc_result.average_row_strides);
external_memory.set_buffer_fds(alloc_result.buffer_fds);
external_memory.set_offsets(alloc_result.offsets);
uint32_t num_planes = util::drm::drm_fourcc_format_get_num_planes(alloc_result.format.fourcc);
if (!avoid_allocation)
{
uint32_t num_memory_planes = 0;
for (uint32_t i = 0; i < num_planes; ++i)
{
auto it = std::find(std::begin(alloc_result.buffer_fds) + i + 1, std::end(alloc_result.buffer_fds),
alloc_result.buffer_fds[i]);
if (it == std::end(alloc_result.buffer_fds))
{
num_memory_planes++;
}
}
assert(alloc_result.is_disjoint == (num_memory_planes > 1));
external_memory.set_num_memories(num_memory_planes);
}
external_memory.set_format_info(alloc_result.is_disjoint, num_planes);
external_memory.set_memory_handle_type(VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_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));
auto &m_allocated_format = m_image_creation_parameters.m_allocated_format;
if (!importable_formats.try_push_back(m_allocated_format))
{
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
TRY_LOG_CALL(allocate_wsialloc(m_image_create_info, image_data, importable_formats, &m_allocated_format, false));
return VK_SUCCESS;
}
uint64_t swapchain::get_modifier()
{
return m_image_creation_parameters.m_allocated_format.modifier;
}
VkResult swapchain::create_wl_buffer(const VkImageCreateInfo &image_create_info, swapchain_image &image,
wayland_image_data *image_data)
{
/* create a wl_buffer using the dma_buf protocol */
zwp_linux_buffer_params_v1 *params = zwp_linux_dmabuf_v1_create_params(m_wsi_surface->get_dmabuf_interface());
uint32_t modifier_hi = m_image_creation_parameters.m_allocated_format.modifier >> 32;
uint32_t modifier_low = m_image_creation_parameters.m_allocated_format.modifier & 0xFFFFFFFF;
for (uint32_t plane = 0; plane < image_data->external_mem.get_num_planes(); plane++)
uint32_t modifier_hi = image_external_memory.get_image_create_info().selected_format.modifier >> 32;
uint32_t modifier_low = image_external_memory.get_image_create_info().selected_format.modifier & 0xFFFFFFFF;
for (uint32_t plane = 0; plane < ext_memory.get_num_planes(); plane++)
{
zwp_linux_buffer_params_v1_add(params, image_data->external_mem.get_buffer_fds()[plane], plane,
image_data->external_mem.get_offsets()[plane],
image_data->external_mem.get_strides()[plane], modifier_hi, modifier_low);
zwp_linux_buffer_params_v1_add(params, ext_memory.get_buffer_fds()[plane], plane, ext_memory.get_offsets()[plane],
ext_memory.get_strides()[plane], modifier_hi, modifier_low);
}
const auto fourcc = util::drm::vk_to_drm_format(image_create_info.format);
assert(image_create_info.extent.width <= INT32_MAX);
assert(image_create_info.extent.height <= INT32_MAX);
image_data->buffer = zwp_linux_buffer_params_v1_create_immed(params, image_create_info.extent.width,
auto buffer = zwp_linux_buffer_params_v1_create_immed(params, image_create_info.extent.width,
image_create_info.extent.height, fourcc, 0);
zwp_linux_buffer_params_v1_destroy(params);
wl_proxy_set_queue(reinterpret_cast<wl_proxy *>(image_data->buffer), m_buffer_queue);
auto res = wl_buffer_add_listener(image_data->buffer, &buffer_listener, this);
wl_proxy_set_queue(reinterpret_cast<wl_proxy *>(buffer), m_buffer_queue);
auto res = wl_buffer_add_listener(buffer, &buffer_listener, this);
if (res < 0)
{
destroy_image(image);
return VK_ERROR_INITIALIZATION_FAILED;
}
return VK_SUCCESS;
wl_buffer_destroy(buffer);
return nullptr;
}
VkResult swapchain::allocate_and_bind_swapchain_image(VkImageCreateInfo image_create_info, swapchain_image &image)
return wayland_owner<wl_buffer>(buffer);
}
VkResult swapchain::allocate_and_bind_swapchain_image(swapchain_image &image)
{
util::unique_lock<util::recursive_mutex> image_status_lock(m_image_status_mutex);
if (!image_status_lock)
@ -508,81 +355,33 @@ VkResult swapchain::allocate_and_bind_swapchain_image(VkImageCreateInfo image_cr
WSI_LOG_ERROR("Failed to acquire mutex lock in allocate_and_bind_swapchain_image.\n");
return VK_ERROR_INITIALIZATION_FAILED;
}
image.status = swapchain_image::FREE;
assert(image.data != nullptr);
auto image_data = static_cast<wayland_image_data *>(image.data);
TRY_LOG(allocate_image(image_data), "Failed to allocate image");
image_status_lock.unlock();
auto &backing_memory = swapchain_image_factory::get_backing_memory_from_image<image_backing_memory_external>(image);
TRY_LOG_CALL(backing_memory.allocate());
TRY_LOG(create_wl_buffer(image_create_info, image, image_data), "Failed to create wl_buffer");
TRY_LOG(image_data->external_mem.import_memory_and_bind_swapchain_image(image.image),
"Failed to import memory and bind swapchain image");
/* Initialize presentation fence. */
auto present_fence = sync_fd_fence_sync::create(m_device_data);
if (!present_fence.has_value())
wayland_owner<wl_buffer> buffer = nullptr;
buffer = create_wl_buffer(backing_memory);
if (buffer == nullptr)
{
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
image_data->present_fence = std::move(present_fence.value());
return VK_SUCCESS;
WSI_LOG_ERROR("Failed to create wl_buffer");
return VK_ERROR_INITIALIZATION_FAILED;
}
VkResult swapchain::create_swapchain_image(VkImageCreateInfo image_create_info, swapchain_image &image)
{
/* Create image_data */
auto image_data = m_allocator.create<wayland_image_data>(1, m_device, m_allocator);
TRY_LOG_CALL(backing_memory.import_and_bind(image.get_image()));
auto image_data = m_allocator.make_unique<wayland_image_data>(std::move(buffer));
if (image_data == nullptr)
{
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
image.data = image_data;
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 modifiers. */
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;
}
wsialloc_format allocated_format = { 0, 0, 0 };
TRY_LOG_CALL(allocate_wsialloc(image_create_info, image_data, importable_formats, &allocated_format, true));
for (auto &prop : drm_format_props)
{
if (prop.drmFormatModifier == allocated_format.modifier)
{
image_data->external_mem.set_num_memories(prop.drmFormatModifierPlaneCount);
}
}
m_image_creation_parameters.m_allocated_format = allocated_format;
}
return m_device_data.disp.CreateImage(m_device, &image_create_info, get_allocation_callbacks(), &image.image);
image.set_data(std::move(image_data));
return VK_SUCCESS;
}
void swapchain::present_image(const pending_present_request &pending_present)
{
int res;
wayland_image_data *image_data =
reinterpret_cast<wayland_image_data *>(m_swapchain_images[pending_present.image_index].data);
auto &image = m_swapchain_images[pending_present.image_index];
auto image_data = image.get_data<wayland_image_data>();
/* if a frame is already pending, wait for a hint to present again */
if (!m_wsi_surface->wait_next_frame_event())
@ -590,9 +389,9 @@ void swapchain::present_image(const pending_present_request &pending_present)
set_error_state(VK_ERROR_SURFACE_LOST_KHR);
}
wl_surface_attach(m_surface, image_data->buffer, 0, 0);
wl_surface_attach(m_surface, image_data->get_buffer(), 0, 0);
auto present_sync_fd = image_data->present_fence.export_sync_fd();
auto present_sync_fd = static_cast<sync_fd_fence_sync *>(image.get_present_fence())->export_sync_fd();
if (!present_sync_fd.has_value())
{
WSI_LOG_ERROR("Failed to export present fence.");
@ -659,7 +458,7 @@ void swapchain::present_image(const pending_present_request &pending_present)
#endif
wl_surface_commit(m_surface);
res = wl_display_flush(m_display);
int res = wl_display_flush(m_display);
if (res < 0)
{
WSI_LOG_ERROR("error flushing the display");
@ -684,44 +483,11 @@ void swapchain::present_image(const pending_present_request &pending_present)
#endif
}
void swapchain::destroy_image(swapchain_image &image)
{
util::unique_lock<util::recursive_mutex> image_status_lock(m_image_status_mutex);
if (!image_status_lock)
{
WSI_LOG_ERROR("Failed to acquire mutex lock in destroy_image.\n");
abort();
}
if (image.status != swapchain_image::INVALID)
{
if (image.image != VK_NULL_HANDLE)
{
m_device_data.disp.DestroyImage(m_device, image.image, get_allocation_callbacks());
image.image = VK_NULL_HANDLE;
}
image.status = swapchain_image::INVALID;
}
image_status_lock.unlock();
if (image.data != nullptr)
{
auto image_data = reinterpret_cast<wayland_image_data *>(image.data);
if (image_data->buffer != nullptr)
{
wl_buffer_destroy(image_data->buffer);
}
m_allocator.destroy(1, image_data);
image.data = nullptr;
}
}
bool swapchain::free_image_found()
{
for (auto &img : m_swapchain_images)
{
if (img.status == swapchain_image::FREE)
if (img.get_status() == swapchain_image::FREE)
{
return true;
}
@ -773,44 +539,33 @@ VkResult swapchain::get_free_buffer(uint64_t *timeout)
}
}
VkResult swapchain::image_set_present_payload(swapchain_image &image, VkQueue queue,
const queue_submit_semaphores &semaphores, const void *submission_pnext)
std::variant<VkResult, util::unique_ptr<vulkan_image_handle_creator>> swapchain::create_image_creator(
const VkSwapchainCreateInfoKHR &swapchain_create_info)
{
auto image_data = reinterpret_cast<wayland_image_data *>(image.data);
return image_data->present_fence.set_payload(queue, semaphores, submission_pnext);
}
VkResult swapchain::image_wait_present(swapchain_image &, uint64_t)
{
/* With explicit sync in use there is no need to wait for the present sync before submiting the image to the
* compositor. */
return VK_SUCCESS;
}
VkResult swapchain::bind_swapchain_image(VkDevice &device, const VkBindImageMemoryInfo *bind_image_mem_info,
const VkBindImageMemorySwapchainInfoKHR *bind_sc_info)
{
UNUSED(device);
const wsi::swapchain_image &swapchain_image = m_swapchain_images[bind_sc_info->imageIndex];
auto image_data = reinterpret_cast<wayland_image_data *>(swapchain_image.data);
return image_data->external_mem.bind_swapchain_image_memory(bind_image_mem_info->image);
}
VkResult swapchain::get_required_image_creator_extensions(
const VkSwapchainCreateInfoKHR &swapchain_create_info,
util::vector<util::unique_ptr<swapchain_image_create_info_extension>> *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)))
auto image_handle_creator = m_allocator.make_unique<vulkan_image_handle_creator>(m_allocator, swapchain_create_info);
if (image_handle_creator == nullptr)
{
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
return VK_SUCCESS;
if (is_mutable_format_enabled())
{
const auto *image_format_list = util::find_extension<VkImageFormatListCreateInfo>(
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;
}
TRY_LOG_CALL(image_handle_creator->add_extension(std::move(mutable_format_uptr)));
}
return image_handle_creator;
}
swapchain_image_factory &swapchain::get_image_factory()
{
return m_image_factory;
}
} // namespace wayland

View file

@ -43,6 +43,7 @@ extern "C" {
#include <wsi/wsi_alloc_utils.hpp>
#include <wsi/image_backing_memory_external.hpp>
#include <wsi/external_memory.hpp>
namespace wsi
@ -50,37 +51,6 @@ namespace wsi
namespace wayland
{
struct wayland_image_data
{
wayland_image_data(const VkDevice &device, const util::allocator &allocator)
: external_mem(device, allocator)
, buffer(nullptr)
{
}
external_memory external_mem;
wl_buffer *buffer;
sync_fd_fence_sync present_fence;
};
struct image_creation_parameters
{
wsialloc_format m_allocated_format;
util::vector<VkSubresourceLayout> m_image_layout;
VkExternalMemoryImageCreateInfoKHR m_external_info;
VkImageDrmFormatModifierExplicitCreateInfoEXT m_drm_mod_info;
image_creation_parameters(wsialloc_format allocated_format, util::allocator allocator,
VkExternalMemoryImageCreateInfoKHR external_info,
VkImageDrmFormatModifierExplicitCreateInfoEXT drm_mod_info)
: m_allocated_format(allocated_format)
, m_image_layout(allocator)
, m_external_info(external_info)
, m_drm_mod_info(drm_mod_info)
{
}
};
class swapchain : public wsi::swapchain_base
{
public:
@ -94,32 +64,35 @@ public:
protected:
/**
* @brief Initialize platform specifics.
* @brief Platform specific initialization
*
* @param device VkDevice object.
* @param swapchain_create_info Pointer to the swapchain create info struct.
* @param[out] use_presentation_thread Flag indicating if image presentation
* must happen in a separate thread.
*
* @return VK_SUCCESS on success or an error code otherwise.
*/
VkResult init_platform(VkDevice device, const VkSwapchainCreateInfoKHR *swapchain_create_info,
bool &use_presentation_thread) override;
/**
* @brief Initalize backend specific image factory.
*
* @param swapchain_create_info Swapchain create info.
* @param image_factory Image factory to initalize.
* @return Vulkan result code.
*/
VkResult init_image_factory(const VkSwapchainCreateInfoKHR &swapchain_create_info);
/**
* @brief Allocates and binds a new swapchain image.
*
* @param image_create_info Data to be used to create the image.
* @param image Handle to the image.
* @param swapchain_image Swapchain image.
*
* @return Returns VK_SUCCESS on success, otherwise an appropriate error code.
*/
VkResult allocate_and_bind_swapchain_image(VkImageCreateInfo image_create_info, swapchain_image &image) override;
/**
* @brief Creates a new swapchain image.
*
* @param image_create_info Data to be used to create the image.
* @param image Handle to the image.
*
* @return If image creation is successful returns VK_SUCCESS, otherwise
* will return VK_ERROR_OUT_OF_DEVICE_MEMORY or VK_ERROR_INITIALIZATION_FAILED
* depending on the error that occurred.
*/
VkResult create_swapchain_image(VkImageCreateInfo image_create_info, swapchain_image &image) override;
VkResult allocate_and_bind_swapchain_image(swapchain_image &image) override;
/**
* @brief Method to present and image
@ -130,13 +103,6 @@ protected:
*/
void present_image(const pending_present_request &pending_present) override;
/**
* @brief Method to release a swapchain image
*
* @param image Handle to the image about to be released.
*/
void destroy_image(swapchain_image &image) override;
/**
* @brief Method to check if there are any free images
*
@ -156,52 +122,20 @@ protected:
VkResult get_free_buffer(uint64_t *timeout) override;
/**
* @brief Sets the present payload for a swapchain image.
* @brief Get the image factory used for creating swapchain images.
*
* @param[in] image The swapchain image for which to set a present payload.
* @param queue A Vulkan queue that can be used for any Vulkan commands needed.
* @param[in] sem_payload Array of Vulkan semaphores that constitute the payload.
* @param[in] submission_pnext Chain of pointers to attach to the payload submission.
*
* @return VK_SUCCESS on success or an error code otherwise.
* @return Swapchain image factory.
*/
VkResult image_set_present_payload(swapchain_image &image, VkQueue queue, const queue_submit_semaphores &semaphores,
const void *submission_pnext) override;
VkResult image_wait_present(swapchain_image &image, uint64_t timeout) override;
/**
* @brief Bind image to a swapchain
*
* @param device is the logical device that owns the images and memory.
* @param bind_image_mem_info details the image we want to bind.
* @param bind_sc_info describes the swapchain memory to bind to.
*
* @return VK_SUCCESS on success, otherwise on failure VK_ERROR_OUT_OF_HOST_MEMORY or VK_ERROR_OUT_OF_DEVICE_MEMORY
* can be returned.
*/
VkResult bind_swapchain_image(VkDevice &device, const VkBindImageMemoryInfo *bind_image_mem_info,
const VkBindImageMemorySwapchainInfoKHR *bind_sc_info) override;
uint64_t get_modifier() override;
/**
* @brief Get backend specific image create info extensions.
*
* @param swapchain_create_info Swapchain create info.
* @param[out] extensions Backend specific swapchain image create info extensions.
*/
VkResult get_required_image_creator_extensions(
const VkSwapchainCreateInfoKHR &swapchain_create_info,
util::vector<util::unique_ptr<swapchain_image_create_info_extension>> *extensions) override;
swapchain_image_factory &get_image_factory() override;
private:
VkResult create_wl_buffer(const VkImageCreateInfo &image_create_info, swapchain_image &image,
wayland_image_data *image_data);
VkResult allocate_image(wayland_image_data *image_data);
VkResult allocate_wsialloc(VkImageCreateInfo &image_create_info, wayland_image_data *image_data,
util::vector<wsialloc_format> &importable_formats, wsialloc_format *allocated_format,
bool avoid_allocation);
/**
* @brief Create a Wayland buffer image for the specified @p image
*
* @param image_external_memory Image external memory
* @return Wayland buffer object or nullptr if there was a failure.
*/
wayland_owner<wl_buffer> create_wl_buffer(image_backing_memory_external &image_external_memory);
/**
* @brief Adds required extensions to the extension list of the swapchain
@ -212,6 +146,15 @@ private:
*/
VkResult add_required_extensions(VkDevice device, const VkSwapchainCreateInfoKHR *swapchain_create_info) override;
/**
* @brief Create the image creator with required extensions.
*
* @param swapchain_create_info VkSwapchainCreateInfoKHR passed by the application.
* @return If error occurred, returns VkResult, vulkan_image_handle_creator handle otherwise.
*/
std::variant<VkResult, util::unique_ptr<vulkan_image_handle_creator>> create_image_creator(
const VkSwapchainCreateInfoKHR &swapchain_create_info);
struct wl_display *m_display;
struct wl_surface *m_surface;
/** Raw pointer to the WSI Surface that this swapchain was created from. The Vulkan specification ensures that the
@ -222,28 +165,14 @@ private:
struct wl_event_queue *m_buffer_queue;
/**
* @brief Handle to the WSI allocator.
* @brief WSIAllocator instance.
*/
util::unique_ptr<swapchain_wsialloc_allocator> m_wsi_allocator;
/**
* @brief Image creation parameters used for all swapchain images.
* @brief Image factory that is used to create swapchain images.
*/
struct image_creation_parameters m_image_creation_parameters;
/**
* @brief Finds what formats are compatible with the requested swapchain image Vulkan Device and Wayland surface.
*
* @param info The Swapchain image creation info.
* @param[out] importable_formats A list of formats that can be imported to the Vulkan Device.
* @param[out] exportable_formats A list of formats that can be exported from the Vulkan Device.
*
* @return VK_SUCCESS or VK_ERROR_OUT_OF_HOST_MEMORY
*/
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);
swapchain_image_factory m_image_factory;
};
} // namespace wayland

View file

@ -79,6 +79,11 @@ static inline void wayland_object_destroy(struct wp_presentation_feedback *obj)
wp_presentation_feedback_destroy(obj);
}
static inline void wayland_object_destroy(wl_buffer *obj)
{
wl_buffer_destroy(obj);
}
template <typename T>
struct wayland_deleter
{

View file

@ -60,9 +60,8 @@ VkResult swapchain_wsialloc_allocator::allocate(const allocation_params &input,
allocation_flags |= WSIALLOC_ALLOCATE_HIGHEST_FIXED_RATE_COMPRESSION;
}
wsialloc_allocate_info alloc_info = { input.importable_formats.data(),
static_cast<unsigned>(input.importable_formats.size()), input.extent.width,
input.extent.height, allocation_flags };
wsialloc_allocate_info alloc_info = { input.importable_formats, static_cast<unsigned>(input.importable_formats_size),
input.extent.width, input.extent.height, allocation_flags };
/* Clear buffer_fds and average_row_strides for error purposes */
for (int i = 0; i < WSIALLOC_MAX_PLANES; ++i)

View file

@ -43,7 +43,8 @@ struct allocation_params
{
bool is_protected_memory;
VkExtent3D extent;
util::vector<wsialloc_format> &importable_formats;
wsialloc_format *importable_formats;
size_t importable_formats_size;
bool enable_fixed_rate;
bool avoid_allocation;
};