🔒 Repository is read-only – file editing is disabled.

PaganDE/smithay-0.7.0/src/backend/allocator/vulkan/mod.rs main

814 linii Raw ← Powrót
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814
//! Module for Buffers created using Vulkan.
//!
//! The [`VulkanAllocator`] type implements the [`Allocator`] trait and [`VulkanImage`] implements [`Buffer`].
//! A [`VulkanImage`] may be exported as a [dmabuf](super::dmabuf).
//!
//! The Vulkan allocator supports up to Vulkan 1.3.
//!
//! The Vulkan allocator requires the following device extensions (and their dependencies):
//! - `VK_EXT_image_drm_format_modifier`
//! - `VK_EXT_external_memory_dmabuf`
//! - `VK_KHR_external_memory_fd`
//!
//! Additionally the Vulkan allocator may enable the following extensions if available:
//! - `VK_EXT_4444_formats`
//!
//! To get the required extensions a device must support to use the Vulkan allocator, use
//! [`VulkanAllocator::required_extensions`].

#![forbid(unsafe_op_in_unsafe_fn)]

pub mod format;

use std::{
    ffi::CStr,
    fmt,
    os::unix::io::{FromRawFd, OwnedFd},
    sync::{mpsc, Arc, Weak},
};

use ash::{ext, khr, vk};
use bitflags::bitflags;
use drm_fourcc::{DrmFormat, DrmFourcc, DrmModifier};
use tracing::instrument;

#[cfg(feature = "backend_drm")]
use crate::backend::drm::DrmNode;
use crate::{
    backend::{
        allocator::dmabuf::DmabufFlags,
        vulkan::{version::Version, PhysicalDevice},
    },
    utils::{Buffer as BufferCoord, Size},
};

use super::{
    dmabuf::{AsDmabuf, Dmabuf, MAX_PLANES},
    Allocator, Buffer,
};

bitflags! {
    /// Flags to indicate the intended usage for a buffer.
    ///
    /// The usage flags may influence whether a buffer with a specific format and modifier combination may
    /// successfully be imported by a graphics api when it is exported as a [`Dmabuf`].
    ///
    /// For example, if a [`Buffer`] is used for scan-out, it is only necessary to specify the color
    /// attachment usage. However, if the exported buffer is used by a client and intended to be rendered to,
    /// then it is possible that the import will fail because the buffer was not allocated with right usage
    /// for the format and modifier combination.
    ///
    /// The default usage set when creating a [`VulkanAllocator`] guarantees that an exported buffer may be
    /// imported successfully with the same usage.
    ///
    /// If you need to allocate buffers with different usages dynamically, then you may use
    /// [`VulkanAllocator::create_buffer_with_usage`].
    ///
    /// [`VulkanAllocator::is_format_supported`] can check if the combination of format, modifier and usage
    /// flags are supported.
    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
    pub struct ImageUsageFlags: vk::Flags {
        /// The image may be the source of a transfer command.
        ///
        /// This allows the content of the exported buffer to be downloaded from the GPU.
        const TRANSFER_SRC = vk::ImageUsageFlags::TRANSFER_SRC.as_raw();

        /// The image may be the destination of a transfer command.
        ///
        /// This allows the content of the exported buffer to be modified by a memory upload to the GPU.
        const TRANSFER_DST = vk::ImageUsageFlags::TRANSFER_DST.as_raw();

        /// Image may be sampled in a shader.
        ///
        /// This should be used if the exported buffer will be used as a texture.
        const SAMPLED = vk::ImageUsageFlags::SAMPLED.as_raw();

        /// The image may be used in a color attachment.
        ///
        /// This should be used if the exported buffer will be rendered to.
        const COLOR_ATTACHMENT = vk::ImageUsageFlags::COLOR_ATTACHMENT.as_raw();
    }
}

/// Error type for [`VulkanAllocator`].
#[derive(Debug, thiserror::Error)]
pub enum Error {
    /// The allocator could not be created.
    ///
    /// This can occur for a few reasons:
    /// - No suitable queue family was found.
    #[error("could not create allocator")]
    Setup,

