xref: /aosp_15_r20/external/crosvm/gpu_display/src/vulkan/external_image.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
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