atagen

monolith

public
Star0
Watch0
189b5d01e76ff8c5598e821788f769777debeb30
1
ssh://brewery@pls.lobotomise.me:33/atagen/monolith.git
crates/render-vk/src/dmabuf.rsRust395 lines13.7 KB
1
use crate::Error;
2
use crate::device::Shared;
3
use crate::format::{SUPPORTED, vk_format};
4
use crate::image::{ImageInner, Memory};
5
use ash::vk;
6
use smithay::backend::allocator::dmabuf::{Dmabuf, DmabufFlags};
7
use smithay::backend::allocator::format::FormatSet;
8
use smithay::backend::allocator::{Allocator, Buffer, Format, Fourcc, Modifier};
9
use std::os::fd::{AsRawFd, FromRawFd, IntoRawFd, OwnedFd};
10
use std::sync::Arc;
11
12
/// Which (format, modifier) pairs the device can import for sampling and
13
/// render into, queried once at startup.
14
#[derive(Debug, Default)]
15
pub(crate) struct DmabufCaps {
16
    pub sample: FormatSet,
17
    pub render: FormatSet,
18
}
19
20
pub(crate) const RENDER_USAGE: vk::ImageUsageFlags = vk::ImageUsageFlags::from_raw(
21
    vk::ImageUsageFlags::COLOR_ATTACHMENT.as_raw()
22
        | vk::ImageUsageFlags::SAMPLED.as_raw()
23
        | vk::ImageUsageFlags::TRANSFER_SRC.as_raw(),
24
);
25
pub(crate) const SAMPLE_USAGE: vk::ImageUsageFlags = vk::ImageUsageFlags::SAMPLED;
26
27
impl DmabufCaps {
28
    pub fn query(shared: &Shared) -> Self {
29
        if shared.device.dmabuf.is_none() {
30
            return Self::default();
31
        }
32
        let (mut sample, mut render) = (Vec::new(), Vec::new());
33
        for fourcc in SUPPORTED {
34
            let Some(fmt) = vk_format(fourcc) else {
35
                continue;
36
            };
37
            for props in modifier_properties(shared, fmt.format) {
38
                if props.drm_format_modifier_plane_count != 1 {
39
                    continue;
40
                }
41
                let features = props.drm_format_modifier_tiling_features;
42
                let format = Format {
43
                    code: fourcc,
44
                    modifier: Modifier::from(props.drm_format_modifier),
45
                };
46
                let supports =
47
                    |usage| external_support(shared, fmt.format, props.drm_format_modifier, usage);
48
                if features.contains(vk::FormatFeatureFlags::SAMPLED_IMAGE)
49
                    && supports(SAMPLE_USAGE).importable
50
                {
51
                    sample.push(format);
52
                }
53
                let renderable = vk::FormatFeatureFlags::COLOR_ATTACHMENT
54
                    | vk::FormatFeatureFlags::COLOR_ATTACHMENT_BLEND;
55
                let s = supports(RENDER_USAGE);
56
                if features.contains(renderable) && s.importable && s.exportable {
57
                    render.push(format);
58
                }
59
            }
60
        }
61
        Self {
62
            sample: sample.into_iter().collect(),
63
            render: render.into_iter().collect(),
64
        }
65
    }
66
}
67
68
fn modifier_properties(
69
    shared: &Shared,
70
    format: vk::Format,
71
) -> Vec<vk::DrmFormatModifierPropertiesEXT> {
72
    let dev = &shared.device;
73
    let mut list = vk::DrmFormatModifierPropertiesListEXT::default();
74
    let mut props = vk::FormatProperties2::default().push_next(&mut list);
75
    unsafe {
76
        dev.instance
77
            .get_physical_device_format_properties2(dev.physical, format, &mut props)
78
    };
79
    let count = list.drm_format_modifier_count as usize;
80
    let mut out = vec![vk::DrmFormatModifierPropertiesEXT::default(); count];
81
    let mut list =
82
        vk::DrmFormatModifierPropertiesListEXT::default().drm_format_modifier_properties(&mut out);
83
    let mut props = vk::FormatProperties2::default().push_next(&mut list);
84
    unsafe {
85
        dev.instance
86
            .get_physical_device_format_properties2(dev.physical, format, &mut props)
87
    };
88
    out
89
}
90
91
struct External {
92
    importable: bool,
93
    exportable: bool,
94
}
95
96
fn external_support(
97
    shared: &Shared,
98
    format: vk::Format,
99
    modifier: u64,
100
    usage: vk::ImageUsageFlags,
101
) -> External {
102
    let dev = &shared.device;
103
    let mut modifier_info = vk::PhysicalDeviceImageDrmFormatModifierInfoEXT::default()
104
        .drm_format_modifier(modifier)
105
        .sharing_mode(vk::SharingMode::EXCLUSIVE);
106
    let mut external_info = vk::PhysicalDeviceExternalImageFormatInfo::default()
107
        .handle_type(vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT);
108
    let info = vk::PhysicalDeviceImageFormatInfo2::default()
109
        .format(format)
110
        .ty(vk::ImageType::TYPE_2D)
111
        .tiling(vk::ImageTiling::DRM_FORMAT_MODIFIER_EXT)
112
        .usage(usage)
113
        .push_next(&mut external_info)
114
        .push_next(&mut modifier_info);
115
    let mut external_props = vk::ExternalImageFormatProperties::default();
116
    let mut props = vk::ImageFormatProperties2::default().push_next(&mut external_props);
117
    let ok = unsafe {
118
        dev.instance
119
            .get_physical_device_image_format_properties2(dev.physical, &info, &mut props)
120
    }
121
    .is_ok();
122
    let features = external_props
123
        .external_memory_properties
124
        .external_memory_features;
125
    External {
126
        importable: ok && features.contains(vk::ExternalMemoryFeatureFlags::IMPORTABLE),
127
        exportable: ok && features.contains(vk::ExternalMemoryFeatureFlags::EXPORTABLE),
128
    }
129
}
130
131
fn memory_type(shared: &Shared, bits: u32, prefer: vk::MemoryPropertyFlags) -> Option<u32> {
132
    let props = unsafe {
133
        shared
134
            .device
135
            .instance
136
            .get_physical_device_memory_properties(shared.device.physical)
137
    };
138
    let types = &props.memory_types[..props.memory_type_count as usize];
139
    let allowed = |i: usize| bits & (1 << i) != 0;
140
    (0..types.len())
141
        .find(|&i| allowed(i) && types[i].property_flags.contains(prefer))
142
        .or_else(|| (0..types.len()).find(|&i| allowed(i)))
143
        .map(|i| i as u32)
144
}
145
146
fn image_info<'a>(
147
    fourcc: Fourcc,
