Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/gpu_display/src/vulkan/external_image.rs
5394 views
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.
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)]
121
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`.
177
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 {
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 {
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
243
fn initial_layout_requirement(&self) -> ImageLayout {
244
self.layout
245
}
246
247
fn final_layout_requirement(&self) -> ImageLayout {
248
self.layout
249
}
250
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)]
258
unsafe fn layout_initialized(&self) {
259
unreachable!()
260
}
261
262
fn is_layout_initialized(&self) -> bool {
263
true
264
}
265
}
266
267