use crate::{
error::{SarektError, SarektResult},
image_data::{ImageData, ImageDataFormat},
renderer::{
buffers_and_images::{
BackendHandleTrait, BufferAndImageLoader, BufferImageHandle, BufferType, IndexBufferElemSize,
MagnificationMinificationFilter, TextureAddressMode,
},
config::NumSamples,
vulkan::{
images::ImageAndView,
vulkan_renderer::vulkan_core::{VulkanCoreStructures, VulkanDeviceStructures},
},
},
};
use ash::{
version::{DeviceV1_0, InstanceV1_0},
vk, Device, Instance,
};
use log::{info, warn};
use std::sync::Arc;
#[derive(Clone)]
pub struct VulkanBufferImageFunctions {
instance: Arc<Instance>,
logical_device: Arc<Device>,
physical_device: vk::PhysicalDevice,
allocator: Arc<vk_mem::Allocator>,
transfer_command_buffer: vk::CommandBuffer,
transfer_command_queue: vk::Queue,
graphics_command_buffer: vk::CommandBuffer,
graphics_command_queue: vk::Queue,
graphics_queue_family: u32,
transfer_queue_family: u32,
ownership_semaphore: [vk::Semaphore; 1],
}
impl VulkanBufferImageFunctions {
pub fn new(
vulkan_core: &VulkanCoreStructures, device_bundle: &VulkanDeviceStructures,
allocator: Arc<vk_mem::Allocator>, graphics_queue_family: u32, transfer_queue_family: u32,
transfer_command_pool: vk::CommandPool, transfer_command_queue: vk::Queue,
graphics_command_pool: vk::CommandPool, graphics_command_queue: vk::Queue,
) -> SarektResult<Self> {
let command_buffer_alloc_info = vk::CommandBufferAllocateInfo::builder()
.level(vk::CommandBufferLevel::PRIMARY)
.command_pool(transfer_command_pool)
.command_buffer_count(1)
.build();
let transfer_command_buffer = unsafe {
device_bundle
.logical_device
.allocate_command_buffers(&command_buffer_alloc_info)?[0]
};
let graphics_command_buffer = if graphics_command_pool != transfer_command_pool {
let command_buffer_alloc_info = vk::CommandBufferAllocateInfo::builder()
.level(vk::CommandBufferLevel::PRIMARY)
.command_pool(graphics_command_pool)
.command_buffer_count(1)
.build();
unsafe {
device_bundle
.logical_device
.allocate_command_buffers(&command_buffer_alloc_info)?[0]
}
} else {
transfer_command_buffer
};
let ownership_semaphore = if graphics_command_pool != transfer_command_pool {
let semaphore_ci = vk::SemaphoreCreateInfo::default();
unsafe {
[device_bundle
.logical_device
.create_semaphore(&semaphore_ci, None)?]
}
} else {
[vk::Semaphore::null()]
};
Ok(Self {
instance: vulkan_core.instance.clone(),
logical_device: device_bundle.logical_device.clone(),
physical_device: device_bundle.physical_device,
allocator,
transfer_command_buffer,
transfer_command_queue,
graphics_command_buffer,
graphics_command_queue,
graphics_queue_family,
transfer_queue_family,
ownership_semaphore,
})
}
fn create_staging_buffer(
&self, buffer_size: u64,
) -> SarektResult<(vk::Buffer, vk_mem::Allocation, vk_mem::AllocationInfo)> {
info!("Creating staging buffer");
self.create_cpu_accessible_buffer(buffer_size, vk::BufferUsageFlags::TRANSFER_SRC)
}
fn create_cpu_accessible_buffer(
&self, buffer_size: u64, usage_flags: vk::BufferUsageFlags,
) -> SarektResult<(vk::Buffer, vk_mem::Allocation, vk_mem::AllocationInfo)> {
info!(
"Creating cpu accessible buffer and memory of size {} to transfer from CPU memory...",
buffer_size
);
let staging_buffer_ci = vk::BufferCreateInfo::builder()
.size(buffer_size)
.usage(usage_flags)
.sharing_mode(vk::SharingMode::EXCLUSIVE) .build();
let staging_alloc_ci = vk_mem::AllocationCreateInfo {
usage: vk_mem::MemoryUsage::CpuToGpu,
..vk_mem::AllocationCreateInfo::default()
};
Ok(
self
.allocator
.create_buffer(&staging_buffer_ci, &staging_alloc_ci)?,
)
}
fn create_gpu_buffer(
&self, buffer_type: BufferType, buffer_size: u64, queue_family_index: u32,
) -> SarektResult<(vk::Buffer, vk_mem::Allocation, vk_mem::AllocationInfo)> {
info!("Creating GPU buffer and memory to use during drawing...");
let buffer_usage =
vk::BufferUsageFlags::TRANSFER_DST | usage_flags_from_buffer_type(buffer_type);
let sharing_mode = vk::SharingMode::EXCLUSIVE;
let queue_family_indices = [queue_family_index];
let buffer_ci = vk::BufferCreateInfo::builder()
.size(buffer_size)
.usage(buffer_usage)
.sharing_mode(sharing_mode)
.queue_family_indices(&queue_family_indices) .build();
let alloc_ci = vk_mem::AllocationCreateInfo {
usage: vk_mem::MemoryUsage::GpuOnly,
..vk_mem::AllocationCreateInfo::default()
};
Ok(self.allocator.create_buffer(&buffer_ci, &alloc_ci)?)
}
fn create_gpu_image(
&self, dimens: (u32, u32), format: vk::Format, usage: vk::ImageUsageFlags,
queue_family_index: u32, mip_levels: u32, num_msaa_samples: NumSamples,
) -> SarektResult<(vk::Image, vk_mem::Allocation, vk_mem::AllocationInfo)> {
let image_ci = vk::ImageCreateInfo::builder()
.image_type(vk::ImageType::TYPE_2D)
.usage(usage)
.extent(vk::Extent3D {
width: dimens.0,
height: dimens.1,
depth: 1,
})
.mip_levels(mip_levels)
.array_layers(1) .format(format)
.tiling(vk::ImageTiling::OPTIMAL) .initial_layout(vk::ImageLayout::UNDEFINED)
.queue_family_indices(&[queue_family_index])
.sharing_mode(vk::SharingMode::EXCLUSIVE) .samples(num_msaa_samples.into()) .build();
let alloc_ci = vk_mem::AllocationCreateInfo {
usage: vk_mem::MemoryUsage::GpuOnly,
..vk_mem::AllocationCreateInfo::default()
};
Ok(self.allocator.create_image(&image_ci, &alloc_ci)?)
}
fn transfer_staging_to_gpu_buffer_or_image(
&self, buffer_size: u64, staging_buffer: vk::Buffer, gpu_buffer_or_image: ImageOrBuffer,
mip_levels: Option<u32>,
) -> SarektResult<()> {
info!("Initiating transfer command to transfer from staging buffer to device only memory...");
let transfer_command_buffer = self.transfer_command_buffer;
let command_begin_info = vk::CommandBufferBeginInfo::builder()
.flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT)
.build();
unsafe {
self
.logical_device
.begin_command_buffer(transfer_command_buffer, &command_begin_info)?;
let (src_queue_family, dst_queue_family) = match gpu_buffer_or_image {
ImageOrBuffer::Buffer(gpu_buffer) => {
let copy_region = vk::BufferCopy::builder()
.src_offset(0)
.dst_offset(0)
.size(buffer_size)
.build();
self.logical_device.cmd_copy_buffer(
transfer_command_buffer,
staging_buffer,
gpu_buffer,
&[copy_region],
);
(vk::QUEUE_FAMILY_IGNORED, vk::QUEUE_FAMILY_IGNORED)
}
ImageOrBuffer::Image(gpu_image, _format, extent) => {
self.insert_layout_transition_barrier(
transfer_command_buffer,
gpu_image,
vk::ImageLayout::UNDEFINED,
vk::ImageLayout::TRANSFER_DST_OPTIMAL,
vk::QUEUE_FAMILY_IGNORED,
vk::QUEUE_FAMILY_IGNORED,
mip_levels.unwrap_or(1),
)?;
let image_subresource = vk::ImageSubresourceLayers::builder()
.aspect_mask(vk::ImageAspectFlags::COLOR)
.mip_level(0)
.base_array_layer(0)
.layer_count(1)
.build();
let regions = [vk::BufferImageCopy::builder()
.buffer_offset(0)
.buffer_row_length(0)
.buffer_image_height(0)
.image_subresource(image_subresource)
.image_offset(vk::Offset3D { x: 0, y: 0, z: 0 })
.image_extent(extent)
.build()];
self.logical_device.cmd_copy_buffer_to_image(
transfer_command_buffer,
staging_buffer,
gpu_image,
vk::ImageLayout::TRANSFER_DST_OPTIMAL,
®ions,
);
self.insert_layout_transition_barrier(
transfer_command_buffer,
gpu_image,
vk::ImageLayout::TRANSFER_DST_OPTIMAL,
vk::ImageLayout::TRANSFER_DST_OPTIMAL,
self.transfer_queue_family,
self.graphics_queue_family,
mip_levels.unwrap_or(1),
)?
}
};
self
.logical_device
.end_command_buffer(transfer_command_buffer)?;
let command_buffers = [transfer_command_buffer];
let mut submit_info_builder = vk::SubmitInfo::builder().command_buffers(&command_buffers);
if src_queue_family != dst_queue_family {
submit_info_builder = submit_info_builder.signal_semaphores(&self.ownership_semaphore)
}
let submit_info = submit_info_builder.build();
self.logical_device.queue_submit(
self.transfer_command_queue,
&[submit_info],
vk::Fence::null(),
)?;
self.accept_image_transfer_and_generate_mipmaps(
&gpu_buffer_or_image,
mip_levels,
src_queue_family,
dst_queue_family,
)?;
self.logical_device.device_wait_idle()?;
self.logical_device.reset_command_buffer(
self.transfer_command_buffer,
vk::CommandBufferResetFlags::empty(),
)?;
self.logical_device.reset_command_buffer(
self.graphics_command_buffer,
vk::CommandBufferResetFlags::empty(),
)?;
}
Ok(())
}
unsafe fn accept_image_transfer_and_generate_mipmaps(
&self, gpu_image: &ImageOrBuffer, mip_levels: Option<u32>, src_queue_family: u32,
dst_queue_family: u32,
) -> SarektResult<()> {
match gpu_image {
ImageOrBuffer::Image(gpu_image, format, extent) => {
let command_begin_info = vk::CommandBufferBeginInfo::builder()
.flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT)
.build();
self
.logical_device
.begin_command_buffer(self.graphics_command_buffer, &command_begin_info)?;
self.transfer_image_queue_ownership_if_necessary(
*gpu_image,
src_queue_family,
dst_queue_family,
mip_levels,
)?;
self.generate_mipmaps_shader_ro_optimal(
self.graphics_command_buffer,
*gpu_image,
*format,
extent.width,
extent.height,
mip_levels.unwrap_or(1),
)?;
}
_ => (),
}
if src_queue_family == dst_queue_family {
return Ok(());
}
self
.logical_device
.end_command_buffer(self.graphics_command_buffer)?;
let command_buffers = [self.graphics_command_buffer];
let mut submit_info = [vk::SubmitInfo::builder()
.command_buffers(&command_buffers)
.wait_semaphores(&self.ownership_semaphore)
.wait_dst_stage_mask(&[vk::PipelineStageFlags::TOP_OF_PIPE])
.build()];
if src_queue_family == dst_queue_family {
submit_info[0].wait_semaphore_count = 0;
}
self.logical_device.queue_submit(
self.graphics_command_queue,
&submit_info,
vk::Fence::null(),
)?;
Ok(())
}
unsafe fn transfer_image_queue_ownership_if_necessary(
&self, gpu_image: vk::Image, src_queue_family: u32, dst_queue_family: u32,
mip_levels: Option<u32>,
) -> SarektResult<()> {
if src_queue_family == dst_queue_family {
return Ok(());
}
let subresource_range = vk::ImageSubresourceRange::builder()
.aspect_mask(vk::ImageAspectFlags::COLOR)
.base_mip_level(0)
.level_count(mip_levels.unwrap_or(1))
.base_array_layer(0)
.layer_count(1)
.build();
let barriers = [vk::ImageMemoryBarrier::builder()
.old_layout(vk::ImageLayout::TRANSFER_DST_OPTIMAL)
.new_layout(vk::ImageLayout::TRANSFER_DST_OPTIMAL)
.src_queue_family_index(src_queue_family) .dst_queue_family_index(dst_queue_family)
.image(gpu_image)
.subresource_range(subresource_range)
.src_access_mask(vk::AccessFlags::empty()) .dst_access_mask(vk::AccessFlags::SHADER_READ)
.build()];
self.logical_device.cmd_pipeline_barrier(
self.graphics_command_buffer,
vk::PipelineStageFlags::TOP_OF_PIPE,
vk::PipelineStageFlags::FRAGMENT_SHADER,
vk::DependencyFlags::empty(),
&[],
&[],
&barriers,
);
Ok(())
}
unsafe fn generate_mipmaps_shader_ro_optimal(
&self, graphics_command_buffer: vk::CommandBuffer, image: vk::Image, format: vk::Format,
width: u32, height: u32, mip_levels: u32,
) -> SarektResult<()> {
if mip_levels > 1 {
let format_properties = self
.instance
.get_physical_device_format_properties(self.physical_device, format);
if !format_properties
.optimal_tiling_features
.intersects(vk::FormatFeatureFlags::SAMPLED_IMAGE_FILTER_LINEAR)
{
return Err(SarektError::FormatDoesNotSupportMipmapping(format!(
"Linear filtering not supported on format {:?}",
format,
)));
}
}
let mut mip_width = width;
let mut mip_height = height;
for i in 1..mip_levels {
let subresource_range = vk::ImageSubresourceRange::builder()
.aspect_mask(vk::ImageAspectFlags::COLOR)
.base_array_layer(0)
.layer_count(1)
.base_mip_level(i - 1)
.level_count(1)
.build();
let barrier = [vk::ImageMemoryBarrier::builder()
.image(image)
.src_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
.dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
.old_layout(vk::ImageLayout::TRANSFER_DST_OPTIMAL)
.new_layout(vk::ImageLayout::TRANSFER_SRC_OPTIMAL)
.src_access_mask(vk::AccessFlags::TRANSFER_WRITE)
.dst_access_mask(vk::AccessFlags::TRANSFER_READ)
.subresource_range(subresource_range)
.build()];
self.logical_device.cmd_pipeline_barrier(
graphics_command_buffer,
vk::PipelineStageFlags::TRANSFER,
vk::PipelineStageFlags::TRANSFER,
vk::DependencyFlags::empty(),
&[],
&[],
&barrier,
);
let src_offsets = [
vk::Offset3D::default(),
vk::Offset3D::builder()
.x(mip_width as i32)
.y(mip_height as i32)
.z(1)
.build(),
];
let src_subresource = vk::ImageSubresourceLayers::builder()
.aspect_mask(vk::ImageAspectFlags::COLOR)
.mip_level(i - 1)
.base_array_layer(0)
.layer_count(1)
.build();
let dst_offsets = [
vk::Offset3D::default(),
vk::Offset3D::builder()
.x(if mip_width > 1 { mip_width / 2 } else { 1 } as i32)
.y(if mip_height > 1 { mip_height / 2 } else { 1 } as i32)
.z(1)
.build(),
];
let dst_subresource = vk::ImageSubresourceLayers::builder()
.aspect_mask(vk::ImageAspectFlags::COLOR)
.mip_level(i)
.base_array_layer(0)
.layer_count(1)
.build();
let blit = [vk::ImageBlit::builder()
.src_offsets(src_offsets)
.src_subresource(src_subresource)
.dst_offsets(dst_offsets)
.dst_subresource(dst_subresource)
.build()];
info!(
"Generating mip level: {} {}x{}",
i, dst_offsets[1].x, dst_offsets[1].y
);
self.logical_device.cmd_blit_image(
graphics_command_buffer,
image,
vk::ImageLayout::TRANSFER_SRC_OPTIMAL,
image,
vk::ImageLayout::TRANSFER_DST_OPTIMAL,
&blit,
vk::Filter::LINEAR,
);
let barrier = [vk::ImageMemoryBarrier::builder()
.image(image)
.subresource_range(subresource_range)
.src_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
.dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
.old_layout(vk::ImageLayout::TRANSFER_SRC_OPTIMAL)
.new_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL)
.src_access_mask(vk::AccessFlags::TRANSFER_READ)
.dst_access_mask(vk::AccessFlags::SHADER_READ)
.build()];
self.logical_device.cmd_pipeline_barrier(
graphics_command_buffer,
vk::PipelineStageFlags::TRANSFER,
vk::PipelineStageFlags::FRAGMENT_SHADER,
vk::DependencyFlags::empty(),
&[],
&[],
&barrier,
);
if mip_width > 1 {
mip_width /= 2;
}
if mip_height > 1 {
mip_height /= 2;
}
}
let subresource_range = vk::ImageSubresourceRange::builder()
.aspect_mask(vk::ImageAspectFlags::COLOR)
.base_mip_level(mip_levels - 1)
.level_count(1)
.base_array_layer(0)
.layer_count(1)
.build();
let barrier = [vk::ImageMemoryBarrier::builder()
.image(image)
.subresource_range(subresource_range)
.src_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
.dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
.old_layout(vk::ImageLayout::TRANSFER_DST_OPTIMAL)
.new_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL)
.src_access_mask(vk::AccessFlags::TRANSFER_WRITE)
.dst_access_mask(vk::AccessFlags::SHADER_READ)
.build()];
info!("Transitioning final mip level to shader ro format");
self.logical_device.cmd_pipeline_barrier(
graphics_command_buffer,
vk::PipelineStageFlags::TRANSFER,
vk::PipelineStageFlags::FRAGMENT_SHADER,
vk::DependencyFlags::empty(),
&[],
&[],
&barrier,
);
Ok(())
}
fn create_image_view(
&self, image: vk::Image, format: vk::Format, aspect: vk::ImageAspectFlags, mip_levels: u32,
) -> SarektResult<vk::ImageView> {
let subresource_range = vk::ImageSubresourceRange::builder()
.base_mip_level(0)
.level_count(mip_levels)
.aspect_mask(aspect)
.base_array_layer(0)
.layer_count(1)
.build();
let image_view_ci = vk::ImageViewCreateInfo::builder()
.image(image)
.view_type(vk::ImageViewType::TYPE_2D)
.format(format)
.subresource_range(subresource_range)
.build();
unsafe {
Ok(
self
.logical_device
.create_image_view(&image_view_ci, None)?,
)
}
}
fn create_sampler(
&self, magnification_filter: MagnificationMinificationFilter,
minification_filter: MagnificationMinificationFilter, address_u: TextureAddressMode,
address_v: TextureAddressMode, address_w: TextureAddressMode, mip_levels: u32,
) -> SarektResult<vk::Sampler> {
let mag_filter = match magnification_filter {
MagnificationMinificationFilter::Linear => vk::Filter::LINEAR,
MagnificationMinificationFilter::Nearest => vk::Filter::NEAREST,
};
let min_filter = match minification_filter {
MagnificationMinificationFilter::Linear => vk::Filter::LINEAR,
MagnificationMinificationFilter::Nearest => vk::Filter::NEAREST,
};
let address_u = match address_u {
TextureAddressMode::Repeat => vk::SamplerAddressMode::REPEAT,
TextureAddressMode::MirroredRepeat => vk::SamplerAddressMode::MIRRORED_REPEAT,
TextureAddressMode::ClampToEdge => vk::SamplerAddressMode::CLAMP_TO_EDGE,
TextureAddressMode::MirroredClampToEdge => vk::SamplerAddressMode::MIRROR_CLAMP_TO_EDGE,
};
let address_v = match address_v {
TextureAddressMode::Repeat => vk::SamplerAddressMode::REPEAT,
TextureAddressMode::MirroredRepeat => vk::SamplerAddressMode::MIRRORED_REPEAT,
TextureAddressMode::ClampToEdge => vk::SamplerAddressMode::CLAMP_TO_EDGE,
TextureAddressMode::MirroredClampToEdge => vk::SamplerAddressMode::MIRROR_CLAMP_TO_EDGE,
};
let address_w = match address_w {
TextureAddressMode::Repeat => vk::SamplerAddressMode::REPEAT,
TextureAddressMode::MirroredRepeat => vk::SamplerAddressMode::MIRRORED_REPEAT,
TextureAddressMode::ClampToEdge => vk::SamplerAddressMode::CLAMP_TO_EDGE,
TextureAddressMode::MirroredClampToEdge => vk::SamplerAddressMode::MIRROR_CLAMP_TO_EDGE,
};
let sampler_ci = vk::SamplerCreateInfo::builder()
.mag_filter(mag_filter)
.min_filter(min_filter)
.address_mode_u(address_u)
.address_mode_v(address_v)
.address_mode_w(address_w)
.anisotropy_enable(true)
.max_anisotropy(16f32)
.border_color(vk::BorderColor::INT_OPAQUE_BLACK)
.unnormalized_coordinates(false)
.compare_enable(false)
.compare_op(vk::CompareOp::ALWAYS)
.mipmap_mode(vk::SamplerMipmapMode::LINEAR)
.mip_lod_bias(0.0f32)
.min_lod(0.0f32)
.max_lod(mip_levels as _)
.build();
unsafe { Ok(self.logical_device.create_sampler(&sampler_ci, None)?) }
}
fn insert_layout_transition_barrier(
&self, command_buffer: vk::CommandBuffer, image: vk::Image, old_layout: vk::ImageLayout,
new_layout: vk::ImageLayout, src_queue_family: u32, dst_queue_family: u32, mip_levels: u32,
) -> SarektResult<(u32, u32)> {
let subresource_range = vk::ImageSubresourceRange::builder()
.aspect_mask(vk::ImageAspectFlags::COLOR)
.base_mip_level(0)
.level_count(mip_levels)
.base_array_layer(0)
.layer_count(1)
.build();
let source_stage: vk::PipelineStageFlags;
let source_access_mask: vk::AccessFlags;
let destination_stage: vk::PipelineStageFlags;
let destination_access_mask: vk::AccessFlags;
if old_layout == vk::ImageLayout::UNDEFINED
&& new_layout == vk::ImageLayout::TRANSFER_DST_OPTIMAL
{
source_access_mask = vk::AccessFlags::empty();
destination_access_mask = vk::AccessFlags::TRANSFER_WRITE;
source_stage = vk::PipelineStageFlags::TOP_OF_PIPE;
destination_stage = vk::PipelineStageFlags::TRANSFER;
} else if old_layout == vk::ImageLayout::TRANSFER_DST_OPTIMAL
&& new_layout == vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL
{
source_access_mask = vk::AccessFlags::TRANSFER_WRITE;
destination_access_mask = vk::AccessFlags::SHADER_READ;
source_stage = vk::PipelineStageFlags::TRANSFER;
destination_stage = vk::PipelineStageFlags::FRAGMENT_SHADER;
} else {
source_access_mask = vk::AccessFlags::TRANSFER_WRITE;
destination_access_mask = vk::AccessFlags::TRANSFER_WRITE;
source_stage = vk::PipelineStageFlags::TRANSFER;
destination_stage = vk::PipelineStageFlags::TRANSFER;
}
let barriers = [vk::ImageMemoryBarrier::builder()
.old_layout(old_layout)
.new_layout(new_layout)
.src_queue_family_index(src_queue_family) .dst_queue_family_index(dst_queue_family)
.image(image)
.subresource_range(subresource_range)
.src_access_mask(source_access_mask)
.dst_access_mask(destination_access_mask)
.build()];
unsafe {
self.logical_device.cmd_pipeline_barrier(
command_buffer,
source_stage,
destination_stage,
vk::DependencyFlags::empty(),
&[],
&[],
&barriers,
);
}
Ok((src_queue_family, dst_queue_family))
}
}
unsafe impl BufferAndImageLoader for VulkanBufferImageFunctions {
type BackendHandle = ResourceWithMemory;
type UniformBufferDataHandle = Vec<BufferAndMemoryMapped>;
type UniformBufferHandle = Vec<BufferImageHandle<VulkanBufferImageFunctions>>;
unsafe fn cleanup(&self) -> SarektResult<()> {
if self.ownership_semaphore[0] != vk::Semaphore::null() {
return Ok(
self
.logical_device
.destroy_semaphore(self.ownership_semaphore[0], None),
);
}
Ok(())
}
fn load_buffer_with_staging<BufElem: Sized + Copy>(
&self, buffer_type: BufferType, buffer: &[BufElem],
) -> SarektResult<ResourceWithMemory> {
let buffer_size =
(std::mem::size_of::<BufElem>() as vk::DeviceSize) * buffer.len() as vk::DeviceSize;
let (staging_buffer, staging_allocation, _) = self.create_staging_buffer(buffer_size)?;
let data = self.allocator.map_memory(&staging_allocation)? as *mut BufElem;
unsafe {
data.copy_from_nonoverlapping(buffer.as_ptr(), buffer.len());
}
self.allocator.unmap_memory(&staging_allocation)?;
let (gpu_buffer, gpu_allocation, _gpu_allocation_info) =
self.create_gpu_buffer(buffer_type, buffer_size, self.transfer_queue_family)?;
self.transfer_staging_to_gpu_buffer_or_image(
buffer_size,
staging_buffer,
ImageOrBuffer::Buffer(gpu_buffer),
None,
)?;
info!("Destroying staging buffer and memory...");
self
.allocator
.destroy_buffer(staging_buffer, &staging_allocation)?;
let index_buffer_elem_size = match buffer_type {
BufferType::Index(size) => Some(size),
_ => None,
};
Ok(ResourceWithMemory::Buffer(BufferAndMemory {
buffer: gpu_buffer,
length: buffer.len() as u32,
index_buffer_elem_size,
allocation: gpu_allocation,
}))
}
fn load_buffer_without_staging<BufElem: Sized + Copy>(
&self, buffer_type: BufferType, buffer: &[BufElem],
) -> SarektResult<ResourceWithMemory> {
let buffer_size =
(std::mem::size_of::<BufElem>() as vk::DeviceSize) * buffer.len() as vk::DeviceSize;
let (vk_buffer, allocation, _) =
self.create_cpu_accessible_buffer(buffer_size, usage_flags_from_buffer_type(buffer_type))?;
let data = self.allocator.map_memory(&allocation)? as *mut BufElem;
unsafe {
data.copy_from_nonoverlapping(buffer.as_ptr(), buffer.len());
}
self.allocator.unmap_memory(&allocation)?;
let index_buffer_elem_size = match buffer_type {
BufferType::Index(size) => Some(size),
_ => None,
};
Ok(ResourceWithMemory::Buffer(BufferAndMemory {
buffer: vk_buffer,
length: buffer.len() as u32,
index_buffer_elem_size,
allocation,
}))
}
fn load_image_with_staging_initialization(
&self, pixels: impl ImageData, magnification_filter: MagnificationMinificationFilter,
minification_filter: MagnificationMinificationFilter, address_u: TextureAddressMode,
address_v: TextureAddressMode, address_w: TextureAddressMode, mip_levels: u32,
) -> SarektResult<ResourceWithMemory> {
if mip_levels < 1 {
return Err(SarektError::IllegalMipmapCount);
}
let dimens = pixels.dimensions();
let (pixel_bytes, format) = {
let format = pixels.format()?.into();
let format_suitable = unsafe {
self
.instance
.get_physical_device_format_properties(self.physical_device, format)
.optimal_tiling_features
.contains(vk::FormatFeatureFlags::SAMPLED_IMAGE)
};
if !format_suitable {
warn!(
"Using an image with unsupported format: {:?}, converting to rgba, consider baking a \
new texture",
format
);
let pixels = pixels.into_rgba8();
let format = pixels.format()?.into();
(pixels.into_bytes(), format)
} else {
let format = pixels.format()?.into();
(pixels.into_bytes(), format)
}
};
info!(
"Loading image with dimensions {:?}, and {} bytes",
dimens,
pixel_bytes.len()
);
let (staging_buffer, staging_allocation, _) =
self.create_staging_buffer(pixel_bytes.len() as u64)?;
let data = self.allocator.map_memory(&staging_allocation)?;
unsafe {
data.copy_from_nonoverlapping(pixel_bytes.as_ptr() as *const u8, pixel_bytes.len());
}
self.allocator.unmap_memory(&staging_allocation)?;
let transfer_src_flag = if mip_levels > 1 {
vk::ImageUsageFlags::TRANSFER_SRC
} else {
vk::ImageUsageFlags::empty()
};
let (image, image_allocation, _) = self.create_gpu_image(
dimens,
format,
vk::ImageUsageFlags::TRANSFER_DST | vk::ImageUsageFlags::SAMPLED | transfer_src_flag,
self.transfer_queue_family,
mip_levels,
NumSamples::One,
)?;
let extent = vk::Extent3D {
width: dimens.0,
height: dimens.1,
depth: 1,
};
self.transfer_staging_to_gpu_buffer_or_image(
pixel_bytes.len() as u64,
staging_buffer,
ImageOrBuffer::Image(image, format, extent),
Some(mip_levels),
)?;
info!("Destroying staging buffer and memory...");
self
.allocator
.destroy_buffer(staging_buffer, &staging_allocation)?;
let image_view = self.create_image_view(
image,
format.into(),
vk::ImageAspectFlags::COLOR,
mip_levels,
)?;
let sampler = self.create_sampler(
magnification_filter,
minification_filter,
address_u,
address_v,
address_w,
mip_levels,
)?;
Ok(ResourceWithMemory::Image(ImageAndMemory {
allocation: image_allocation,
image_and_view: unsafe { ImageAndView::new(image, image_view) },
sampler: Some(sampler),
}))
}
fn create_uninitialized_image(
&self, dimensions: (u32, u32), format: ImageDataFormat, num_msaa_samples: NumSamples,
) -> SarektResult<ResourceWithMemory> {
info!("Creating image with dimensions {:?}", dimensions);
let (image, image_allocation, _) = self.create_gpu_image(
dimensions,
format.into(),
vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT,
self.graphics_queue_family,
1,
num_msaa_samples,
)?;
let image_view =
self.create_image_view(image, format.into(), vk::ImageAspectFlags::DEPTH, 1)?;
Ok(ResourceWithMemory::Image(ImageAndMemory {
allocation: image_allocation,
image_and_view: unsafe { ImageAndView::new(image, image_view) },
sampler: None,
}))
}
fn delete_buffer_or_image(&self, handle: ResourceWithMemory) -> SarektResult<()> {
info!(
"Deleting image or buffer and associated memory {:?}...",
handle
);
match handle {
ResourceWithMemory::Buffer(handle) => self
.allocator
.destroy_buffer(handle.buffer, &handle.allocation)?,
ResourceWithMemory::Image(handle) => {
unsafe {
if let Some(sampler) = handle.sampler {
self.logical_device.destroy_sampler(sampler, None);
}
self
.logical_device
.destroy_image_view(handle.image_and_view.view, None);
}
self
.allocator
.destroy_image(handle.image_and_view.image, &handle.allocation)?;
}
}
Ok(())
}
}
#[derive(Copy, Clone, Debug)]
pub enum ResourceWithMemory {
Buffer(BufferAndMemory),
Image(ImageAndMemory),
}
impl ResourceWithMemory {
pub fn buffer(self) -> SarektResult<BufferAndMemory> {
match self {
ResourceWithMemory::Buffer(buffer) => Ok(buffer),
_ => Err(SarektError::IncorrectResourceType),
}
}
pub fn image(self) -> SarektResult<ImageAndMemory> {
match self {
ResourceWithMemory::Image(image) => Ok(image),
_ => Err(SarektError::IncorrectResourceType),
}
}
}
unsafe impl BackendHandleTrait for ResourceWithMemory {}
#[derive(Copy, Clone, Debug)]
pub struct BufferAndMemory {
pub(crate) buffer: vk::Buffer,
pub(crate) length: u32,
pub(crate) index_buffer_elem_size: Option<IndexBufferElemSize>,
pub(crate) allocation: vk_mem::Allocation,
}
#[derive(Copy, Clone, Debug)]
pub struct BufferAndMemoryMapped {
pub(crate) buffer_and_memory: BufferAndMemory,
pub(crate) ptr: *mut u8,
}
impl BufferAndMemoryMapped {
pub(crate) fn new(buffer_and_memory: BufferAndMemory, ptr: *mut u8) -> Self {
Self {
buffer_and_memory,
ptr,
}
}
}
fn usage_flags_from_buffer_type(buffer_type: BufferType) -> vk::BufferUsageFlags {
match buffer_type {
BufferType::Vertex => vk::BufferUsageFlags::VERTEX_BUFFER,
BufferType::Index(_) => vk::BufferUsageFlags::INDEX_BUFFER,
BufferType::Uniform => vk::BufferUsageFlags::UNIFORM_BUFFER,
}
}
#[derive(Copy, Clone, Debug)]
pub struct ImageAndMemory {
pub(crate) image_and_view: ImageAndView,
pub(crate) allocation: vk_mem::Allocation,
pub(crate) sampler: Option<vk::Sampler>,
}
enum ImageOrBuffer {
Buffer(vk::Buffer),
Image(vk::Image, vk::Format, vk::Extent3D),
}
impl ImageOrBuffer {
fn image(&self) -> SarektResult<(vk::Image, vk::Format, vk::Extent3D)> {
match *self {
ImageOrBuffer::Image(image, format, extent) => Ok((image, format, extent)),
_ => Err(SarektError::IncorrectResourceType),
}
}
}