148
    size: (u32, u32),
149
    usage: vk::ImageUsageFlags,
150
) -> Result<vk::ImageCreateInfo<'a>, Error> {
151
    let fmt = vk_format(fourcc).ok_or(Error::UnsupportedFormat(fourcc))?;
152
    Ok(vk::ImageCreateInfo::default()
153
        .image_type(vk::ImageType::TYPE_2D)
154
        .format(fmt.format)
155
        .extent(vk::Extent3D {
156
            width: size.0,
157
            height: size.1,
158
            depth: 1,
159
        })
160
        .mip_levels(1)
161
        .array_layers(1)
162
        .samples(vk::SampleCountFlags::TYPE_1)
163
        .tiling(vk::ImageTiling::DRM_FORMAT_MODIFIER_EXT)
164
        .usage(usage)
165
        .sharing_mode(vk::SharingMode::EXCLUSIVE)
166
        .initial_layout(vk::ImageLayout::UNDEFINED))
167
}
168
169
/// Imports a single-plane dmabuf as an image, without copying.
170
pub(crate) fn import(
171
    shared: &Arc<Shared>,
172
    caps: &DmabufCaps,
173
    dmabuf: &Dmabuf,
174
    usage: vk::ImageUsageFlags,
175
) -> Result<Arc<ImageInner>, Error> {
176
    let ext = shared
177
        .device
178
        .dmabuf
179
        .as_ref()
180
        .ok_or(Error::DmabufUnsupported)?;
181
    let format = dmabuf.format();
182
    let set = if usage.contains(vk::ImageUsageFlags::COLOR_ATTACHMENT) {
183
        &caps.render
184
    } else {
185
        &caps.sample
186
    };
187
    if dmabuf.num_planes() != 1 || !set.contains(&format) {
188
        return Err(Error::UnsupportedDmabuf(format));
189
    }
190
    let (Some(fd), Some(offset), Some(stride)) = (
191
        dmabuf.handles().next(),
192
        dmabuf.offsets().next(),
193
        dmabuf.strides().next(),
194
    ) else {
195
        return Err(Error::UnsupportedDmabuf(format));
196
    };
197
    let size = (dmabuf.width(), dmabuf.height());
198
    let d = &shared.device.device;
199
    let layouts = [vk::SubresourceLayout {
200
        offset: u64::from(offset),
201
        size: 0,
202
        row_pitch: u64::from(stride),
203
        array_pitch: 0,
204
        depth_pitch: 0,
205
    }];
206
    let mut explicit = vk::ImageDrmFormatModifierExplicitCreateInfoEXT::default()
207
        .drm_format_modifier(u64::from(format.modifier))
208
        .plane_layouts(&layouts);
209
    let mut external = vk::ExternalMemoryImageCreateInfo::default()
210
        .handle_types(vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT);
211
    let info = image_info(format.code, size, usage)?
212
        .push_next(&mut external)
213
        .push_next(&mut explicit);
214
    let image = unsafe { d.create_image(&info, None)? };
215
    let destroy = || unsafe { d.destroy_image(image, None) };
216
    let owned = match fd.try_clone_to_owned() {
217
        Ok(o) => o,
218
        Err(_) => {
219
            destroy();
220
            return Err(Error::UnsupportedDmabuf(format));
221
        }
222
    };
223
    let mut fd_props = vk::MemoryFdPropertiesKHR::default();
224
    if let Err(e) = unsafe {
225
        ext.memory_fd.get_memory_fd_properties(
226
            vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT,
227
            owned.as_raw_fd(),
228
            &mut fd_props,
229
        )
230
    } {
231
        destroy();
232
        return Err(e.into());
233
    }
234
    let requirements = unsafe { d.get_image_memory_requirements(image) };
235
    let Some(type_index) = memory_type(
236
        shared,
237
        requirements.memory_type_bits & fd_props.memory_type_bits,
238
        vk::MemoryPropertyFlags::empty(),
239
    ) else {
240
        destroy();
241
        return Err(Error::UnsupportedDmabuf(format));
242
    };
243
    let raw = owned.into_raw_fd();
244
    let mut import = vk::ImportMemoryFdInfoKHR::default()
245
        .handle_type(vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT)
246
        .fd(raw);
247
    let mut dedicated = vk::MemoryDedicatedAllocateInfo::default().image(image);
248
    let alloc = vk::MemoryAllocateInfo::default()
249
        .allocation_size(requirements.size)
250
        .memory_type_index(type_index)
251
        .push_next(&mut import)
252
        .push_next(&mut dedicated);
253
    let memory = match unsafe { d.allocate_memory(&alloc, None) } {
254
        Ok(m) => m,
255
        Err(e) => {
256
            // SAFETY: a failed import leaves the fd with us, per
257
            // VK_KHR_external_memory_fd.
258
            drop(unsafe { OwnedFd::from_raw_fd(raw) });
259
            destroy();
260
            return Err(e.into());
261
        }
262
    };
263
    ImageInner::finish(
264
        shared,
265
        image,
266
        Memory::Imported(memory),
267
        memory,
268
        0,
269
        format.code,
270
        size.0,
271
        size.1,
272
        true,
273
    )
274
}
275
276
/// Allocates a dmabuf the device can render into, picking among `modifiers`.
277
pub(crate) fn export(
278
    shared: &Arc<Shared>,
279
    caps: &DmabufCaps,
280
    fourcc: Fourcc,
281
    size: (u32, u32),
282
    modifiers: &[Modifier],
283
) -> Result<Dmabuf, Error> {
284
    let ext = shared
285
        .device
286
        .dmabuf
287
        .as_ref()
288
        .ok_or(Error::DmabufUnsupported)?;
289
    let allowed: Vec<u64> = modifiers
290
        .iter()
291
        .filter(|&&modifier| {
292
            caps.render.contains(&Format {
293
                code: fourcc,
294
                modifier,
295
            })
296
        })
297
        .map(|&m| u64::from(m))
298
        .collect();
299
    if allowed.is_empty() {
300
        return Err(Error::UnsupportedFormat(fourcc));
301
    }
302
    let d = &shared.device.device;
303
    let mut list =
304
        vk::ImageDrmFormatModifierListCreateInfoEXT::default().drm_format_modifiers(&allowed);
305
    let mut external = vk::ExternalMemoryImageCreateInfo::default()
306
        .handle_types(vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT);
307
    let info = image_info(fourcc, size, RENDER_USAGE)?
308
        .push_next(&mut external)
309
        .push_next(&mut list);
310
    let image = unsafe { d.create_image(&info, None)? };
311
    let result = (|| -> Result<Dmabuf, Error> {
312
        let requirements = unsafe { d.get_image_memory_requirements(image) };
313
        let type_index = memory_type(
314
            shared,
315
            requirements.memory_type_bits,
316
            vk::MemoryPropertyFlags::DEVICE_LOCAL,
317
        )
318
        .ok_or(Error::UnsupportedFormat(fourcc))?;
319
        let mut export = vk::ExportMemoryAllocateInfo::default()
320
            .handle_types(vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT);
321
        let mut dedicated = vk::MemoryDedicatedAllocateInfo::default().image(image);
322
        let alloc = vk::MemoryAllocateInfo::default()
323
            .allocation_size(requirements.size)
324
            .memory_type_index(type_index)
325
            .push_next(&mut export)
326
            .push_next(&mut dedicated);
327
        let memory = unsafe { d.allocate_memory(&alloc, None)? };
328
        let exported = (|| -> Result<Dmabuf, Error> {
329
            unsafe { d.bind_image_memory(image, memory, 0)? };
330
            let raw = unsafe {
331
                ext.memory_fd.get_memory_fd(
332
                    &vk::MemoryGetFdInfoKHR::default()
333
                        .memory(memory)
334
                        .handle_type(vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT),
335
                )?
336
            };
337
            // SAFETY: vkGetMemoryFdKHR hands the caller a new fd to own.
338
            let fd = unsafe { OwnedFd::from_raw_fd(raw) };
339
            let mut props = vk::ImageDrmFormatModifierPropertiesEXT::default();
340
            unsafe {
341
                ext.modifier
342
                    .get_image_drm_format_modifier_properties(image, &mut props)?
343
            };
344
            let layout = unsafe {
345
                d.get_image_subresource_layout(
346
                    image,
347
                    vk::ImageSubresource::default()
348
                        .aspect_mask(vk::ImageAspectFlags::MEMORY_PLANE_0_EXT),
349
                )
350
            };
351
            let mut builder = Dmabuf::builder(
352
                (size.0 as i32, size.1 as i32),
353
                fourcc,
354
                Modifier::from(props.drm_format_modifier),
355
                DmabufFlags::empty(),
356
            );
357
            builder.add_plane(fd, layout.offset as u32, layout.row_pitch as u32);
358
            builder.build().ok_or(Error::UnsupportedFormat(fourcc))
359
        })();
360
        unsafe { d.free_memory(memory, None) };
361
        exported
362
    })();
363
    unsafe { d.destroy_image(image, None) };
364
    result
365
}
366
367
/// Allocates render-capable dmabufs, for swapchains.
368
#[derive(Clone, Debug)]
369
pub struct VulkanAllocator {
370
    pub(crate) shared: Arc<Shared>,
371
    pub(crate) caps: Arc<DmabufCaps>,
372
}
373
374
impl Allocator for VulkanAllocator {
375
    type Buffer = Dmabuf;
376
    type Error = Error;
377
378
    fn create_buffer(
379
        &mut self,
380
        width: u32,
381
        height: u32,
382
        fourcc: Fourcc,
383
        modifiers: &[Modifier],
384
    ) -> Result<Dmabuf, Error> {
385
        export(&self.shared, &self.caps, fourcc, (width, height), modifiers)
386
    }
387
}
388
389
impl std::fmt::Debug for Shared {
390
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
391
        f.debug_struct("Shared")
392
            .field("device", &self.device.name)
393
            .finish()
394
    }
395
}