    /// The size specified to create the buffer was too small.
    ///
    /// The size must be greater than `0 x 0` pixels.
    #[error("invalid buffer size")]
    InvalidSize,

    /// The specified format is not supported.
    ///
    /// This error may occur for a few reasons:
    /// 1. There is no equivalent Vulkan format for the specified format code.
    /// 2. The driver does not support any of the specified modifiers for the format code.
    /// 3. The driver does support some of the format, but the size of the buffer requested is too large.
    /// 4. The usage is empty
    #[error("format is not supported")]
    UnsupportedFormat,

    /// Some error from the Vulkan driver.
    #[error(transparent)]
    Vk(#[from] vk::Result),
}

/// An allocator which uses Vulkan to create buffers.
pub struct VulkanAllocator {
    formats: Vec<FormatEntry>,
    images: Vec<ImageInner>,
    default_usage: ImageUsageFlags,
    remaining_allocations: u32,
    extension_fns: ExtensionFns,
    dropped_recv: mpsc::Receiver<ImageInner>,
    dropped_sender: mpsc::Sender<ImageInner>,
    phd: PhysicalDevice,
    #[cfg(feature = "backend_drm")]
    node: Option<DrmNode>,
    device: Arc<ash::Device>,
}

impl fmt::Debug for VulkanAllocator {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("VulkanAllocator")
            .field("formats", &self.formats)
            .field("images", &self.images)
            .field("default_usage", &self.default_usage)
            .field("remaining_allocations", &self.remaining_allocations)
            .field("dropped_recv", &self.dropped_recv)
            .field("dropped_sender", &self.dropped_sender)
            .field("phd", &self.phd)
            .finish()
    }
}

impl VulkanAllocator {
    /// Maximum supported version instance version that may be used with the allocator.
    pub const MAX_INSTANCE_VERSION: Version = Version::VERSION_1_3;

    /// Returns the list of device extensions required by the Vulkan allocator.
    ///
    /// This function may return a different list for each [`PhysicalDevice`], meaning each device should be
    /// filtered using it's own call to this function.
    pub fn required_extensions(phd: &PhysicalDevice) -> Vec<&'static CStr> {
        // Always required extensions
        let mut extensions = vec![
            ext::image_drm_format_modifier::NAME,
            ext::external_memory_dma_buf::NAME,
            khr::external_memory_fd::NAME,
        ];

        if phd.api_version() < Version::VERSION_1_2 {
            // VK_EXT_image_drm_format_modifier requires VK_KHR_image_format_list.
            // VK_KHR_image_format_list is part of the core API in Vulkan 1.2
            extensions.push(khr::image_format_list::NAME);
        }

        // Optional extensions:

        // VK_EXT_4444_formats is part of the core API in Vulkan 1.3. Although not always supported
        // (see the 1.3 features to enable)
        if phd.api_version() < Version::VERSION_1_3 {
            // In 1.2 and below, the device must support the extension to use it.
            if phd.has_device_extension(ext::_4444_formats::NAME) {
                extensions.push(ext::_4444_formats::NAME);
            }
        }

