1 // Copyright 2023 The ChromiumOS Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 use std::sync::Arc; 6 7 use anyhow::Context; 8 use anyhow::Result; 9 use ash::vk; 10 #[cfg(windows)] 11 use base::FromRawDescriptor; 12 use base::SafeDescriptor; 13 use smallvec::SmallVec; 14 use vulkano::command_buffer::sys::UnsafeCommandBufferBuilder; 15 use vulkano::device::Device; 16 use vulkano::device::DeviceOwned; 17 use vulkano::image::sys::UnsafeImage; 18 use vulkano::image::sys::UnsafeImageCreateInfo; 19 use vulkano::image::ImageAccess; 20 use vulkano::image::ImageDescriptorLayouts; 21 use vulkano::image::ImageInner; 22 use vulkano::image::ImageLayout; 23 use vulkano::image::ImageSubresourceRange; 24 use vulkano::memory::DedicatedAllocation; 25 use vulkano::memory::DeviceMemory; 26 use vulkano::memory::ExternalMemoryHandleType; 27 use vulkano::memory::ExternalMemoryHandleTypes; 28 use vulkano::memory::MemoryAllocateInfo; 29 use vulkano::memory::MemoryImportInfo; 30 use vulkano::sync::AccessFlags; 31 use vulkano::sync::DependencyInfo; 32 use vulkano::sync::ImageMemoryBarrier; 33 use vulkano::sync::PipelineStages; 34 use vulkano::sync::QueueFamilyTransfer; 35 use vulkano::DeviceSize; 36 37 pub struct AcquireImageMemoryBarrier { 38 pub source_stages: PipelineStages, 39 pub destination_stages: PipelineStages, 40 pub destination_access: AccessFlags, 41 pub destination_queue_family_index: u32, 42 pub subresource_range: ImageSubresourceRange, 43 } 44 45 pub struct ReleaseImageMemoryBarrier { 46 pub source_stages: PipelineStages, 47 pub source_access: AccessFlags, 48 pub destination_stages: PipelineStages, 49 pub new_layout: ImageLayout, 50 pub source_queue_family_index: u32, 51 } 52 53 /// ExternalImage represents a vulkan image that is imported from an external context. 54 pub struct ExternalImage { 55 image: Arc<UnsafeImage>, 56 memory: DeviceMemory, 57 } 58 59 impl ExternalImage { 60 /// Import an external image into this Device. This function will take the ownership of the 61 /// handle in `memory_import_info` on all platforms. import( device: &Arc<Device>, image_create_info: UnsafeImageCreateInfo, memory_allocate_info: MemoryAllocateInfo<'_>, memory_import_info: MemoryImportInfo, dedicated_allocation: bool, bind_offset: DeviceSize, ) -> Result<Self>62 pub fn import( 63 device: &Arc<Device>, 64 image_create_info: UnsafeImageCreateInfo, 65 memory_allocate_info: MemoryAllocateInfo<'_>, 66 memory_import_info: MemoryImportInfo, 67 dedicated_allocation: bool, 68 bind_offset: DeviceSize, 69 ) -> Result<Self> { 70 // See https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkImportMemoryWin32HandleInfoKHR.html#_description. 71 let _descriptor_to_close: Option<SafeDescriptor> = match memory_import_info { 72 #[cfg(windows)] 73 MemoryImportInfo::Win32 { 74 handle_type: ExternalMemoryHandleType::OpaqueWin32, 75 handle, 76 // SAFETY: Safe because we are consuming `memory_import_info` and do not use handle 77 // again. 78 } => Some(unsafe { SafeDescriptor::from_raw_descriptor(handle) }), 79 #[cfg(unix)] 80 MemoryImportInfo::Fd { 81 handle_type: ExternalMemoryHandleType::OpaqueFd, 82 .. 83 } => None, 84 _ => unimplemented!(), 85 }; 86 let image = UnsafeImage::new(Arc::clone(device), image_create_info) 87 .with_context(|| "create image for external memory")?; 88 let dedicated_allocation = if dedicated_allocation { 89 Some(DedicatedAllocation::Image(image.as_ref())) 90 } else { 91 None 92 }; 93 let memory_allocate_info = MemoryAllocateInfo { 94 dedicated_allocation, 95 export_handle_types: ExternalMemoryHandleTypes::empty(), 96 ..memory_allocate_info 97 }; 98 // SAFETY: Safe because `memory_import_info` and `memory_allocate_info` outlive the call 99 // to import and they contain no pointers. The handle in memory_import_info is consumed by 100 // this function and closed when this function completes. 101 let memory = unsafe { 102 DeviceMemory::import(Arc::clone(device), memory_allocate_info, memory_import_info) 103 } 104 .context("import external Vulkan device memory")?; 105 106 // SAFETY: Safe irrespective of vulkan spec conformance. 107 unsafe { image.bind_memory(&memory, bind_offset) } 108 .context("bind the image to the external memory")?; 109 Ok(Self { image, memory }) 110 } 111 112 /// Transition this image from the external source to be useable. This means performing the 113 /// layout transition that it was exported with and applying the appropriate queue family 114 /// transfers. 115 /// 116 /// # Safety 117 /// 118 /// - The ExternalImageAccess returned by this function needs to outlive 119 /// `command_buffer_builder`. 120 #[deny(unsafe_op_in_unsafe_fn)] acquire( self, command_buffer_builder: &mut UnsafeCommandBufferBuilder, image_memory_barrier: AcquireImageMemoryBarrier, last_layout_transition: (ImageLayout, ImageLayout), ) -> ExternalImageAccess121 pub unsafe fn acquire( 122 self, 123 command_buffer_builder: &mut UnsafeCommandBufferBuilder, 124 image_memory_barrier: AcquireImageMemoryBarrier, 125 last_layout_transition: (ImageLayout, ImageLayout), 126 ) -> ExternalImageAccess { 127 let dep_info = DependencyInfo { 128 image_memory_barriers: SmallVec::from_vec(vec![ImageMemoryBarrier { 129 source_stages: image_memory_barrier.source_stages, 130 // The acquire queue transfer will ignore the `srcAccessMask`. 131 source_access: AccessFlags::empty(), 132 destination_stages: image_memory_barrier.destination_stages, 133 destination_access: image_memory_barrier.destination_access, 134 old_layout: last_layout_transition.0, 135 new_layout: last_layout_transition.1, 136 queue_family_transfer: Some(QueueFamilyTransfer { 137 source_index: vk::QUEUE_FAMILY_EXTERNAL, 138 destination_index: image_memory_barrier.destination_queue_family_index, 139 }), 140 subresource_range: image_memory_barrier.subresource_range.clone(), 141 ..ImageMemoryBarrier::image(Arc::clone(&self.image)) 142 }]), 143 ..Default::default() 144 }; 145 146 // SAFETY: Safe irrespective of vulkan spec conformance: `pipeline_barriers` copies all of 147 // the contents of `dep_info` into new structs, so dep_info itself does not need to outlive 148 // this function call. Safety comments for this function require caller to ensure this 149 // object outlives the command_buffer. 150 unsafe { command_buffer_builder.pipeline_barrier(&dep_info) }; 151 152 ExternalImageAccess { 153 image: self.image, 154 memory: self.memory, 155 layout: last_layout_transition.1, 156 subresource_range: image_memory_barrier.subresource_range, 157 } 158 } 159 } 160 161 #[derive(Debug)] 162 /// ExternalImageAccess represents a vulkan image that is imported from an external context and 163 /// transitioned for use by another context. 164 pub struct ExternalImageAccess { 165 image: Arc<UnsafeImage>, 166 memory: DeviceMemory, 167 layout: ImageLayout, 168 subresource_range: ImageSubresourceRange, 169 } 170 171 impl ExternalImageAccess { 172 /// Transition this image back to an ExternalImage, after which it can be used by other 173 /// contexts. This undoes the queue family and layout transitions done by acquire. 174 /// # Safety 175 /// 176 /// - The ExternalImage returned by this function needs to outlive `command_buffer_builder`. release( self, command_buffer_builder: &mut UnsafeCommandBufferBuilder, image_memory_barrier: ReleaseImageMemoryBarrier, ) -> ExternalImage177 pub unsafe fn release( 178 self, 179 command_buffer_builder: &mut UnsafeCommandBufferBuilder, 180 image_memory_barrier: ReleaseImageMemoryBarrier, 181 ) -> ExternalImage { 182 let old_layout = self.layout; 183 let new_layout = image_memory_barrier.new_layout; 184 let dep_info = DependencyInfo { 185 image_memory_barriers: SmallVec::from_vec(vec![ImageMemoryBarrier { 186 source_stages: image_memory_barrier.source_stages, 187 source_access: image_memory_barrier.source_access, 188 destination_stages: image_memory_barrier.destination_stages, 189 // The release queue transfer will ignore the `dstAccessMask`. 190 destination_access: AccessFlags::empty(), 191 old_layout, 192 new_layout, 193 queue_family_transfer: Some(QueueFamilyTransfer { 194 source_index: image_memory_barrier.source_queue_family_index, 195 destination_index: vk::QUEUE_FAMILY_EXTERNAL, 196 }), 197 subresource_range: self.subresource_range, 198 ..ImageMemoryBarrier::image(Arc::clone(&self.image)) 199 }]), 200 ..Default::default() 201 }; 202 // SAFETY: Safe irrespective of vulkan spec conformance: `pipeline_barriers` copies all of 203 // the contents of `dep_info` into new structs, so dep_info itself does not need to 204 // outlive this function call. Safety comments for this function require caller to 205 // ensure this object outlives the command_buffer. 206 unsafe { command_buffer_builder.pipeline_barrier(&dep_info) }; 207 ExternalImage { 208 image: self.image, 209 memory: self.memory, 210 } 211 } 212 } 213 214 // SAFETY: Safe irrespective of vulkan spec conformance. 215 unsafe impl DeviceOwned for ExternalImageAccess { device(&self) -> &Arc<Device>216 fn device(&self) -> &Arc<Device> { 217 self.image.device() 218 } 219 } 220 221 // SAFETY: Safe irrespective of vulkan spec conformance. 222 unsafe impl ImageAccess for ExternalImageAccess { inner(&self) -> ImageInner<'_>223 fn inner(&self) -> ImageInner<'_> { 224 ImageInner { 225 image: &self.image, 226 first_layer: self.subresource_range.array_layers.start, 227 num_layers: self 228 .subresource_range 229 .array_layers 230 .len() 231 .try_into() 232 .expect("number of layers too large"), 233 first_mipmap_level: self.subresource_range.mip_levels.start, 234 num_mipmap_levels: self 235 .subresource_range 236 .mip_levels 237 .len() 238 .try_into() 239 .expect("number of mip levels too large"), 240 } 241 } 242 initial_layout_requirement(&self) -> ImageLayout243 fn initial_layout_requirement(&self) -> ImageLayout { 244 self.layout 245 } 246 final_layout_requirement(&self) -> ImageLayout247 fn final_layout_requirement(&self) -> ImageLayout { 248 self.layout 249 } 250 descriptor_layouts(&self) -> Option<ImageDescriptorLayouts>251 fn descriptor_layouts(&self) -> Option<ImageDescriptorLayouts> { 252 todo!() 253 } 254 255 // The image layout should have been transitioned to the `self.layout` at the creation time of 256 // this struct. 257 #[deny(unsafe_op_in_unsafe_fn)] layout_initialized(&self)258 unsafe fn layout_initialized(&self) { 259 unreachable!() 260 } 261 is_layout_initialized(&self) -> bool262 fn is_layout_initialized(&self) -> bool { 263 true 264 } 265 } 266