Skip to main content

gpu/
vk_dmabuf.rs

1// vk_dmabuf -- the swapchain of a Linux program: images without a VkSurface (programs against glibc;
2// docs/GPU.md "Ausgabe unter Linux")
3//
4// Vulkan's Wayland surface wants a pointer from libwayland-client, the X11 one from libxcb.  JFLib
5// speaks both protocols itself and has neither.  So the GPU draws into images of its own, exported as
6// dma-bufs, and plat hands them to the window (lib/plat/linux_gpuout.rs).  For the caller this is the
7// same as vk_swap.rs: Surface, create_swapchain, try_acquire, view, present, rebuild.
8//
9//   Surface::native(&instance, plat::win_gpu_child(w), w)   what the display of window w accepts
10//   surface.track_size()              (the window loop's thread) the window's size for a rebuild on a
11//                                     worker: only that thread may ask plat
12//   device.create_swapchain(&surface, &SwapchainDesc)       any thread
13//        min_images images (0: 3), XRGB8888 (else XBGR8888, ARGB8888, ABGR8888) as B8G8R8A8 /
14//        R8G8B8A8, single-plane modifiers the device can export and the display accepts: the driver
15//        picks (VkImageDrmFormatModifierListCreateInfoEXT), a dedicated allocation, dma-buf
16//   sc.try_acquire()                  never waits, never rebuilds; registers the images with plat at
17//        its first call; looks at the frame before (flush); then NotReady while one is on its way or
18//        the display has not answered the last commit (the frame stays in the caller's texture and
19//        its newest state is drawn then: nothing queues up) or no image is free
20//   sc.present(frame, [cmd, ..])      submits; the image goes to the window when the GPU has finished
21//   sc.flush() -> still on its way    that look at the timeline: the frame goes to the window (the
22//        window loop calls it, a frame is rarely done before present returns)
23//   sc.busy()                         a frame is on its way: flush soon
24//   sc.next_look(retry)               when a frame that found NotReady should try again
25//   sc.damage(rects)                  what the next presented image changes against the last one (None:
26//        all; without any call: all).  What the compositor has to draw again, nothing else
27//   sc.acquire()                      waits (pumping the display, events stay queued) and rebuilds
28//   sc.rebuild()                      any thread, without waiting: the old images die with the frames
29//        that use them
30//
31// Why the image can never be wrong on the screen: linux_gpuout.rs.  Here: an image goes to plat only
32// after its submission reached the timeline; an image is drawn into only when plat says FREE and a
33// poll of its dma-buf for POLLOUT says no fence of another reader is left.  Queue ownership goes to
34// VK_QUEUE_FAMILY_FOREIGN_EXT at the end of every frame and comes back at the start of the next.
35// Compression (modifiers with more than one plane) is left out: the window system does not gain
36// enough from it for the checks each driver wants at the hand-over.
37//
38// Threads: plat's display calls only on the thread of the window loop (linux_disp.rs).  So a worker's
39// rebuild and any drop only note the buffers that go (RETIRED), try_acquire and flush free them.
40
41use alloc::sync::Arc;
42use alloc::vec;
43use alloc::vec::Vec;
44use core::cell::{Cell, RefCell};
45use core::ffi::c_void;
46use core::sync::atomic::{AtomicU64, Ordering};
47
48use rt::sync::SpinLock;
49
50use crate::vk_dev::{Device, Garbage, Shared};
51use crate::vk_ffi as vk;
52use crate::vk_inst::Adapter;
53use crate::*;
54
55const DRM_FORMAT_MOD_INVALID: u64 = 0x00FF_FFFF_FFFF_FFFF;
56const FORMAT_FEATURE_TRANSFER_SRC_BIT: u32 = 0x4000;
57const F_DUPFD_CLOEXEC: usize = 1030;
58const POLLOUT: u16 = 4;
59/// images of a swapchain without SwapchainDesc::min_images
60const IMAGES_DEFAULT: u32 = 3;
61/// while a frame is on its way: the next look at the timeline (us)
62const GPU_LOOK_US: u64 = 1_000;
63const WAIT_US: u64 = 10_000_000;
64
65/// the DRM formats, as Vulkan calls them; the images are opaque (the fourth byte is never shown)
66fn candidates(srgb: bool) -> [(u32, u32, Format); 4] {
67    let (bgra, bf, rgba, rf) = if srgb {
68        (vk::FORMAT_B8G8R8A8_SRGB, Format::Bgra8Srgb, vk::FORMAT_R8G8B8A8_SRGB, Format::Rgba8Srgb)
69    } else {
70        (vk::FORMAT_B8G8R8A8_UNORM, Format::Bgra8Unorm, vk::FORMAT_R8G8B8A8_UNORM, Format::Rgba8Unorm)
71    };
72    [(0x3432_5258, bgra, bf), (0x3432_4258, rgba, rf), (0x3432_5241, bgra, bf), (0x3432_4241, rgba, rf)]
73}
74
75/// the buffers that go: (window, plat's handle)
76static RETIRED: SpinLock<Vec<(i32, u32)>> = SpinLock::new(Vec::new());
77
78fn free_retired() {
79    let list = core::mem::take(&mut *RETIRED.lock());
80    for (w, h) in list {
81        plat::gpu_buf_free(w, h);
82    }
83}
84
85fn fd_close(fd: i32) {
86    // SAFETY: a descriptor of ours.
87    unsafe { rt::os::syscall3(rt::os::SYS_CLOSE, fd as usize, 0, 0) };
88}
89
90pub(crate) struct SurfShared {
91    pub window: i32,
92    caps: plat::GpuCaps,
93    size: AtomicU64,
94}
95
96/// a window as a target for swapchains
97#[derive(Clone)]
98pub struct Surface(pub(crate) Arc<SurfShared>);
99
100impl Surface {
101    /// the surface of plat window `window`
102    pub fn new(instance: &Instance, window: i32) -> Result<Surface> {
103        let nw = plat::win_gpu_child(window).ok_or(GpuError::Unsupported("the display takes no GPU buffers"))?;
104        Surface::native(instance, nw, window)
105    }
106
107    /// the surface from what plat::win_gpu_child said about plat window `window` (any thread)
108    pub fn native(_instance: &Instance, nw: plat::NativeWindow, window: i32) -> Result<Surface> {
109        let (w, h) = nw.size;
110        Ok(Surface(Arc::new(SurfShared { window, caps: nw.caps, size: AtomicU64::new(pack(w, h)) })))
111    }
112
113    /// (the window loop's thread) the window's size now: what a rebuild on a worker makes
114    pub fn track_size(&self) {
115        let (w, h) = plat::win_size(self.0.window);
116        self.0.size.store(pack(w, h), Ordering::Relaxed);
117        free_retired();
118    }
119
120    fn size(&self) -> (u32, u32) {
121        let v = self.0.size.load(Ordering::Relaxed);
122        ((v >> 32) as u32, v as u32)
123    }
124}
125
126fn pack(w: i32, h: i32) -> u64 {
127    (w.max(0) as u64) << 32 | h.max(0) as u64
128}
129
130/// an acquired image (present it with Swapchain::present)
131#[must_use]
132pub struct Frame {
133    image: u32,
134    generation: u32,
135    size: (u32, u32),
136}
137
138/// what Swapchain::try_acquire found
139#[must_use]
140pub enum TryAcquire {
141    Frame(Frame),
142    /// no image is free yet, or the display has not answered the last frame: try again later
143    NotReady,
144    /// the swapchain does not fit the window any more: Swapchain::rebuild first
145    Stale,
146    /// the window is minimised or has no area
147    NoArea,
148}
149
150impl Frame {
151    /// the image's index in the swapchain
152    pub fn index(&self) -> u32 {
153        self.image
154    }
155    pub fn size(&self) -> (u32, u32) {
156        self.size
157    }
158}
159
160struct Img {
161    tex: Texture,
162    image: vk::Handle,
163    mem: vk::Handle,
164    fd: i32,     // the dma-buf (poll; a copy goes to plat)
165    buf: u32,    // plat's handle (0: not registered yet)
166    offset: u32,
167    stride: u32,
168}
169
170/// the images a window shows, presented in turn
171pub struct Swapchain {
172    sh: Arc<Shared>,
173    surf: Surface,
174    desc: SwapchainDesc,
175    format: Format,
176    vk_format: u32,
177    fourcc: u32,
178    mods: Vec<u64>,
179    modifier: u64,
180    mode: PresentMode,
181    extent: (u32, u32),
182    images: Vec<Img>,
183    generation: u32,
184    next: Cell<u32>,
185    stale: Cell<bool>,
186    outstanding: Cell<bool>,
187    /// the frame on its way: image, timeline value, its damage (None: all)
188    fly: RefCell<Option<(u32, u64, Option<Vec<[i32; 4]>>)>>,
189    dmg: RefCell<Vec<[i32; 4]>>,
190    dmg_full: Cell<bool>,
191    dmg_known: Cell<bool>,
192    /// the last try_acquire found the display not ready
193    waiting: Cell<bool>,
194    /// the first frame after prepare does not wait for the display's answer to the CPU path's last
195    /// frame: with a moving window there always is one on its way
196    first: Cell<bool>,
197}
198
199fn present_mode_of(m: PresentMode) -> PresentMode {
200    // one image per answer of the display, always the newest: what MAILBOX means to a window; a
201    // client of the window system has no way to tear, and FIFO's queue would only add latency
202    match m {
203        PresentMode::Fifo => PresentMode::Fifo,
204        _ => PresentMode::Mailbox,
205    }
206}
207
208/// the modifiers (single plane) a device can draw into in this format
209fn device_modifiers(a: &crate::vk_inst::InstShared, pd: vk::PhysicalDevice, format: u32, features: u32) -> Vec<u64> {
210    let Some(get) = a.f.get_physical_device_format_properties2 else { return Vec::new() };
211    let mut list: vk::DrmFormatModifierPropertiesListEXT = vk::Zero::zero();
212    list.s_type = vk::STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_LIST_EXT;
213    let mut fp: vk::FormatProperties2 = vk::Zero::zero();
214    fp.s_type = vk::STRUCTURE_TYPE_FORMAT_PROPERTIES_2;
215    fp.p_next = &mut list as *mut _ as *mut c_void;
216    // SAFETY: the two-call enumeration of a valid chain.
217    unsafe { get(pd, format, &mut fp) };
218    let mut v: Vec<vk::DrmFormatModifierPropertiesEXT> = vec![vk::Zero::zero(); list.count as usize];
219    list.p_properties = v.as_mut_ptr();
220    fp.p_next = &mut list as *mut _ as *mut c_void;
221    // SAFETY: v holds list.count entries.
222    unsafe { get(pd, format, &mut fp) };
223    v.truncate(list.count as usize);
224    v.iter().filter(|m| m.plane_count == 1 && m.tiling_features & features == features).map(|m| m.modifier).collect()
225}
226
227/// an image of this kind can be made and its memory exported
228fn exportable(a: &crate::vk_inst::InstShared, pd: vk::PhysicalDevice, format: u32, usage: u32, modifier: u64) -> bool {
229    let Some(get) = a.f.get_physical_device_image_format_properties2 else { return false };
230    let dm = vk::PhysicalDeviceImageDrmFormatModifierInfoEXT {
231        s_type: vk::STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_DRM_FORMAT_MODIFIER_INFO_EXT,
232        modifier,
233        sharing_mode: vk::SHARING_MODE_EXCLUSIVE,
234        ..vk::Zero::zero()
235    };
236    let ei = vk::PhysicalDeviceExternalImageFormatInfo {
237        s_type: vk::STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO,
238        p_next: &dm as *const _ as *const c_void,
239        handle_type: vk::EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
240    };
241    let info = vk::PhysicalDeviceImageFormatInfo2 {
242        s_type: vk::STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2,
243        p_next: &ei as *const _ as *const c_void,
244        format,
245        image_type: vk::IMAGE_TYPE_2D,
246        tiling: vk::IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT,
247        usage,
248        flags: 0,
249    };
250    let mut ep: vk::ExternalImageFormatProperties = vk::Zero::zero();
251    ep.s_type = vk::STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES;
252    let mut p: vk::ImageFormatProperties2 = vk::Zero::zero();
253    p.s_type = vk::STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2;
254    p.p_next = &mut ep as *mut _ as *mut c_void;
255    // SAFETY: valid chains on this stack frame.
256    let r = unsafe { get(pd, &info, &mut p) };
257    r == vk::SUCCESS && ep.external_memory_properties.external_memory_features & vk::EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT != 0
258}
259
260fn image_usage(copy_src: bool) -> (u32, u32) {
261    let mut usage = vk::IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
262    let mut features = vk::FORMAT_FEATURE_COLOR_ATTACHMENT_BIT;
263    if copy_src {
264        usage |= vk::IMAGE_USAGE_TRANSFER_SRC_BIT;
265        features |= FORMAT_FEATURE_TRANSFER_SRC_BIT;
266    }
267    (usage, features)
268}
269
270/// the first format both sides have -> (fourcc, Vulkan format, Format, the modifiers both accept)
271fn pick_format(a: &crate::vk_inst::InstShared, pd: vk::PhysicalDevice, caps: &plat::GpuCaps, srgb: bool, copy_src: bool)
272               -> Option<(u32, u32, Format, Vec<u64>)> {
273    let (usage, features) = image_usage(copy_src);
274    for (fourcc, vf, ff) in candidates(srgb) {
275        if !caps.formats.iter().any(|e| e.0 == fourcc && e.1 != DRM_FORMAT_MOD_INVALID) {
276            continue;
277        }
278        let mods: Vec<u64> = device_modifiers(a, pd, vf, features)
279            .into_iter()
280            .filter(|&m| caps.formats.contains(&(fourcc, m)) && exportable(a, pd, vf, usage, m))
281            .collect();
282        if !mods.is_empty() {
283            return Some((fourcc, vf, ff, mods));
284        }
285    }
286    None
287}
288
289/// the device the display renders with is this adapter's (not known: yes)
290fn same_device(adapter: &Adapter, caps: &plat::GpuCaps) -> bool {
291    let (Some(d), true) = (caps.device, adapter.exts.physical_drm) else { return true };
292    let major = ((d >> 32) & 0xFFFF_F000 | (d >> 8) & 0xFFF) as i64;
293    let minor = ((d >> 12) & 0xFFFF_FF00 | d & 0xFF) as i64;
294    let mut drm: vk::PhysicalDeviceDrmPropertiesEXT = vk::Zero::zero();
295    drm.s_type = vk::STRUCTURE_TYPE_PHYSICAL_DEVICE_DRM_PROPERTIES_EXT;
296    let mut p2: vk::PhysicalDeviceProperties2 = vk::Zero::zero();
297    p2.s_type = vk::STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
298    p2.p_next = &mut drm as *mut _ as *mut c_void;
299    // SAFETY: a valid chain on this stack frame.
300    unsafe { (adapter.inst.f.get_physical_device_properties2)(adapter.pd, &mut p2) };
301    (drm.has_primary != 0 && (drm.primary_major, drm.primary_minor) == (major, minor))
302        || (drm.has_render != 0 && (drm.render_major, drm.render_minor) == (major, minor))
303}
304
305/// (Adapter::can_present) a device of this adapter can show images on the surface
306pub(crate) fn can_present(adapter: &Adapter, surface: &Surface) -> bool {
307    adapter.exts.dmabuf
308        && adapter.info.kind != AdapterKind::Cpu
309        && same_device(adapter, &surface.0.caps)
310        && pick_format(&adapter.inst, adapter.pd, &surface.0.caps, false, false).is_some()
311}
312
313impl Device {
314    pub fn create_swapchain(&self, surface: &Surface, desc: &SwapchainDesc) -> Result<Swapchain> {
315        let sh = &self.sh;
316        sh.check()?;
317        if !sh.features.swapchain {
318            return Err(GpuError::Unsupported("images for a window (VK_EXT_image_drm_format_modifier, not on a CPU adapter)"));
319        }
320        let (fourcc, vk_format, format, mods) = pick_format(&sh.inst, sh.pd, &surface.0.caps, desc.srgb, desc.copy_src)
321            .ok_or(GpuError::Unsupported("a format the display and the device share"))?;
322        let mut d = *desc;
323        d.frames_in_flight = d.frames_in_flight.clamp(1, 3);
324        match plat::env("jfgui_GPU_PRESENT").as_deref() {
325            Some("fifo") => d.present = PresentMode::Fifo,
326            Some("mailbox") => d.present = PresentMode::Mailbox,
327            Some("immediate") => d.present = PresentMode::Immediate,
328            _ => {}
329        }
330        let mut sc = Swapchain {
331            sh: sh.clone(),
332            surf: surface.clone(),
333            desc: d,
334            format,
335            vk_format,
336            fourcc,
337            mods,
338            modifier: 0,
339            mode: present_mode_of(d.present),
340            extent: (0, 0),
341            images: Vec::new(),
342            generation: 0,
343            next: Cell::new(0),
344            stale: Cell::new(false),
345            outstanding: Cell::new(false),
346            fly: RefCell::new(None),
347            dmg: RefCell::new(Vec::new()),
348            dmg_full: Cell::new(false),
349            dmg_known: Cell::new(false),
350            waiting: Cell::new(false),
351            first: Cell::new(false),
352        };
353        sc.build()?;
354        Ok(sc)
355    }
356}
357
358impl Swapchain {
359    /// the images and what hangs on them go: when the GPU has finished with them
360    fn release(&mut self) {
361        let window = self.surf.0.window;
362        for m in self.images.drain(..) {
363            if m.buf != 0 {
364                RETIRED.lock().push((window, m.buf));
365            }
366            fd_close(m.fd);
367            drop(m.tex);
368            self.sh.defer(Garbage::Image(m.image, None));
369            self.sh.defer(Garbage::Memory(m.mem));
370        }
371        *self.fly.borrow_mut() = None;
372    }
373
374    /// an exported image; the driver picks from mods
375    fn make_image(&self, w: u32, h: u32, mods: &[u64]) -> Result<(Img, u64)> {
376        let sh = self.sh.clone();
377        let (Some(get_mod), Some(get_layout), Some(get_fd)) =
378            (sh.f.get_image_drm_format_modifier_properties_ext, sh.f.get_image_subresource_layout, sh.f.get_memory_fd_khr)
379        else {
380            return Err(GpuError::Unsupported("VK_EXT_image_drm_format_modifier"));
381        };
382        let (usage, _) = image_usage(self.desc.copy_src);
383        let list = vk::ImageDrmFormatModifierListCreateInfoEXT {
384            s_type: vk::STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_LIST_CREATE_INFO_EXT,
385            p_next: core::ptr::null(),
386            count: mods.len() as u32,
387            p_modifiers: mods.as_ptr(),
388        };
389        let ext = vk::ExternalMemoryImageCreateInfo {
390            s_type: vk::STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO,
391            p_next: &list as *const _ as *const c_void,
392            handle_types: vk::EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
393        };
394        let ii = vk::ImageCreateInfo {
395            s_type: vk::STRUCTURE_TYPE_IMAGE_CREATE_INFO,
396            p_next: &ext as *const _ as *const c_void,
397            image_type: vk::IMAGE_TYPE_2D,
398            format: self.vk_format,
399            extent: vk::Extent3D { width: w, height: h, depth: 1 },
400            mip_levels: 1,
401            array_layers: 1,
402            samples: vk::SAMPLE_COUNT_1_BIT,
403            tiling: vk::IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT,
404            usage,
405            sharing_mode: vk::SHARING_MODE_EXCLUSIVE,
406            initial_layout: vk::IMAGE_LAYOUT_UNDEFINED,
407            ..vk::Zero::zero()
408        };
409        let mut image = 0;
410        // SAFETY: valid chains on this stack frame.
411        sh.vk(unsafe { (sh.f.create_image)(sh.dev, &ii, core::ptr::null(), &mut image) })?;
412        let mut mem = 0;
413        let mut fd = -1;
414        let made = (|| {
415            let mut mp: vk::ImageDrmFormatModifierPropertiesEXT = vk::Zero::zero();
416            mp.s_type = vk::STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_PROPERTIES_EXT;
417            // SAFETY: the image just created with a modifier.
418            sh.vk(unsafe { get_mod(sh.dev, image, &mut mp) })?;
419            let mut req: vk::MemoryRequirements = vk::Zero::zero();
420            // SAFETY: the image just created.
421            unsafe { (sh.f.get_image_memory_requirements)(sh.dev, image, &mut req) };
422            let mut props: vk::PhysicalDeviceMemoryProperties = vk::Zero::zero();
423            // SAFETY: plain out-parameter.
424            unsafe { (sh.inst.f.get_physical_device_memory_properties)(sh.pd, &mut props) };
425            let n = props.memory_type_count.min(32);
426            let ty = (0..n)
427                .find(|&k| req.memory_type_bits & 1 << k != 0 && props.memory_types[k as usize].property_flags & vk::MEMORY_PROPERTY_DEVICE_LOCAL_BIT != 0)
428                .or_else(|| (0..n).find(|&k| req.memory_type_bits & 1 << k != 0))
429                .ok_or(GpuError::Unsupported("memory for an exported image"))?;
430            let ded = vk::MemoryDedicatedAllocateInfo {
431                s_type: vk::STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO,
432                image,
433                ..vk::Zero::zero()
434            };
435            let exp = vk::ExportMemoryAllocateInfo {
436                s_type: vk::STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO,
437                p_next: &ded as *const _ as *const c_void,
438                handle_types: vk::EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
439            };
440            let ai = vk::MemoryAllocateInfo {
441                s_type: vk::STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
442                p_next: &exp as *const _ as *const c_void,
443                allocation_size: req.size,
444                memory_type_index: ty,
445            };
446            // SAFETY: valid chains on this stack frame.
447            sh.vk(unsafe { (sh.f.allocate_memory)(sh.dev, &ai, core::ptr::null(), &mut mem) })?;
448            // SAFETY: the dedicated allocation of this image.
449            sh.vk(unsafe { (sh.f.bind_image_memory)(sh.dev, image, mem, 0) })?;
450            let gi = vk::MemoryGetFdInfoKHR {
451                s_type: vk::STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR,
452                p_next: core::ptr::null(),
453                memory: mem,
454                handle_type: vk::EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
455            };
456            // SAFETY: memory allocated for export.
457            sh.vk(unsafe { get_fd(sh.dev, &gi, &mut fd) })?;
458            let sub = vk::ImageSubresource { aspect_mask: vk::IMAGE_ASPECT_MEMORY_PLANE_0_BIT_EXT, mip_level: 0, array_layer: 0 };
459            let mut lay: vk::SubresourceLayout = vk::Zero::zero();
460            // SAFETY: plane 0 of an image with a single-plane modifier.
461            unsafe { get_layout(sh.dev, image, &sub, &mut lay) };
462            let mut tu = TextureUsage::COLOR_TARGET;
463            if self.desc.copy_src {
464                tu = tu | TextureUsage::COPY_SRC;
465            }
466            let tex = crate::vk_res::wrap_image(&sh, image, TextureDesc::d2(w, h, self.format, tu))?;
467            Ok((tex, mp.modifier, lay.offset as u32, lay.row_pitch as u32))
468        })();
469        match made {
470            Ok((tex, modifier, offset, stride)) => Ok((Img { tex, image, mem, fd, buf: 0, offset, stride }, modifier)),
471            Err(e) => {
472                if fd >= 0 {
473                    fd_close(fd);
474                }
475                sh.defer(Garbage::Image(image, None));
476                if mem != 0 {
477                    sh.defer(Garbage::Memory(mem));
478                }
479                Err(e)
480            }
481        }
482    }
483
484    /// (re)create the images for the window's size as the window loop last said it (no area: none)
485    fn build(&mut self) -> Result<()> {
486        self.release();
487        let (w, h) = self.surf.size();
488        self.stale.set(false);
489        self.outstanding.set(false);
490        self.extent = (0, 0);
491        if w == 0 || h == 0 {
492            return Ok(());
493        }
494        let n = if self.desc.min_images != 0 { self.desc.min_images } else { IMAGES_DEFAULT }.clamp(2, 8);
495        let mut mods = self.mods.clone();
496        for _ in 0..n {
497            let (img, m) = self.make_image(w, h, &mods)?;
498            // the first image's choice for the others: one layout per swapchain
499            mods = vec![m];
500            self.modifier = m;
501            self.images.push(img);
502        }
503        self.extent = (w, h);
504        self.generation = self.generation.wrapping_add(1);
505        self.dmg_full.set(true);
506        Ok(())
507    }
508
509    /// the images for the size the window has now, without waiting (any thread)
510    pub fn rebuild(&mut self) -> Result<()> {
511        self.sh.check()?;
512        self.build()
513    }
514
515    /// the images to plat (the window loop's thread)
516    fn register(&mut self) -> Result<()> {
517        let (w, h) = self.extent;
518        for k in 0..self.images.len() {
519            if self.images[k].buf != 0 {
520                continue;
521            }
522            // SAFETY: a descriptor of ours, duplicated.
523            let dup = unsafe { rt::os::syscall3(rt::os::SYS_FCNTL, self.images[k].fd as usize, F_DUPFD_CLOEXEC, 3) };
524            if dup < 0 {
525                return Err(GpuError::Unsupported("no descriptor for a buffer"));
526            }
527            let m = &self.images[k];
528            let d = plat::GpuBufDesc {
529                w: w as i32,
530                h: h as i32,
531                fourcc: self.fourcc,
532                modifier: self.modifier,
533                planes: vec![plat::GpuPlane { fd: dup as i32, offset: m.offset, stride: m.stride }],
534            };
535            let buf = plat::gpu_buf_new(self.surf.0.window, &d);
536            if buf == 0 {
537                return Err(GpuError::Unsupported("the display refuses the buffer"));
538            }
539            self.images[k].buf = buf;
540        }
541        Ok(())
542    }
543
544    /// nobody reads the image: no fence of another reader on its dma-buf
545    fn unread(fd: i32) -> bool {
546        #[repr(C)]
547        struct PollFd {
548            fd: i32,
549            events: u16,
550            revents: u16,
551        }
552        let mut p = PollFd { fd, events: POLLOUT, revents: 0 };
553        // SAFETY: one pollfd, no wait.
554        let r = unsafe { rt::os::poll(&mut p as *mut PollFd as usize, 1, 0) };
555        r == 1
556    }
557
558    /// the frame on its way: finished by the GPU -> it goes to the window; -> still on its way
559    pub fn flush(&self) -> Result<bool> {
560        free_retired();
561        let Some(&(k, v, _)) = self.fly.borrow().as_ref() else { return Ok(false) };
562        if v > self.sh.refresh() {
563            self.sh.check()?;
564            return Ok(true);
565        }
566        let Some((_, _, rects)) = self.fly.borrow_mut().take() else { return Ok(false) };
567        if let Some(m) = self.images.get(k as usize) {
568            plat::gpu_show(self.surf.0.window, m.buf, rects.as_deref());
569        }
570        Ok(false)
571    }
572
573    /// a frame is on its way to the window: flush soon
574    pub fn busy(&self) -> bool {
575        self.fly.borrow().is_some()
576    }
577
578    /// when a frame that found NotReady should try again: soon when the GPU is drawing; when the
579    /// display's answer is overdue if it has not given it (the answer itself ends the wait earlier);
580    /// else retry, the caller's time
581    pub fn next_look(&self, retry: u64) -> u64 {
582        if self.busy() {
583            return plat::time_us() + GPU_LOOK_US;
584        }
585        if !self.waiting.get() {
586            return retry;
587        }
588        let w = self.surf.0.window;
589        if plat::gpu_can_show(w) && self.images.iter().any(|m| plat::gpu_buf_state(w, m.buf) == plat::GPU_BUF_FREE) {
590            return retry;
591        }
592        plat::gpu_next_look(w)
593    }
594
595    /// the images to the display, its answer there (the window loop's thread)
596    pub fn prepare(&mut self) -> Result<bool> {
597        self.register()?;
598        let w = self.surf.0.window;
599        for m in &self.images {
600            match plat::gpu_buf_state(w, m.buf) {
601                plat::GPU_BUF_FAILED => return Err(GpuError::Unsupported("the display refuses the buffer")),
602                plat::GPU_BUF_PENDING => return Ok(false),
603                _ => {}
604            }
605        }
606        self.first.set(true);
607        Ok(true)
608    }
609
610    /// what the next presented image changes against the last one: rectangles x, y, w, h (None: all)
611    pub fn damage(&self, rects: Option<&[[i32; 4]]>) {
612        self.dmg_known.set(true);
613        match rects {
614            Some(r) => self.dmg.borrow_mut().extend_from_slice(r),
615            None => self.dmg_full.set(true),
616        }
617    }
618
619    /// the next image to draw into, without waiting and without rebuilding (see TryAcquire)
620    pub fn try_acquire(&mut self) -> Result<TryAcquire> {
621        self.sh.check()?;
622        free_retired();
623        let window = self.surf.0.window;
624        if plat::win_state(window) & plat::WST_MINIMIZED != 0 {
625            return Ok(TryAcquire::NoArea);
626        }
627        let (ww, wh) = plat::win_size(window);
628        let size = (ww.max(0) as u32, wh.max(0) as u32);
629        if self.stale.get() || self.outstanding.get() || self.images.is_empty() || size != self.extent {
630            self.stale.set(true);
631            return Ok(TryAcquire::Stale);
632        }
633        self.register()?;
634        self.waiting.set(true);
635        if self.flush()? || (!self.first.get() && !plat::gpu_can_show(window)) {
636            return Ok(TryAcquire::NotReady);
637        }
638        let n = self.images.len() as u32;
639        let first = self.next.get();
640        for j in 0..n {
641            let k = (first + j) % n;
642            let m = &self.images[k as usize];
643            match plat::gpu_buf_state(window, m.buf) {
644                plat::GPU_BUF_FAILED => return Err(GpuError::Unsupported("the display refuses the buffer")),
645                plat::GPU_BUF_FREE if Self::unread(m.fd) => {
646                    self.next.set((k + 1) % n);
647                    self.outstanding.set(true);
648                    self.waiting.set(false);
649                    self.first.set(false);
650                    return Ok(TryAcquire::Frame(Frame { image: k, generation: self.generation, size: self.extent }));
651                }
652                _ => {}
653            }
654        }
655        Ok(TryAcquire::NotReady)
656    }
657
658    /// the next image to draw into, rebuilt when stale; waits (the display is pumped, events stay
659    /// queued); None while the window is minimised or has no area
660    pub fn acquire(&mut self) -> Result<Option<Frame>> {
661        self.sh.check()?;
662        let window = self.surf.0.window;
663        if plat::win_state(window) & plat::WST_MINIMIZED != 0 {
664            return Ok(None);
665        }
666        if self.outstanding.get() {
667            self.stale.set(true); // the last frame was never presented
668        }
669        let dl = plat::time_us() + WAIT_US;
670        loop {
671            self.surf.track_size();
672            let (w, h) = self.surf.size();
673            if w == 0 || h == 0 {
674                return Ok(None);
675            }
676            if self.stale.get() || self.images.is_empty() || (w, h) != self.extent {
677                self.build()?;
678            }
679            match self.try_acquire()? {
680                TryAcquire::Frame(f) => return Ok(Some(f)),
681                TryAcquire::NoArea => return Ok(None),
682                TryAcquire::Stale => {}
683                TryAcquire::NotReady => {
684                    let now = plat::time_us();
685                    if now >= dl {
686                        return Err(GpuError::Timeout);
687                    }
688                    plat::gpu_wait(self.next_look(now).min(now + 2_000));
689                }
690            }
691        }
692    }
693
694    /// the frame's image as a render target (Undefined at the start, Present at the end)
695    pub fn view(&self, frame: &Frame) -> &TextureView {
696        self.images[frame.image as usize].tex.view()
697    }
698
699    /// the frame's image (copies: SwapchainDesc::copy_src)
700    pub fn texture(&self, frame: &Frame) -> &Texture {
701        &self.images[frame.image as usize].tex
702    }
703
704    // an image's queue ownership: barrier in a command buffer of its own
705    fn ownership(&self, image: vk::Handle, acquire: bool) -> Result<crate::vk_dev::Slot> {
706        let sh = &self.sh;
707        let foreign = if sh.queue_foreign { vk::QUEUE_FAMILY_FOREIGN_EXT } else { vk::QUEUE_FAMILY_EXTERNAL };
708        let slot = sh.take_slot()?;
709        let range = vk::ImageSubresourceRange { aspect_mask: vk::IMAGE_ASPECT_COLOR_BIT, base_mip_level: 0, level_count: 1, base_array_layer: 0, layer_count: 1 };
710        let b = vk::ImageMemoryBarrier {
711            s_type: vk::STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
712            src_access_mask: if acquire { 0 } else { vk::ACCESS_COLOR_ATTACHMENT_WRITE_BIT | vk::ACCESS_TRANSFER_READ_BIT },
713            dst_access_mask: 0,
714            old_layout: if acquire { vk::IMAGE_LAYOUT_UNDEFINED } else { vk::IMAGE_LAYOUT_GENERAL },
715            new_layout: vk::IMAGE_LAYOUT_GENERAL,
716            src_queue_family_index: if acquire { foreign } else { sh.family },
717            dst_queue_family_index: if acquire { sh.family } else { foreign },
718            image,
719            subresource_range: range,
720            ..vk::Zero::zero()
721        };
722        let (ss, ds) = if acquire {
723            (vk::PIPELINE_STAGE_TOP_OF_PIPE_BIT, vk::PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT)
724        } else {
725            (vk::PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | vk::PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT)
726        };
727        // SAFETY: a buffer in the recording state, one barrier on this stack frame.
728        unsafe { (sh.f.cmd_pipeline_barrier)(slot.cb, ss, ds, 0, 0, core::ptr::null(), 0, core::ptr::null(), 1, &b) };
729        // SAFETY: as above.
730        if let Err(e) = sh.vk(unsafe { (sh.f.end_command_buffer)(slot.cb) }) {
731            sh.give_slot(slot);
732            return Err(e);
733        }
734        Ok(slot)
735    }
736
737    /// submit the frame's commands (they leave the image in Layout::Present); it goes to the window
738    /// when the GPU has finished it (flush)
739    pub fn present<'a, I: IntoIterator<Item = CommandBuffer<'a>>>(&self, frame: Frame, cmds: I) -> Result<SubmitId> {
740        let sh = &self.sh;
741        let mut slots = Vec::new();
742        for c in cmds {
743            slots.push(c.into_slot());
744        }
745        if frame.generation != self.generation || !self.outstanding.get() || self.images.is_empty() {
746            for s in slots {
747                sh.give_slot(s);
748            }
749            return Err(GpuError::Invalid("present: a frame of an older swapchain"));
750        }
751        let image = self.images[frame.image as usize].image;
752        let first = match self.ownership(image, true) {
753            Ok(s) => s,
754            Err(e) => {
755                for s in slots {
756                    sh.give_slot(s);
757                }
758                return Err(e);
759            }
760        };
761        let last = match self.ownership(image, false) {
762            Ok(s) => s,
763            Err(e) => {
764                sh.give_slot(first);
765                for s in slots {
766                    sh.give_slot(s);
767                }
768                return Err(e);
769            }
770        };
771        slots.insert(0, first);
772        slots.push(last);
773        let cbs: Vec<vk::CommandBuffer> = slots.iter().map(|s| s.cb).collect();
774        let value = sh.submit(&cbs, slots, None, None)?;
775        self.outstanding.set(false);
776        let rects = if self.dmg_known.get() && !self.dmg_full.replace(false) {
777            Some(core::mem::take(&mut *self.dmg.borrow_mut()))
778        } else {
779            self.dmg.borrow_mut().clear();
780            None
781        };
782        *self.fly.borrow_mut() = Some((frame.image, value, rects));
783        self.flush()?;
784        Ok(SubmitId(value))
785    }
786
787    /// rebuild at the next acquire (the window's size is also checked at every acquire)
788    pub fn resize(&mut self) {
789        self.stale.set(true);
790    }
791
792    /// another present mode from the next acquire on
793    pub fn set_present_mode(&mut self, m: PresentMode) {
794        self.desc.present = m;
795        self.mode = present_mode_of(m);
796    }
797
798    pub fn present_mode(&self) -> PresentMode {
799        self.mode
800    }
801    pub fn format(&self) -> Format {
802        self.format
803    }
804    /// the images' size (0, 0 while the window has no area)
805    pub fn size(&self) -> (u32, u32) {
806        self.extent
807    }
808    pub fn image_count(&self) -> usize {
809        self.images.len()
810    }
811    /// the images' DRM format and modifier (tests, traces)
812    pub fn drm_format(&self) -> (u32, u64) {
813        (self.fourcc, self.modifier)
814    }
815}
816
817impl Drop for Swapchain {
818    fn drop(&mut self) {
819        self.release();
820    }
821}