        extensions
    }

    /// Creates a [`VulkanAllocator`].
    ///
    /// # Panics
    ///
    /// - If the version of instance which created the [`PhysicalDevice`] is higher than [`VulkanAllocator::MAX_INSTANCE_VERSION`].
    /// - If the default [`ImageUsageFlags`] are empty.
    #[instrument(err, skip(phd), fields(physical_device = phd.name()))]
    pub fn new(phd: &PhysicalDevice, default_usage: ImageUsageFlags) -> Result<VulkanAllocator, Error> {
        // Panic if the instance version is too high
        if phd.instance().api_version() > Self::MAX_INSTANCE_VERSION {
            panic!("Exceeded maximum instance api version for VulkanAllocator (1.3 max)")
        }

        // VUID-VkPhysicalDeviceImageFormatInfo2-usage-requiredbitmask
        // At least one image usage flag must be specified.
        if default_usage.is_empty() {
            panic!("Default usage flags for allocator are empty")
        }

        // Get required extensions
        let extensions = Self::required_extensions(phd);
        let extension_pointers = extensions.iter().copied().map(CStr::as_ptr).collect::<Vec<_>>();

        // We don't actually submit any commands to the queue, but Vulkan requires that we create devices with
        // at least one queue (VUID-VkDeviceCreateInfo-queueCreateInfoCount-arraylength).
        let queue_families = unsafe {
            phd.instance()
                .handle()
                .get_physical_device_queue_family_properties(phd.handle())
        };

        let queue_family_index = queue_families
            .iter()
            // Find a queue with transfer
            .position(|properties| properties.queue_flags.contains(vk::QueueFlags::TRANSFER))
            // If there is no transfer queue family, then try graphics (which must support transfer)
            .or_else(|| {
                queue_families
                    .iter()
                    .position(|properties| properties.queue_flags.contains(vk::QueueFlags::GRAPHICS))
            })
            .ok_or(Error::Setup)?;

        // TODO: Enable 4444 formats features
        let queue_create_info = [vk::DeviceQueueCreateInfo::default()
            .queue_family_index(queue_family_index as u32)
            .queue_priorities(&[0.0])];
        let create_info = vk::DeviceCreateInfo::default()
            .enabled_extension_names(&extension_pointers)
            .queue_create_infos(&queue_create_info);

        let instance = phd.instance().handle();
        let device = unsafe { instance.create_device(phd.handle(), &create_info, None) }?;

        // Load extension functions
        let extension_fns = ExtensionFns {
            ext_image_format_modifier: ext::image_drm_format_modifier::Device::new(instance, &device),
            khr_external_memory_fd: khr::external_memory_fd::Device::new(instance, &device),
        };

        let (dropped_sender, dropped_recv) = mpsc::channel();

        #[cfg(feature = "backend_drm")]
        let node = phd
            .render_node()
            .ok()
            .flatten()
            .or_else(|| phd.primary_node().ok().flatten());

        let mut allocator = VulkanAllocator {
            formats: Vec::new(),
            images: Vec::new(),
            default_usage,
            remaining_allocations: phd.limits().max_memory_allocation_count,
            extension_fns,
            dropped_recv,
            dropped_sender,
            phd: phd.clone(),
            #[cfg(feature = "backend_drm")]
            node,
            device: Arc::new(device),
        };

        allocator.init_formats();

        Ok(allocator)
    }

    /// Returns whether this allocator supports the specified format with the usage flags.
    pub fn is_format_supported(&self, format: DrmFormat, usage: ImageUsageFlags) -> bool {
        // VUID-VkPhysicalDeviceImageFormatInfo2-usage-requiredbitmask
        // At least one image usage flag must be specified.
        if usage.is_empty() {
            return false;
        }

        // TODO: Check if the extents are also valid?
        // Vulkan states a maximum extent size for images.
        // This may also be useful as a function on Allocator.
        unsafe { self.get_format_info(format, vk::ImageUsageFlags::from_raw(usage.bits())) }
            .ok()
            .is_some()
    }

    /// Try to create a buffer with the given dimensions, pixel format and usage flags.
    ///
    /// This may return [`Err`] for one of the following reasons:
    /// - The `usage` is empty.
    /// - The `fourcc` format is not supported.
    /// - All of the allowed `modifiers` are not supported.
    /// - The size of the buffer is too large for the `usage`, `fourcc` format or `modifiers`.
    /// - The `fourcc` format and `modifiers` do not support the specified usage.
    #[instrument(level = "trace", err)]
    #[profiling::function]
    pub fn create_buffer_with_usage(
        &mut self,
        width: u32,
        height: u32,
        fourcc: DrmFourcc,
        modifiers: &[DrmModifier],
        usage: ImageUsageFlags,
    ) -> Result<VulkanImage, Error> {
        self.cleanup();

        let vk_format = format::get_vk_format(fourcc).ok_or(Error::UnsupportedFormat)?;
        let vk_usage = vk::ImageUsageFlags::from_raw(usage.bits());

        // VUID-VkImageCreateInfo-extent-00944, VUID-VkImageCreateInfo-extent-00945
        if width == 0 || height == 0 {
            return Err(Error::InvalidSize);
        }

        // VUID-VkPhysicalDeviceImageFormatInfo2-usage-requiredbitmask
        // At least one image usage flag must be specified.
        if usage.is_empty() {
            return Err(Error::UnsupportedFormat);
        }

        // VUID-VkImageCreateInfo-usage-00964, VUID-VkImageCreateInfo-usage-00965
        if usage.contains(ImageUsageFlags::COLOR_ATTACHMENT) {
            let limits = self.phd.limits();

            if width > limits.max_framebuffer_width || height > limits.max_framebuffer_height {
                return Err(Error::InvalidSize);
            }
        }

        // Filter out any format + modifier combinations that are not supported
        let modifiers = self.filter_modifiers(width, height, vk_usage, fourcc, modifiers);

        // VUID-VkImageDrmFormatModifierListCreateInfoEXT-drmFormatModifierCount-arraylength
        if modifiers.is_empty() {
            return Err(Error::UnsupportedFormat);
        }

        Ok(unsafe { self.create_image(width, height, vk_format, vk_usage, fourcc, &modifiers[..]) }?)
    }

    /// Returns the [`PhysicalDevice`] this allocator was created with.
    pub fn physical_device(&self) -> &PhysicalDevice {
        &self.phd
    }
}

