1use crate::device::VulkanContext;
17use crate::resource::{backend_err, vk_format, TextureMemory, VulkanTexture};
18use ash::vk;
19use impeller_hal::{
20 Error, Extent2D, FormatModifierSet, Modifier, PixelFormat, Result, TextureUsage,
21};
22
23pub fn query_format_modifiers(
29 instance: &ash::Instance,
30 physical_device: vk::PhysicalDevice,
31 format: PixelFormat,
32) -> Option<FormatModifierSet> {
33 let fourcc = format.fourcc()?;
34
35 let mut list = vk::DrmFormatModifierPropertiesListEXT::default();
38 let mut properties = vk::FormatProperties2::default().push_next(&mut list);
39 unsafe {
41 instance.get_physical_device_format_properties2(
42 physical_device,
43 vk_format(format),
44 &mut properties,
45 );
46 }
47
48 let count = list.drm_format_modifier_count as usize;
49 if count == 0 {
50 return None;
51 }
52 let mut entries = vec![vk::DrmFormatModifierPropertiesEXT::default(); count];
53 list.p_drm_format_modifier_properties = entries.as_mut_ptr();
54 let mut properties = vk::FormatProperties2::default().push_next(&mut list);
55 unsafe {
57 instance.get_physical_device_format_properties2(
58 physical_device,
59 vk_format(format),
60 &mut properties,
61 );
62 }
63
64 let modifiers: Vec<Modifier> = entries
65 .iter()
66 .take(count)
67 .filter(|e| {
70 e.drm_format_modifier_tiling_features
71 .contains(vk::FormatFeatureFlags::COLOR_ATTACHMENT)
72 })
73 .map(|e| Modifier(e.drm_format_modifier))
74 .collect();
75
76 if modifiers.is_empty() {
77 return None;
78 }
79 Some(FormatModifierSet::new(fourcc, modifiers))
80}
81
82impl VulkanContext {
83 pub fn create_exportable_texture(
90 &mut self,
91 extent: Extent2D,
92 format: PixelFormat,
93 modifiers: &[Modifier],
94 ) -> Result<VulkanTexture> {
95 if !self.capabilities().dma_buf.can_allocate_scanout() {
96 return Err(Error::Unsupported(
97 "exporting images requires dma-buf export and explicit modifiers",
98 ));
99 }
100 if modifiers.is_empty() {
101 return Err(Error::Unsupported("no candidate modifiers were offered"));
102 }
103 if !self.capabilities().can_allocate(extent) {
104 return Err(Error::LimitExceeded {
105 what: "texture dimension",
106 requested: extent.width.max(extent.height) as u64,
107 limit: self.capabilities().max_texture_size as u64,
108 });
109 }
110
111 let device = self.raw_device().clone();
112 let raw_modifiers: Vec<u64> = modifiers.iter().map(|m| m.0).collect();
113
114 let mut modifier_list = vk::ImageDrmFormatModifierListCreateInfoEXT::default()
115 .drm_format_modifiers(&raw_modifiers);
116 let mut external = vk::ExternalMemoryImageCreateInfo::default()
117 .handle_types(vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT);
118
119 let info = vk::ImageCreateInfo::default()
120 .image_type(vk::ImageType::TYPE_2D)
121 .format(vk_format(format))
122 .extent(vk::Extent3D {
123 width: extent.width,
124 height: extent.height,
125 depth: 1,
126 })
127 .mip_levels(1)
128 .array_layers(1)
129 .samples(vk::SampleCountFlags::TYPE_1)
130 .tiling(vk::ImageTiling::DRM_FORMAT_MODIFIER_EXT)
134 .usage(vk::ImageUsageFlags::COLOR_ATTACHMENT | vk::ImageUsageFlags::TRANSFER_SRC)
135 .sharing_mode(vk::SharingMode::EXCLUSIVE)
136 .initial_layout(vk::ImageLayout::UNDEFINED)
137 .push_next(&mut modifier_list)
138 .push_next(&mut external);
139
140 let image = unsafe { device.create_image(&info, None) }
143 .map_err(|e| backend_err("create_image (exportable)", e))?;
144
145 let requirements = unsafe { device.get_image_memory_requirements(image) };
146 let memory_type = match self.find_memory_type(
147 requirements.memory_type_bits,
148 vk::MemoryPropertyFlags::DEVICE_LOCAL,
149 ) {
150 Some(index) => index,
151 None => {
152 unsafe { device.destroy_image(image, None) };
153 return Err(Error::Backend {
154 backend: "vulkan",
155 detail: "no device-local memory type accepts an exportable image".into(),
156 });
157 }
158 };
159
160 let mut dedicated = vk::MemoryDedicatedAllocateInfo::default().image(image);
164 let mut export = vk::ExportMemoryAllocateInfo::default()
165 .handle_types(vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT);
166 let allocate = vk::MemoryAllocateInfo::default()
167 .allocation_size(requirements.size)
168 .memory_type_index(memory_type)
169 .push_next(&mut dedicated)
170 .push_next(&mut export);
171
172 let memory = match unsafe { device.allocate_memory(&allocate, None) } {
173 Ok(memory) => memory,
174 Err(e) => {
175 unsafe { device.destroy_image(image, None) };
176 return Err(backend_err("allocate_memory (exportable)", e));
177 }
178 };
179
180 if let Err(e) = unsafe { device.bind_image_memory(image, memory, 0) } {
181 unsafe {
182 device.free_memory(memory, None);
183 device.destroy_image(image, None);
184 }
185 return Err(backend_err("bind_image_memory (exportable)", e));
186 }
187
188 Ok(VulkanTexture {
189 image,
190 memory: TextureMemory::Dedicated(memory),
191 extent,
192 format,
193 layout: std::cell::Cell::new(vk::ImageLayout::UNDEFINED),
194 mip_levels: 1,
197 usage: TextureUsage {
198 render_target: true,
199 transfer: true,
200 scanout: true,
201 ..TextureUsage::default()
202 },
203 })
204 }
205
206 pub fn texture_modifier(&self, texture: &VulkanTexture) -> Result<Modifier> {
208 if !texture.usage.scanout {
209 return Err(Error::Unsupported(
210 "only an exportable image has a negotiated modifier",
211 ));
212 }
213 let loader = ash::ext::image_drm_format_modifier::Device::new(
214 self.raw_instance(),
215 self.raw_device(),
216 );
217 let mut properties = vk::ImageDrmFormatModifierPropertiesEXT::default();
218 unsafe {
221 loader
222 .get_image_drm_format_modifier_properties(texture.image, &mut properties)
223 .map_err(|e| backend_err("get_image_drm_format_modifier_properties", e))?;
224 }
225 Ok(Modifier(properties.drm_format_modifier))
226 }
227
228 #[cfg(unix)]
234 pub fn export_texture(
235 &self,
236 texture: &VulkanTexture,
237 ) -> Result<impeller_hal::ExternalImageDesc> {
238 use std::os::fd::FromRawFd;
239
240 if !texture.usage.scanout {
241 return Err(Error::Unsupported(
242 "only an image created for export can be exported",
243 ));
244 }
245 let fourcc = texture.format.fourcc().ok_or(Error::Unsupported(
246 "this format has no scanout representation",
247 ))?;
248 let modifier = self.texture_modifier(texture)?;
249
250 let memory = match &texture.memory {
251 TextureMemory::Dedicated(memory) => *memory,
252 TextureMemory::Pooled(_) => {
253 return Err(Error::Unsupported(
254 "a suballocated image cannot be exported on its own",
255 ))
256 }
257 TextureMemory::Borrowed => {
260 return Err(Error::Unsupported(
261 "an image this backend did not allocate cannot be exported",
262 ))
263 }
264 };
265
266 let loader =
267 ash::khr::external_memory_fd::Device::new(self.raw_instance(), self.raw_device());
268 let info = vk::MemoryGetFdInfoKHR::default()
269 .memory(memory)
270 .handle_type(vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT);
271 let raw =
273 unsafe { loader.get_memory_fd(&info) }.map_err(|e| backend_err("get_memory_fd", e))?;
274
275 let fd = unsafe { std::os::fd::OwnedFd::from_raw_fd(raw) };
279
280 let subresource =
281 vk::ImageSubresource::default().aspect_mask(vk::ImageAspectFlags::MEMORY_PLANE_0_EXT);
282 let layout = unsafe {
284 self.raw_device()
285 .get_image_subresource_layout(texture.image, subresource)
286 };
287
288 Ok(impeller_hal::ExternalImageDesc {
289 planes: vec![impeller_hal::DmaBufPlane {
290 fd,
291 offset: layout.offset as u32,
292 stride: layout.row_pitch as u32,
293 }],
294 fourcc,
295 modifier,
296 })
297 }
298
299 fn find_memory_type(&self, type_bits: u32, flags: vk::MemoryPropertyFlags) -> Option<u32> {
300 let properties = unsafe {
302 self.raw_instance()
303 .get_physical_device_memory_properties(self.raw_physical_device())
304 };
305 (0..properties.memory_type_count).find(|i| {
306 let usable = type_bits & (1 << i) != 0;
307 usable
308 && properties.memory_types[*i as usize]
309 .property_flags
310 .contains(flags)
311 })
312 }
313}
314
315pub fn render_formats(
317 instance: &ash::Instance,
318 physical_device: vk::PhysicalDevice,
319) -> Vec<FormatModifierSet> {
320 [
321 PixelFormat::Bgra8Unorm,
322 PixelFormat::Rgba8Unorm,
323 PixelFormat::Rgb10A2Unorm,
324 ]
325 .into_iter()
326 .filter_map(|format| query_format_modifiers(instance, physical_device, format))
327 .collect()
328}