use std::sync::Arc; use ash::vk; use crate::*; /// `DescriptorSet` is a collection of buffers that can be bound to a shader. /// It depends on a DescriptorSetLayout which defines linkage to the shader. /// This structure does not need shader directly, shader will be provided by `Pipeline`. /// It can be reused between different pipelines and shaders with the same layout. #[derive(Clone)] pub struct DescriptorSet { /// Device that owns the descriptor set. device: Arc, /// Descriptor set layout that defines linkage to the shader. /// Keep it alive to prevent it from being dropped. _layout: Arc, /// Collection of uniform buffers. uniform_buffers: Vec<(usize, Arc)>, /// Collection of storage buffers. storage_buffers: Vec<(usize, Arc)>, /// Native Vulkan descriptor pool handle. vk_descriptor_pool: vk::DescriptorPool, /// Native Vulkan descriptor set handle. vk_descriptor_set: vk::DescriptorSet, } pub struct DescriptorSetBuilder { descriptor_set_layout: Arc, uniform_buffers: Vec<(usize, Arc)>, storage_buffers: Vec<(usize, Arc)>, } // Mark `DescriptorSet` as a GPU resource that should be kept alive while it's in use by the GPU context. impl Resource for DescriptorSet {} impl DescriptorSetBuilder { pub fn add_uniform_buffer(mut self, binding: usize, uniform_buffer: Arc) -> Self { self.uniform_buffers.push((binding, uniform_buffer)); self } pub fn add_storage_buffer(mut self, binding: usize, storage_buffer: Arc) -> Self { self.storage_buffers.push((binding, storage_buffer)); self } pub fn build(&self) -> GpuResult> { DescriptorSet::new( self.descriptor_set_layout.device().clone(), self.descriptor_set_layout.clone(), self.uniform_buffers.clone(), self.storage_buffers.clone(), ) } } impl DescriptorSet { pub fn builder(descriptor_set_layout: Arc) -> DescriptorSetBuilder { DescriptorSetBuilder { descriptor_set_layout, uniform_buffers: Vec::new(), storage_buffers: Vec::new(), } } fn new( device: Arc, layout: Arc, uniform_buffers: Vec<(usize, Arc)>, storage_buffers: Vec<(usize, Arc)>, ) -> GpuResult> { // Vulkan descriptor pool is required to allocate descriptor sets. let vk_descriptor_pool = Self::create_vk_descriptor_pool(&device, &uniform_buffers, &storage_buffers)?; // Create Vulkan descriptor set. let vk_descriptor_set_layouts = vec![layout.vk_descriptor_set_layout()]; let vk_descriptor_set_result = Self::create_vk_descriptor_set(&device, &vk_descriptor_set_layouts, vk_descriptor_pool); let vk_descriptor_set = match vk_descriptor_set_result { Ok(vk_descriptor_set) => vk_descriptor_set, Err(error) => { unsafe { // Destroy descriptor pool if descriptor set creation failed. device.vk_device().destroy_descriptor_pool( vk_descriptor_pool, device.cpu_allocation_callbacks(), ); } return Err(error); } }; let result = Arc::new(Self { device, _layout: layout, uniform_buffers, storage_buffers, vk_descriptor_pool, vk_descriptor_set, }); result.update()?; Ok(result) } pub fn vk_descriptor_set(&self) -> vk::DescriptorSet { self.vk_descriptor_set } fn create_vk_descriptor_pool( device: &Arc, uniform_buffers: &[(usize, Arc)], storage_buffers: &[(usize, Arc)], ) -> GpuResult { let mut vk_descriptor_pool_sizes = Vec::new(); if !uniform_buffers.is_empty() { vk_descriptor_pool_sizes.push( vk::DescriptorPoolSize::default() .ty(vk::DescriptorType::UNIFORM_BUFFER) .descriptor_count(uniform_buffers.len() as u32), ); } if !storage_buffers.is_empty() { vk_descriptor_pool_sizes.push( vk::DescriptorPoolSize::default() .ty(vk::DescriptorType::STORAGE_BUFFER) .descriptor_count(storage_buffers.len() as u32), ); } if !vk_descriptor_pool_sizes.is_empty() { let vk_descriptor_pool_create_info = vk::DescriptorPoolCreateInfo::default() .pool_sizes(&vk_descriptor_pool_sizes) .max_sets(1); unsafe { device .vk_device() .create_descriptor_pool( &vk_descriptor_pool_create_info, device.cpu_allocation_callbacks(), ) .map_err(GpuError::from) } } else { Err(GpuError::Other( "DescriptorSet must have at least one uniform or storage buffer".to_string(), )) } } fn create_vk_descriptor_set( device: &Arc, vk_descriptor_set_layout: &[vk::DescriptorSetLayout], vk_descriptor_pool: vk::DescriptorPool, ) -> GpuResult { let vk_descriptor_set_allocate_info = vk::DescriptorSetAllocateInfo::default() .descriptor_pool(vk_descriptor_pool) .set_layouts(vk_descriptor_set_layout); unsafe { Ok(device .vk_device() .allocate_descriptor_sets(&vk_descriptor_set_allocate_info)?[0]) } } fn update(&self) -> GpuResult<()> { // Collected parameters for vk::update_descriptor_sets let mut vk_write_descriptor_sets: Vec = vec![]; // `vk_descriptor_uniform_buffer_infos` is a collection of uniform buffers. // It should be alive because `vk_write_descriptor_sets` references pointer to it. let mut vk_descriptor_uniform_buffer_infos = Vec::new(); for (_binding, uniform_buffer) in &self.uniform_buffers { if uniform_buffer.buffer_type() != BufferType::Uniform { return Err(GpuError::Other( "Uniform buffer type must be `BufferType::Uniform`".to_string(), )); } let vk_descriptor_buffer_info = vk::DescriptorBufferInfo::default() .buffer(uniform_buffer.vk_buffer()) .offset(0) .range(uniform_buffer.size() as u64); vk_descriptor_uniform_buffer_infos.push(vec![vk_descriptor_buffer_info]); } for ((binding, _uniform_buffer), buffer_infos) in self .uniform_buffers .iter() .zip(vk_descriptor_uniform_buffer_infos.iter()) { let vk_write_descriptor_set = vk::WriteDescriptorSet::default() .dst_set(self.vk_descriptor_set) .dst_binding(*binding as u32) .dst_array_element(0) .descriptor_type(vk::DescriptorType::UNIFORM_BUFFER) .buffer_info(buffer_infos); vk_write_descriptor_sets.push(vk_write_descriptor_set); } // `vk_descriptor_storage_buffer_infos` is a collection of storage buffers. // It should be alive because `vk_write_descriptor_sets` references pointer to it. let mut vk_descriptor_storage_buffer_infos = Vec::new(); for (_binding, storage_buffer) in &self.storage_buffers { if storage_buffer.buffer_type() != BufferType::Storage { return Err(GpuError::Other( "Storage buffer type must be `BufferType::Storage`".to_string(), )); } let vk_descriptor_buffer_info = vk::DescriptorBufferInfo::default() .buffer(storage_buffer.vk_buffer()) .offset(0) .range(storage_buffer.size() as u64); vk_descriptor_storage_buffer_infos.push(vec![vk_descriptor_buffer_info]); } for ((binding, _storage_buffer), buffer_info) in self .storage_buffers .iter() .zip(vk_descriptor_storage_buffer_infos.iter()) { let vk_write_descriptor_set = vk::WriteDescriptorSet::default() .dst_set(self.vk_descriptor_set) .dst_binding(*binding as u32) .dst_array_element(0) .descriptor_type(vk::DescriptorType::STORAGE_BUFFER) .buffer_info(buffer_info); vk_write_descriptor_sets.push(vk_write_descriptor_set); } unsafe { self.device .vk_device() .update_descriptor_sets(&vk_write_descriptor_sets, &[]); } Ok(()) } } impl Drop for DescriptorSet { fn drop(&mut self) { unsafe { if self.vk_descriptor_pool != vk::DescriptorPool::null() { self.device.vk_device().destroy_descriptor_pool( self.vk_descriptor_pool, self.device.cpu_allocation_callbacks(), ); self.vk_descriptor_pool = vk::DescriptorPool::null() } } self.uniform_buffers.clear(); self.storage_buffers.clear(); } }