impl Allocator for VulkanAllocator {
    type Buffer = VulkanImage;
    type Error = Error;

    fn create_buffer(
        &mut self,
        width: u32,
        height: u32,
        fourcc: DrmFourcc,
        modifiers: &[DrmModifier],
    ) -> Result<VulkanImage, Self::Error> {
        self.create_buffer_with_usage(width, height, fourcc, modifiers, self.default_usage)
    }
}

impl Drop for VulkanAllocator {
    fn drop(&mut self) {
        unsafe {
            for image in &self.images {
                self.device.destroy_image(image.image, None);
                self.device.free_memory(image.memory, None);
            }

            self.device.destroy_device(None);
        }
    }
}

/// Vulkan image object.
///
/// This type implements [`Buffer`] and the underlying image may be exported as a dmabuf.
pub struct VulkanImage {
    inner: ImageInner,
    width: u32,
    height: u32,
    format: DrmFormat,
    #[cfg(feature = "backend_drm")]
    node: Option<DrmNode>,
    /// The number of planes the image has for dmabuf export.
    format_plane_count: u32,
    khr_external_memory_fd: khr::external_memory_fd::Device,
    dropped_sender: mpsc::Sender<ImageInner>,
    device: Weak<ash::Device>,
}

impl fmt::Debug for VulkanImage {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("VulkanImage")
            .field("width", &self.width)
            .field("height", &self.height)
            .field("format", &self.format)
            .field("inner", &self.inner)
            .finish()
    }
}

impl Buffer for VulkanImage {
    fn width(&self) -> u32 {
        self.width
    }

    fn height(&self) -> u32 {
        self.height
    }

    fn size(&self) -> Size<i32, BufferCoord> {
        (self.width as i32, self.height as i32).into()
    }

    fn format(&self) -> DrmFormat {
        self.format
    }
}

impl AsDmabuf for VulkanImage {
    type Error = ExportError;

