#![allow(unsafe_code)] use crate::error::*; use std::mem::size_of; use std::ptr::copy_nonoverlapping; use vulkanalia::{ Device, Instance }; use vulkanalia::vk::{ self, Handle, HasBuilder, InstanceV1_0, DeviceV1_0 }; #[allow(unsafe_code)] pub fn init_buffer(instance: &Instance, physical_device: &vk::PhysicalDevice, device: &Device, queue: &vk::Queue, command_pool: &vk::CommandPool, usage: vk::BufferUsageFlags, contents: &[T]) -> Result<(vk::Buffer, vk::DeviceMemory)> { let (staging_buffer, staging_memory, size) = stage_in_buffer(instance, physical_device, device, contents)?; let final_usage = vk::BufferUsageFlags::TRANSFER_DST | usage; let final_memory_flags = vk::MemoryPropertyFlags::DEVICE_LOCAL; let (final_buffer, device_memory) = allocate_buffer(instance, physical_device, device, size as vk::DeviceSize, final_usage, final_memory_flags)?; copy_buffer(device, queue, command_pool, &staging_buffer, &final_buffer, size as vk::DeviceSize)?; unsafe { device.destroy_buffer(staging_buffer, None) }; unsafe { device.free_memory(staging_memory, None) }; Ok((final_buffer, device_memory)) } pub fn stage_in_buffer(instance: &Instance, physical_device: &vk::PhysicalDevice, device: &Device, contents: &[T]) -> Result<(vk::Buffer, vk::DeviceMemory, usize)> { let size = size_of::() * contents.len(); let staging_usage = vk::BufferUsageFlags::TRANSFER_SRC; let staging_memory_flags = vk::MemoryPropertyFlags::HOST_COHERENT | vk::MemoryPropertyFlags::HOST_VISIBLE; let (staging_buffer, staging_memory) = allocate_buffer(instance, physical_device, device, size as vk::DeviceSize, staging_usage, staging_memory_flags)?; let host_memory = unsafe { device.map_memory(staging_memory, 0, size as vk::DeviceSize, vk::MemoryMapFlags::empty()) }?; unsafe { copy_nonoverlapping(contents.as_ptr(), host_memory.cast(), contents.len()) }; unsafe { device.unmap_memory(staging_memory) }; Ok((staging_buffer, staging_memory, size)) } #[allow(unsafe_code)] pub fn allocate_buffer(instance: &Instance, physical_device: &vk::PhysicalDevice, device: &Device, size: vk::DeviceSize, usage: vk::BufferUsageFlags, memory_flags: vk::MemoryPropertyFlags) -> Result<(vk::Buffer, vk::DeviceMemory)> { let buffer_info = vk::BufferCreateInfo::builder() .size(size) .usage(usage) .sharing_mode(vk::SharingMode::EXCLUSIVE); let buffer = unsafe { device.create_buffer(&buffer_info, None) }?; // The requirements are mostly what you'd think: size and alignment. The // bits field is something special; see pick_memory_type() for the // explanation. Despite the simplicity of this data, Vulkan wants to be the // one to tell us about it, and we let it. let requirements = unsafe { device.get_buffer_memory_requirements(buffer) }; let type_index = pick_memory_type(instance, physical_device, &memory_flags, &requirements)?; let memory_info = vk::MemoryAllocateInfo::builder() .allocation_size(requirements.size) .memory_type_index(type_index); let device_memory = unsafe { device.allocate_memory(&memory_info, None) }?; unsafe { device.bind_buffer_memory(buffer, device_memory, 0) }?; Ok((buffer, device_memory)) } #[allow(unsafe_code)] pub fn copy_buffer(device: &Device, queue: &vk::Queue, command_pool: &vk::CommandPool, source: &vk::Buffer, destination: &vk::Buffer, size: vk::DeviceSize) -> Result<()> { let command_buffer = begin_transient_commands(device, command_pool)?; let copy_info = vk::BufferCopy::builder().size(size); unsafe { device.cmd_copy_buffer(command_buffer, *source, *destination, &[copy_info]) }; end_transient_commands(command_buffer, device, queue, command_pool)?; Ok(()) } #[allow(unsafe_code)] pub fn pick_memory_type(instance: &Instance, physical_device: &vk::PhysicalDevice, properties: &vk::MemoryPropertyFlags, requirements: &vk::MemoryRequirements) -> Result { let memory_map = unsafe { instance.get_physical_device_memory_properties(*physical_device) }; // So. The memory_type_bits field is a map of which indices are suitable, // based on the buffer our caller passed to // get_buffer_memory_requirements(). Yes, that means there's a hard cap on // how many memory types there can be, based on the size of the bitfield. for index in 0 .. memory_map.memory_type_count { if requirements.memory_type_bits & (1 << index) == 0 { continue; } let memory_type = memory_map.memory_types[index as usize]; if memory_type.property_flags.contains(*properties) { return Ok(index); } } Err(Error { message: "The system has no suitable memory for a buffer.".to_string() }) } #[allow(unsafe_code)] pub fn begin_transient_commands(device: &Device, command_pool: &vk::CommandPool) -> Result { let command_buffer_allocation_info = vk::CommandBufferAllocateInfo::builder() .command_pool(*command_pool) .level(vk::CommandBufferLevel::PRIMARY) .command_buffer_count(1); let command_buffer = unsafe { device.allocate_command_buffers(&command_buffer_allocation_info) }?[0]; let command_buffer_begin_info = vk::CommandBufferBeginInfo::builder() .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT); unsafe { device.begin_command_buffer(command_buffer, &command_buffer_begin_info) }?; Ok(command_buffer) } #[allow(unsafe_code)] pub fn end_transient_commands(command_buffer: vk::CommandBuffer, device: &Device, queue: &vk::Queue, command_pool: &vk::CommandPool) -> Result<()> { unsafe { device.end_command_buffer(command_buffer) }?; let command_buffers = [command_buffer]; let submit_info = vk::SubmitInfo::builder() .command_buffers(&command_buffers); unsafe { device.queue_submit(*queue, &[submit_info], vk::Fence::null()) }?; unsafe { device.queue_wait_idle(*queue) }?; unsafe { device.free_command_buffers(*command_pool, &command_buffers) }; Ok(()) }