atagen

monolith

public
Star0
Watch0
89dad88fda98a4158d03d1169f0ebd48fcf2d476
1
ssh://brewery@pls.lobotomise.me:33/atagen/monolith.git
crates/render-vk/src/dmabuf.rsRust397 lines13.8 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
        usage,
273
        true,
274
        dmabuf.y_inverted(),
275
    )
276
}
277
278
/// Allocates a dmabuf the device can render into, picking among `modifiers`.
279
pub(crate) fn export(
280
    shared: &Arc<Shared>,
281
    caps: &DmabufCaps,
282
    fourcc: Fourcc,
283
    size: (u32, u32),
284
    modifiers: &[Modifier],
285
) -> Result<Dmabuf, Error> {
286
    let ext = shared
287
        .device
288
        .dmabuf
289
        .as_ref()
290
        .ok_or(Error::DmabufUnsupported)?;
291
    let allowed: Vec<u64> = modifiers
292
        .iter()
293
        .filter(|&&modifier| {
294
            caps.render.contains(&Format {
295
                code: fourcc,
296
                modifier,
297
            })
298
        })
299
        .map(|&m| u64::from(m))
300
        .collect();
301
    if allowed.is_empty() {
302
        return Err(Error::UnsupportedFormat(fourcc));
303
    }
304
    let d = &shared.device.device;
305
    let mut list =
306
        vk::ImageDrmFormatModifierListCreateInfoEXT::default().drm_format_modifiers(&allowed);
307
    let mut external = vk::ExternalMemoryImageCreateInfo::default()
308
        .handle_types(vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT);
309
    let info = image_info(fourcc, size, RENDER_USAGE)?
310
        .push_next(&mut external)
311
        .push_next(&mut list);
312
    let image = unsafe { d.create_image(&info, None)? };
313
    let result = (|| -> Result<Dmabuf, Error> {
314
        let requirements = unsafe { d.get_image_memory_requirements(image) };
315
        let type_index = memory_type(
316
            shared,
317
            requirements.memory_type_bits,
318
            vk::MemoryPropertyFlags::DEVICE_LOCAL,
319
        )
320
        .ok_or(Error::UnsupportedFormat(fourcc))?;
321
        let mut export = vk::ExportMemoryAllocateInfo::default()
322
            .handle_types(vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT);
323
        let mut dedicated = vk::MemoryDedicatedAllocateInfo::default().image(image);
324
        let alloc = vk::MemoryAllocateInfo::default()
325
            .allocation_size(requirements.size)
326
            .memory_type_index(type_index)
327
            .push_next(&mut export)
328
            .push_next(&mut dedicated);
329
        let memory = unsafe { d.allocate_memory(&alloc, None)? };
330
        let exported = (|| -> Result<Dmabuf, Error> {
331
            unsafe { d.bind_image_memory(image, memory, 0)? };
332
            let raw = unsafe {
333
                ext.memory_fd.get_memory_fd(
334
                    &vk::MemoryGetFdInfoKHR::default()
335
                        .memory(memory)
336
                        .handle_type(vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT),
337
                )?
338
            };
339
            // SAFETY: vkGetMemoryFdKHR hands the caller a new fd to own.
340
            let fd = unsafe { OwnedFd::from_raw_fd(raw) };
341
            let mut props = vk::ImageDrmFormatModifierPropertiesEXT::default();
342
            unsafe {
343
                ext.modifier
344
                    .get_image_drm_format_modifier_properties(image, &mut props)?
345
            };
346
            let layout = unsafe {
347
                d.get_image_subresource_layout(
348
                    image,
349
                    vk::ImageSubresource::default()
350
                        .aspect_mask(vk::ImageAspectFlags::MEMORY_PLANE_0_EXT),
351
                )
352
            };
353
            let mut builder = Dmabuf::builder(
354
                (size.0 as i32, size.1 as i32),
355
                fourcc,
356
                Modifier::from(props.drm_format_modifier),
357
                DmabufFlags::empty(),
358
            );
359
            builder.add_plane(fd, layout.offset as u32, layout.row_pitch as u32);
360
            builder.build().ok_or(Error::UnsupportedFormat(fourcc))
361
        })();
362
        unsafe { d.free_memory(memory, None) };
363
        exported
364
    })();
365
    unsafe { d.destroy_image(image, None) };
366
    result
367
}
368
369
/// Allocates render-capable dmabufs, for swapchains.
370
#[derive(Clone, Debug)]
371
pub struct VulkanAllocator {
372
    pub(crate) shared: Arc<Shared>,
373
    pub(crate) caps: Arc<DmabufCaps>,
374
}
375
376
impl Allocator for VulkanAllocator {
377
    type Buffer = Dmabuf;
378
    type Error = Error;
379
380
    fn create_buffer(
381
        &mut self,
382
        width: u32,
383
        height: u32,
384
        fourcc: Fourcc,
385
        modifiers: &[Modifier],
386
    ) -> Result<Dmabuf, Error> {
387
        export(&self.shared, &self.caps, fourcc, (width, height), modifiers)
388
    }
389
}
390
391
impl std::fmt::Debug for Shared {
392
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
393
        f.debug_struct("Shared")
394
            .field("device", &self.device.name)
395
            .finish()
396
    }
397
}