    #[profiling::function]
    fn export(&self) -> Result<Dmabuf, Self::Error> {
        let device = self.device.upgrade().ok_or(ExportError::AllocatorDestroyed)?;

        // Implementation may be broken if the plane count is wrong.
        if self.format_plane_count == 0 {
            return Err(ExportError::Failed);
        }

        assert!(
            self.format_plane_count as usize <= MAX_PLANES,
            "Vulkan implementation reported too many planes"
        );

        let create_info = vk::MemoryGetFdInfoKHR::default()
            .handle_type(vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT)
            // VUID-VkMemoryGetFdInfoKHR-handleType-00671: Memory was allocated with DMA_BUF_EXT
            .memory(self.inner.memory);

        let fd = unsafe { self.khr_external_memory_fd.get_memory_fd(&create_info) }?;
        // SAFETY: `vkGetMemoryFdKHR` creates a new file descriptor owned by the caller.
        let fd = unsafe { OwnedFd::from_raw_fd(fd) };
        let mut builder = Dmabuf::builder(
            self.size(),
            self.format().code,
            self.format().modifier,
            DmabufFlags::empty(),
        );

        for idx in 0..self.format_plane_count {
            // get_image_subresource_layout only gets the layout of one memory plane. This mask specifies
            // which plane should the layout be obtained for.
            let aspect_mask = match idx {
                0 => vk::ImageAspectFlags::MEMORY_PLANE_0_EXT,
                1 => vk::ImageAspectFlags::MEMORY_PLANE_1_EXT,
                2 => vk::ImageAspectFlags::MEMORY_PLANE_2_EXT,
                3 => vk::ImageAspectFlags::MEMORY_PLANE_3_EXT,
                _ => unreachable!(),
            };

            // VUID-vkGetImageSubresourceLayout-image-02270: All allocate images are created with drm tiling
            let subresource = vk::ImageSubresource::default().aspect_mask(aspect_mask);
            let layout = unsafe { device.get_image_subresource_layout(self.inner.image, subresource) };
            builder.add_plane(
                fd.try_clone().or(Err(ExportError::Failed))?,
                idx,
                layout.offset as u32,
                layout.row_pitch as u32,
            );
        }

        #[cfg(feature = "backend_drm")]
        if let Some(node) = self.node {
            builder.set_node(node);
        }

        Ok(builder.build().unwrap())
    }
}

impl Drop for VulkanImage {
    fn drop(&mut self) {
        let _ = self.dropped_sender.send(self.inner);
    }
}

/// The error type for exporting a [`VulkanImage`] as a [`Dmabuf`].
#[derive(Debug, thiserror::Error)]
pub enum ExportError {
    /// The image could not export a dmabuf since the allocator has been destroyed.
    #[error("allocator has been destroyed")]
    AllocatorDestroyed,

    /// The allocator could not export a dmabuf for an implementation dependent reason.
    #[error("could not export a dmabuf")]
    Failed,

    /// Vulkan API error.
    #[error(transparent)]
    Vk(#[from] vk::Result),
}

#[derive(Debug, Clone, Copy, PartialEq)]
struct ImageInner {
    // TODO: image usage?
    image: vk::Image,
    /// The first entry will always have a non-null device memory handle.
    ///
    /// The other three entries may be a null handle or valid device memory (the latter with disjoint dmabufs).
    memory: vk::DeviceMemory,
}

#[derive(Debug)]
struct FormatEntry {
    format: DrmFormat,
    modifier_properties: vk::DrmFormatModifierPropertiesEXT,
}

struct ExtensionFns {
    /// Functions to get DRM format information.
    ///
    /// These are required, and therefore you may assume this functionality is available.
    ext_image_format_modifier: ext::image_drm_format_modifier::Device,
    /// Functions used for dmabuf import and export.
    ///
    /// If this is [`Some`], then the allocator will support dmabuf import and export operations.
    khr_external_memory_fd: khr::external_memory_fd::Device,
}

impl VulkanAllocator {
    fn init_formats(&mut self) {
        for &fourcc in format::known_formats() {
            let vk_format = format::get_vk_format(fourcc).unwrap();
            let modifier_properties = self
                .phd
                .get_format_modifier_properties(vk_format)
                .expect("The Vulkan allocator requires VK_EXT_image_drm_format_modifier");

            for modifier_properties in modifier_properties {
                self.formats.push(FormatEntry {
                    format: DrmFormat {
                        code: fourcc,
                        modifier: DrmModifier::from(modifier_properties.drm_format_modifier),
                    },
                    modifier_properties,
                });
            }
        }
    }

