jay-compositor 1.15.0

The Jay compositor
use crate::gfx_apis::vulkan::VulkanError;
use crate::gfx_apis::vulkan::device::VulkanDevice;
use crate::utils::errorfmt::ErrorFmt;
use crate::utils::numcell::NumCell;
use crate::utils::stack::Stack;
use ash::vk::CommandBuffer;
use ash::vk::CommandBufferAllocateInfo;
use ash::vk::CommandBufferLevel;
use ash::vk::CommandBufferResetFlags;
use ash::vk::CommandPool;
use ash::vk::CommandPoolCreateFlags;
use ash::vk::CommandPoolCreateInfo;
use std::rc::Rc;

pub struct VulkanCommandPool {
    device: Rc<VulkanDevice>,
    pool: CommandPool,
}

pub struct VulkanCommandBuffer {
    pool: Rc<VulkanCommandPool>,
    pub(super) buffer: CommandBuffer,
}

pub(super) struct CachedCommandBuffers {
    pool: Rc<VulkanCommandPool>,
    buffers: Stack<Rc<VulkanCommandBuffer>>,
    total_buffers: NumCell<usize>,
}

impl Drop for VulkanCommandPool {
    fn drop(&mut self) {
        unsafe {
            self.device.device.destroy_command_pool(self.pool, None);
        }
    }
}

impl Drop for VulkanCommandBuffer {
    fn drop(&mut self) {
        unsafe {
            self.pool
                .device
                .device
                .free_command_buffers(self.pool.pool, &[self.buffer]);
        }
    }
}

impl VulkanCommandPool {
    fn allocate_buffer(self: &Rc<Self>) -> Result<Rc<VulkanCommandBuffer>, VulkanError> {
        let create_info = CommandBufferAllocateInfo::default()
            .command_pool(self.pool)
            .command_buffer_count(1)
            .level(CommandBufferLevel::PRIMARY);
        let buffer = unsafe { self.device.device.allocate_command_buffers(&create_info) };
        let mut buffer = buffer.map_err(VulkanError::AllocateCommandBuffer)?;
        assert_eq!(buffer.len(), 1);
        Ok(Rc::new(VulkanCommandBuffer {
            pool: self.clone(),
            buffer: buffer.pop().unwrap(),
        }))
    }
}

impl VulkanDevice {
    pub fn create_command_pool(
        self: &Rc<Self>,
        queue: u32,
    ) -> Result<CachedCommandBuffers, VulkanError> {
        let info = CommandPoolCreateInfo::default()
            .queue_family_index(queue)
            .flags(
                CommandPoolCreateFlags::TRANSIENT | CommandPoolCreateFlags::RESET_COMMAND_BUFFER,
            );
        let pool = unsafe { self.device.create_command_pool(&info, None) };
        let pool = pool.map_err(VulkanError::AllocateCommandPool)?;
        Ok(CachedCommandBuffers {
            pool: Rc::new(VulkanCommandPool {
                device: self.clone(),
                pool,
            }),
            buffers: Default::default(),
            total_buffers: Default::default(),
        })
    }
}

impl CachedCommandBuffers {
    pub(super) fn allocate(&self) -> Result<Rc<VulkanCommandBuffer>, VulkanError> {
        zone!("allocate_command_buffer");
        let buf = match self.buffers.pop() {
            Some(b) => b,
            _ => {
                self.total_buffers.fetch_add(1);
                self.pool.allocate_buffer()?
            }
        };
        Ok(buf)
    }

    pub(super) fn release(&self, buffer: Rc<VulkanCommandBuffer>) {
        let res = unsafe {
            buffer
                .pool
                .device
                .device
                .reset_command_buffer(buffer.buffer, CommandBufferResetFlags::empty())
        };
        if let Err(e) = res {
            log::error!("Could not reset command buffer: {}", ErrorFmt(e));
            return;
        }
        self.buffers.push(buffer);
    }
}