    /// Returns whether the format + modifier combination and the usage flags are supported.
    unsafe fn get_format_info(
        &self,
        format: DrmFormat,
        usage: vk::ImageUsageFlags,
    ) -> Result<Option<vk::ImageFormatProperties>, vk::Result> {
        let vk_format = format::get_vk_format(format.code);

        match vk_format {
            Some(vk_format) => {
                let mut image_drm_format_info = vk::PhysicalDeviceImageDrmFormatModifierInfoEXT::default()
                    .drm_format_modifier(format.modifier.into())
                    .sharing_mode(vk::SharingMode::EXCLUSIVE);
                let format_info = vk::PhysicalDeviceImageFormatInfo2::default()
                    .format(vk_format)
                    .ty(vk::ImageType::TYPE_2D)
                    .tiling(vk::ImageTiling::DRM_FORMAT_MODIFIER_EXT)
                    .usage(usage)
                    .flags(vk::ImageCreateFlags::empty())
                    // VUID-VkPhysicalDeviceImageFormatInfo2-tiling-02249
                    .push_next(&mut image_drm_format_info);
                let mut image_format_properties = vk::ImageFormatProperties2::default();

                // VUID-vkGetPhysicalDeviceImageFormatProperties-tiling-02248: Must use vkGetPhysicalDeviceImageFormatProperties2
                let result = unsafe {
                    self.phd
                        .instance()
                        .handle()
                        .get_physical_device_image_format_properties2(
                            self.phd.handle(),
                            &format_info,
                            &mut image_format_properties,
                        )
                };

                result
                    .map(|_| Some(image_format_properties.image_format_properties))
                    .or_else(|result| {
                        // Unsupported format + usage combination
                        if result == vk::Result::ERROR_FORMAT_NOT_SUPPORTED {
                            Ok(None)
                        } else {
                            Err(result)
                        }
                    })
            }

            None => Ok(None),
        }
    }

    fn filter_modifiers(
        &self,
        width: u32,
        height: u32,
        vk_usage: vk::ImageUsageFlags,
        fourcc: DrmFourcc,
        modifiers: &[DrmModifier],
    ) -> Vec<u64> {
        modifiers
            .iter()
            .copied()
            .filter_map(move |modifier| {
                let info = unsafe {
                    self.get_format_info(
                        DrmFormat {
                            code: fourcc,
                            modifier,
                        },
                        vk_usage,
                    )
                }
                .ok()
                .flatten()?;

                Some((modifier, info))
            })
            // Filter modifiers where the required image creation limits are not met
            .filter(move |(_, properties)| {
                let max_extent = properties.max_extent;

                // VUID-VkImageCreateInfo-extent-02252
                max_extent.width >= width
                // VUID-VkImageCreateInfo-extent-02253
                && max_extent.height >= height
                // VUID-VkImageCreateInfo-extent-02254
                // VUID-VkImageCreateInfo-extent-00946
                // VUID-VkImageCreateInfo-imageType-00957
                && max_extent.depth >= 1
                // VUID-VkImageCreateInfo-samples-02258
                && properties.sample_counts.contains(vk::SampleCountFlags::TYPE_1)
            })
            .map(|(modifier, _)| modifier)
            .map(Into::<u64>::into)
            // TODO: Could use a smallvec or tinyvec to reduce number allocations
            .collect::<Vec<_>>()
    }

    /// # Safety
    ///
    /// * The list of modifiers must be supported for the given format and image usage flags.
    /// * The extent of the image must be within the maximum extents Vulkan tells.
    unsafe fn create_image(
        &mut self,
        width: u32,
        height: u32,
        vk_format: vk::Format,
        vk_usage: vk::ImageUsageFlags,
        fourcc: DrmFourcc,
        modifiers: &[u64],
    ) -> Result<VulkanImage, vk::Result> {
        assert!(width > 0);
        assert!(height > 0);

        // Ensure maximum allocations are not exceeded.
        if self.remaining_allocations == 0 {
            todo!()
        }

        // Now that the list of valid modifiers is known, create an image using one of the modifiers.
        let mut modifier_list =
            vk::ImageDrmFormatModifierListCreateInfoEXT::default().drm_format_modifiers(modifiers);
        let mut external_memory_image_create_info = vk::ExternalMemoryImageCreateInfo::default()
            .handle_types(vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT);

        let image_create_info = vk::ImageCreateInfo::default()
            .image_type(vk::ImageType::TYPE_2D)
            .format(vk_format)
            .extent(vk::Extent3D {
                width,
                height,
                // VUID-VkImageCreateInfo-extent-00946
                // VUID-VkImageCreateInfo-imageType-00957
                depth: 1,
            })
            // VUID-VkImageCreateInfo-samples-parameter
            .samples(vk::SampleCountFlags::TYPE_1)
            // VUID-VkImageCreateInfo-mipLevels-00947
            .mip_levels(1)
            // VUID-VkImageCreateInfo-arrayLayers-00948
            .array_layers(1)
            // VUID-VkImageCreateInfo-pNext-02262
            .tiling(vk::ImageTiling::DRM_FORMAT_MODIFIER_EXT)
            // VUID-VkImageCreateInfo-usage-requiredbitmask
            // FIXME: We don't assert any usage flags are set
            .usage(vk_usage)
            // VUID-VkImageCreateInfo-initialLayout-00993
            .initial_layout(vk::ImageLayout::UNDEFINED)
            // VUID-VkImageCreateInfo-tiling-02261
            .push_next(&mut modifier_list)
            // TODO: VUID-VkImageCreateInfo-pNext-00990
            .push_next(&mut external_memory_image_create_info);

        // The image is placed in a scope guard to safely handle future allocation failures.
        let mut guard = scopeguard::guard(
            ImageInner {
                // This is the only spot where ? may be used to detect and error since no previous handles have been created.
                image: unsafe { self.device.create_image(&image_create_info, None) }?,
                memory: vk::DeviceMemory::null(),
            },
            |inner| unsafe { self.device.destroy_image(inner.image, None) },
        );

        // Get the modifier Vulkan created the image using.
        let format = {
            let mut image_modifier_properties = vk::ImageDrmFormatModifierPropertiesEXT::default();

            unsafe {
                self.extension_fns
                    .ext_image_format_modifier
                    .get_image_drm_format_modifier_properties(guard.image, &mut image_modifier_properties)
            }?;

            DrmFormat {
                code: fourcc,
                modifier: DrmModifier::from(image_modifier_properties.drm_format_modifier),
            }
        };

        // Now that we know the plane count, get the number of planes for the format + modifier
        let format_plane_count = self
            .formats
            .iter()
            .find(|entry| entry.format == format)
            .unwrap()
            .modifier_properties
            .drm_format_modifier_plane_count;

        // Allocate image memory
        let memory_reqs = unsafe { self.device.get_image_memory_requirements(guard.image) };
        // TODO: Memory type index
        let mut export_memory_allocate_info = vk::ExportMemoryAllocateInfo::default()
            .handle_types(vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT);
        let alloc_create_info = vk::MemoryAllocateInfo::default()
            .allocation_size(memory_reqs.size)
            .push_next(&mut export_memory_allocate_info);

        unsafe {
            // Allocate memory for the image.
            guard.memory = self.device.allocate_memory(&alloc_create_info, None)?;
            // Finally bind the memory to the image
            self.device.bind_image_memory(guard.image, guard.memory, 0)?;
        }

        // Initialization is complete, prevent the scope guard from running it's dropfn.
        let inner = scopeguard::ScopeGuard::into_inner(guard);

        // Track the image for destruction.
        self.images.push(inner);

        self.remaining_allocations -= 1;

        Ok(VulkanImage {
            inner,
            width,
            height,
            format,
            format_plane_count,
            khr_external_memory_fd: self.extension_fns.khr_external_memory_fd.clone(),
            dropped_sender: self.dropped_sender.clone(),
            device: Arc::downgrade(&self.device),
            #[cfg(feature = "backend_drm")]
            node: self.node,
        })
    }

    fn cleanup(&mut self) {
        let dropped = self.dropped_recv.try_iter().collect::<Vec<_>>();

        self.images.retain(|image| {
            // Only drop if the
            let drop = dropped.contains(image);

            if drop {
                // Destroy the underlying image resource
                unsafe {
                    self.device.destroy_image(image.image, None);
                    self.device.free_memory(image.memory, None);
                }

                self.remaining_allocations = self
                    .remaining_allocations
                    .checked_add(1)
                    .expect("Remaining allocations overflowed");
                debug_assert!(
                    self.phd.limits().max_memory_allocation_count >= self.remaining_allocations,
                    "Too many allocations released",
                );
            }

            // If the image was dropped, return false
            !drop
        })
    }
}