Skip to main content

gpu/
vk_swap.rs

1// vk_swap -- window surfaces and swapchains
2//
3//   Surface::new(&instance, window) -> Result<Surface>   a plat window (Windows: its HWND and
4//        HINSTANCE through plat::win_native, VK_KHR_win32_surface; Android: the ANativeWindow of
5//        plat::win_gpu_child, VK_KHR_android_surface, NotReady while there is none); other targets:
6//        Unsupported
7//   device.create_swapchain(&surface, &SwapchainDesc) -> Result<Swapchain>
8//        B8G8R8A8 (or R8G8B8A8) UNORM / SRGB, sRGB non-linear colour space; the present mode of
9//        the desc or jfgui_GPU_PRESENT=fifo|mailbox|immediate, FIFO when the surface lacks it;
10//        image count: the surface's minimum + 1 (or SwapchainDesc::min_images); copy_src adds
11//        TRANSFER_SRC (read-back)
12//   sc.acquire() -> Result<Option<Frame>>    None while the window is minimised or has no area;
13//        rebuilds the swapchain first when the window's size changed, the last present said
14//        out-of-date / suboptimal, resize() or set_present_mode() asked for it, or the last frame
15//        was never presented; waits until the frame slot's previous submission is done
16//        (frames_in_flight)
17//   sc.try_acquire() -> Result<TryAcquire>   never waits, never rebuilds: Frame, NotReady (no
18//        image free yet, or the frame slot's previous submission runs), Stale (the window's size
19//        changed, the last present said out-of-date / suboptimal, resize() asked: rebuild() first),
20//        NoArea (minimised)
21//   sc.rebuild()                             what acquire() does when stale (any thread, once
22//        the swapchain has moved there; waits for the queue to be idle)
23//   sc.view(&frame) / texture(&frame)        the image: Layout::Undefined at the start of the
24//        frame's commands, Layout::Present at their end
25//   sc.present(frame, [cmd, ..]) -> Result<SubmitId>   submit (waiting for the image at the
26//        colour-output, copy and compute stages, signalling the image's render-done semaphore),
27//        then vkQueuePresentKHR
28//   sc.size(), format(), present_mode(), resize(), set_present_mode(m)
29//
30// Rebuilding (and dropping) waits for the queue to be idle -- vkQueueWaitIdle covers the
31// presents too, the timeline does not see a present's semaphore wait -- then creates the new
32// swapchain with the old one as oldSwapchain and destroys the old one with its views and
33// semaphores (resizes are rare; a frame never waits for this).  Semaphores: one acquire
34// semaphore per frame slot (reused once the slot's submission completed), one render-done
35// semaphore per image (the present that waited on it has finished when the image is acquired
36// again).
37// The window must not be presented by the CPU (plat::win_present) at the same time: a plat
38// window that never presents keeps nothing of its own to paint (win_api.rs WM_PAINT).
39//
40// Android: the image stays upright (VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) and the compositor turns
41// it; the surface's currentTransform would ask us to draw it turned, with every rectangle turned as
42// well.  The system then says SUBOPTIMAL at every present while the display is turned, which means
43// nothing here: a new size is seen in window_size (a surface there never says OUT_OF_DATE for it).
44
45use alloc::sync::Arc;
46use alloc::vec;
47use alloc::vec::Vec;
48use core::cell::Cell;
49
50use crate::vk_dev::{Device, Garbage, Shared};
51use crate::vk_ffi as vk;
52use crate::vk_inst::InstShared;
53use crate::*;
54
55pub(crate) struct SurfShared {
56    inst: Arc<InstShared>,
57    pub h: vk::Handle,
58    pub window: i32,
59}
60
61impl Drop for SurfShared {
62    fn drop(&mut self) {
63        if let Some(d) = self.inst.f.destroy_surface_khr {
64            // SAFETY: the swapchains of the surface hold it, so none is left.
65            unsafe { d(self.inst.inst, self.h, core::ptr::null()) };
66        }
67    }
68}
69
70/// a window as a target for swapchains
71#[derive(Clone)]
72pub struct Surface(pub(crate) Arc<SurfShared>);
73
74impl Surface {
75    /// (Linux: the window's size for a rebuild on a worker; here the driver knows it)
76    pub fn track_size(&self) {}
77
78    /// the surface of plat window `window` (an index of plat::win_create)
79    pub fn new(instance: &Instance, window: i32) -> Result<Surface> {
80        #[cfg(windows)]
81        {
82            let nw = plat::win_native(window).ok_or(GpuError::Invalid("no such window"))?;
83            Surface::native(instance, nw, window)
84        }
85        #[cfg(target_os = "android")]
86        {
87            let nw = plat::win_gpu_child(window).ok_or(GpuError::NotReady)?;
88            Surface::native(instance, nw, window)
89        }
90        #[cfg(not(any(windows, target_os = "android")))]
91        {
92            let _ = (instance, window);
93            Err(GpuError::Unsupported("window surfaces on this target"))
94        }
95    }
96
97    /// the surface of a window given by its handles (the ANativeWindow of plat::win_gpu_child)
98    /// whose size and state are those of plat window `window`
99    #[cfg(target_os = "android")]
100    pub fn native(instance: &Instance, nw: plat::NativeWindow, window: i32) -> Result<Surface> {
101        let sh = &instance.sh;
102        if !sh.surface {
103            return Err(GpuError::Unsupported("window surfaces"));
104        }
105        let create = sh.f.create_android_surface_khr.ok_or(GpuError::Unsupported("VK_KHR_android_surface"))?;
106        let ci = vk::AndroidSurfaceCreateInfoKHR {
107            s_type: vk::STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR,
108            window: nw.window,
109            ..vk::Zero::zero()
110        };
111        let mut h = 0;
112        // SAFETY: a live ANativeWindow; the surface takes a reference of its own.
113        let r = unsafe { create(sh.inst, &ci, core::ptr::null(), &mut h) };
114        if r != vk::SUCCESS {
115            return Err(crate::vk_inst::err(r));
116        }
117        Ok(Surface(Arc::new(SurfShared { inst: sh.clone(), h, window })))
118    }
119
120    /// the surface of a window given by its handles (Windows: the child of plat::win_gpu_child)
121    /// whose size and state are those of plat window `window`
122    #[cfg(windows)]
123    pub fn native(instance: &Instance, nw: plat::NativeWindow, window: i32) -> Result<Surface> {
124        let sh = &instance.sh;
125        if !sh.surface {
126            return Err(GpuError::Unsupported("window surfaces"));
127        }
128        {
129            let create = sh.f.create_win32_surface_khr.ok_or(GpuError::Unsupported("VK_KHR_win32_surface"))?;
130            let ci = vk::Win32SurfaceCreateInfoKHR {
131                s_type: vk::STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR,
132                hinstance: nw.hinstance,
133                hwnd: nw.hwnd,
134                ..vk::Zero::zero()
135            };
136            let mut h = 0;
137            // SAFETY: a live window of this process and its module.
138            let r = unsafe { create(sh.inst, &ci, core::ptr::null(), &mut h) };
139            if r != vk::SUCCESS {
140                return Err(crate::vk_inst::err(r));
141            }
142            Ok(Surface(Arc::new(SurfShared { inst: sh.clone(), h, window })))
143        }
144    }
145}
146
147/// an acquired swapchain image (present it with Swapchain::present)
148#[must_use]
149pub struct Frame {
150    image: u32,
151    slot: u32,
152    generation: u32,
153    size: (u32, u32),
154}
155
156/// what Swapchain::try_acquire found
157#[must_use]
158pub enum TryAcquire {
159    Frame(Frame),
160    /// no image is free yet, or the frame slot's previous submission still runs: try again later
161    NotReady,
162    /// the swapchain does not fit the window any more: Swapchain::rebuild first
163    Stale,
164    /// the window is minimised or has no area
165    NoArea,
166}
167
168impl Frame {
169    /// the image's index in the swapchain
170    pub fn index(&self) -> u32 {
171        self.image
172    }
173    pub fn size(&self) -> (u32, u32) {
174        self.size
175    }
176}
177
178/// the images a window shows, presented in turn
179pub struct Swapchain {
180    sh: Arc<Shared>,
181    surf: Surface,
182    desc: SwapchainDesc,
183    h: vk::Handle,
184    format: Format,
185    vk_format: u32,
186    color_space: u32,
187    mode: PresentMode,
188    extent: (u32, u32),
189    images: Vec<Texture>,
190    acquire: Vec<vk::Handle>,
191    done: Vec<vk::Handle>,
192    slot_value: Vec<Cell<u64>>,
193    slot: Cell<u32>,
194    generation: u32,
195    stale: Cell<bool>,
196    outstanding: Cell<bool>,
197}
198
199/// Android: SUBOPTIMAL only says the display is turned and the compositor turns the image (the file's
200/// header)
201const TURNED_IS_FINE: bool = cfg!(target_os = "android");
202
203fn present_mode_vk(m: PresentMode) -> u32 {
204    match m {
205        PresentMode::Fifo => vk::PRESENT_MODE_FIFO_KHR,
206        PresentMode::Mailbox => vk::PRESENT_MODE_MAILBOX_KHR,
207        PresentMode::Immediate => vk::PRESENT_MODE_IMMEDIATE_KHR,
208    }
209}
210
211impl Device {
212    pub fn create_swapchain(&self, surface: &Surface, desc: &SwapchainDesc) -> Result<Swapchain> {
213        let sh = &self.sh;
214        sh.check()?;
215        if !sh.swapchain {
216            return Err(GpuError::Unsupported("swapchains (VK_KHR_swapchain)"));
217        }
218        let f = &sh.inst.f;
219        let (Some(support), Some(formats)) = (f.get_physical_device_surface_support_khr, f.get_physical_device_surface_formats_khr)
220        else {
221            return Err(GpuError::Unsupported("surface queries"));
222        };
223        let mut ok = 0;
224        // SAFETY: plain query.
225        sh.vk(unsafe { support(sh.pd, sh.family, surface.0.h, &mut ok) })?;
226        if ok == 0 {
227            return Err(GpuError::Unsupported("presentation from this queue"));
228        }
229        let mut n = 0u32;
230        // SAFETY: the two-call enumeration.
231        sh.vk(unsafe { formats(sh.pd, surface.0.h, &mut n, core::ptr::null_mut()) })?;
232        let mut fl: Vec<vk::SurfaceFormatKHR> = vec![vk::Zero::zero(); n as usize];
233        // SAFETY: fl holds n entries.
234        let r = unsafe { formats(sh.pd, surface.0.h, &mut n, fl.as_mut_ptr()) };
235        if r != vk::INCOMPLETE {
236            sh.vk(r)?;
237        }
238        fl.truncate(n as usize);
239        let want: [(u32, Format); 2] = if desc.srgb {
240            [(vk::FORMAT_B8G8R8A8_SRGB, Format::Bgra8Srgb), (vk::FORMAT_R8G8B8A8_SRGB, Format::Rgba8Srgb)]
241        } else {
242            [(vk::FORMAT_B8G8R8A8_UNORM, Format::Bgra8Unorm), (vk::FORMAT_R8G8B8A8_UNORM, Format::Rgba8Unorm)]
243        };
244        let pick = want.iter().find_map(|&(vf, ff)| {
245            fl.iter().find(|s| s.format == vf && s.color_space == vk::COLOR_SPACE_SRGB_NONLINEAR_KHR).map(|s| (vf, ff, s.color_space))
246        });
247        let Some((vk_format, format, color_space)) = pick else {
248            return Err(GpuError::Unsupported("an 8-bit RGBA / BGRA surface format"));
249        };
250        let mut d = *desc;
251        d.frames_in_flight = d.frames_in_flight.clamp(1, 3);
252        match plat::env("jfgui_GPU_PRESENT").as_deref() {
253            Some("fifo") => d.present = PresentMode::Fifo,
254            Some("mailbox") => d.present = PresentMode::Mailbox,
255            Some("immediate") => d.present = PresentMode::Immediate,
256            _ => {}
257        }
258        let mut sc = Swapchain {
259            sh: sh.clone(),
260            surf: surface.clone(),
261            desc: d,
262            h: 0,
263            format,
264            vk_format,
265            color_space,
266            mode: PresentMode::Fifo,
267            extent: (0, 0),
268            images: Vec::new(),
269            acquire: Vec::new(),
270            done: Vec::new(),
271            slot_value: (0..d.frames_in_flight).map(|_| Cell::new(0)).collect(),
272            slot: Cell::new(0),
273            generation: 0,
274            stale: Cell::new(false),
275            outstanding: Cell::new(false),
276        };
277        sc.build()?;
278        Ok(sc)
279    }
280}
281
282impl Swapchain {
283    fn semaphore(&self) -> Result<vk::Handle> {
284        let sh = &self.sh;
285        let si = vk::SemaphoreCreateInfo { s_type: vk::STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, ..vk::Zero::zero() };
286        let mut s = 0;
287        // SAFETY: valid create info.
288        sh.vk(unsafe { (sh.f.create_semaphore)(sh.dev, &si, core::ptr::null(), &mut s) })?;
289        Ok(s)
290    }
291
292    /// drop the images and semaphores (the device is idle or nothing was submitted for them)
293    fn release(&mut self) {
294        self.images.clear();
295        for s in self.acquire.drain(..).chain(self.done.drain(..)) {
296            self.sh.defer(Garbage::Semaphore(s));
297        }
298    }
299
300    /// (re)create the swapchain for the window's current size (extent 0: no swapchain)
301    fn build(&mut self) -> Result<()> {
302        let sh = self.sh.clone();
303        let inst = &sh.inst.f;
304        let (Some(caps_fn), Some(modes_fn)) =
305            (inst.get_physical_device_surface_capabilities_khr, inst.get_physical_device_surface_present_modes_khr)
306        else {
307            return Err(GpuError::Unsupported("surface queries"));
308        };
309        let (Some(create), Some(images_fn)) = (sh.f.create_swapchain_khr, sh.f.get_swapchain_images_khr) else {
310            return Err(GpuError::Unsupported("VK_KHR_swapchain"));
311        };
312        let surf = self.surf.0.h;
313        let mut caps: vk::SurfaceCapabilitiesKHR = vk::Zero::zero();
314        // SAFETY: plain query.
315        let r = unsafe { caps_fn(sh.pd, surf, &mut caps) };
316        sh.vk(r)?;
317        let mut ext = caps.current_extent;
318        if ext.width == u32::MAX {
319            let (ww, wh) = window_size(self.surf.0.window);
320            ext = vk::Extent2D {
321                width: ww.clamp(caps.min_image_extent.width, caps.max_image_extent.width),
322                height: wh.clamp(caps.min_image_extent.height, caps.max_image_extent.height),
323            };
324        }
325        self.stale.set(false);
326        self.outstanding.set(false);
327        let old = self.h;
328        if ext.width == 0 || ext.height == 0 {
329            // minimised / no area: keep nothing, acquire returns None until there is an area
330            self.release();
331            if old != 0 {
332                sh.defer(Garbage::Swapchain(old));
333            }
334            self.h = 0;
335            self.extent = (0, 0);
336            return Ok(());
337        }
338        // the present mode: the wanted one when the surface has it, else FIFO
339        let mut n = 0u32;
340        // SAFETY: the two-call enumeration.
341        sh.vk(unsafe { modes_fn(sh.pd, surf, &mut n, core::ptr::null_mut()) })?;
342        let mut modes = vec![0u32; n as usize];
343        // SAFETY: modes holds n entries.
344        let r = unsafe { modes_fn(sh.pd, surf, &mut n, modes.as_mut_ptr()) };
345        if r != vk::INCOMPLETE {
346            sh.vk(r)?;
347        }
348        modes.truncate(n as usize);
349        self.mode = if modes.contains(&present_mode_vk(self.desc.present)) { self.desc.present } else { PresentMode::Fifo };
350        let mut count = if self.desc.min_images != 0 { self.desc.min_images.max(caps.min_image_count) } else { caps.min_image_count + 1 };
351        if caps.max_image_count != 0 {
352            count = count.min(caps.max_image_count);
353        }
354        let mut usage = vk::IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
355        if caps.supported_usage_flags & vk::IMAGE_USAGE_TRANSFER_DST_BIT != 0 {
356            usage |= vk::IMAGE_USAGE_TRANSFER_DST_BIT;
357        }
358        let copy_src = self.desc.copy_src && caps.supported_usage_flags & vk::IMAGE_USAGE_TRANSFER_SRC_BIT != 0;
359        if copy_src {
360            usage |= vk::IMAGE_USAGE_TRANSFER_SRC_BIT;
361        }
362        let alpha = if caps.supported_composite_alpha & vk::COMPOSITE_ALPHA_OPAQUE_BIT_KHR != 0 {
363            vk::COMPOSITE_ALPHA_OPAQUE_BIT_KHR
364        } else if caps.supported_composite_alpha & vk::COMPOSITE_ALPHA_INHERIT_BIT_KHR != 0 {
365            vk::COMPOSITE_ALPHA_INHERIT_BIT_KHR
366        } else {
367            1 << caps.supported_composite_alpha.trailing_zeros().min(31)
368        };
369        let ci = vk::SwapchainCreateInfoKHR {
370            s_type: vk::STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
371            surface: surf,
372            min_image_count: count,
373            image_format: self.vk_format,
374            image_color_space: self.color_space,
375            image_extent: ext,
376            image_array_layers: 1,
377            image_usage: usage,
378            image_sharing_mode: vk::SHARING_MODE_EXCLUSIVE,
379            pre_transform: if cfg!(target_os = "android") && caps.supported_transforms & vk::SURFACE_TRANSFORM_IDENTITY_BIT_KHR != 0 {
380                vk::SURFACE_TRANSFORM_IDENTITY_BIT_KHR // (the file's header)
381            } else {
382                caps.current_transform
383            },
384            composite_alpha: alpha,
385            present_mode: present_mode_vk(self.mode),
386            clipped: 1,
387            old_swapchain: old,
388            ..vk::Zero::zero()
389        };
390        let mut h = 0;
391        // SAFETY: valid create info; old (if any) is retired by this call.
392        let r = unsafe { create(sh.dev, &ci, core::ptr::null(), &mut h) };
393        self.release();
394        if old != 0 {
395            sh.defer(Garbage::Swapchain(old));
396        }
397        self.h = 0;
398        sh.vk(r)?;
399        self.h = h;
400        self.extent = (ext.width, ext.height);
401        self.generation = self.generation.wrapping_add(1);
402        let mut n = 0u32;
403        // SAFETY: the two-call enumeration.
404        sh.vk(unsafe { images_fn(sh.dev, h, &mut n, core::ptr::null_mut()) })?;
405        let mut imgs = vec![0u64; n as usize];
406        // SAFETY: imgs holds n handles.
407        let r = unsafe { images_fn(sh.dev, h, &mut n, imgs.as_mut_ptr()) };
408        if r != vk::INCOMPLETE {
409            sh.vk(r)?;
410        }
411        imgs.truncate(n as usize);
412        let mut tu = TextureUsage::COLOR_TARGET | TextureUsage::COPY_DST;
413        if copy_src {
414            tu = tu | TextureUsage::COPY_SRC;
415        }
416        let td = TextureDesc::d2(ext.width, ext.height, self.format, tu);
417        for &img in &imgs {
418            self.images.push(crate::vk_res::wrap_image(&sh, img, td)?);
419        }
420        for _ in 0..self.desc.frames_in_flight {
421            let s = self.semaphore()?;
422            self.acquire.push(s);
423        }
424        for _ in 0..imgs.len() {
425            let s = self.semaphore()?;
426            self.done.push(s);
427        }
428        for v in &self.slot_value {
429            v.set(0);
430        }
431        Ok(())
432    }
433
434    /// (re)create the swapchain for the window's size now; the old images may still be in use:
435    /// waits for the queue to be idle (resizes are rare)
436    pub fn rebuild(&mut self) -> Result<()> {
437        self.sh.queue_idle()?;
438        self.build()
439    }
440
441    /// the next image to draw into, without waiting and without rebuilding (see TryAcquire)
442    pub fn try_acquire(&mut self) -> Result<TryAcquire> {
443        self.sh.check()?;
444        let Some(acq) = self.sh.f.acquire_next_image_khr else {
445            return Err(GpuError::Unsupported("VK_KHR_swapchain"));
446        };
447        if window_minimised(self.surf.0.window) {
448            return Ok(TryAcquire::NoArea);
449        }
450        if self.stale.get() || self.outstanding.get() || self.h == 0 || window_size(self.surf.0.window) != self.extent {
451            self.stale.set(true);
452            return Ok(TryAcquire::Stale);
453        }
454        let k = self.slot.get();
455        let prev = self.slot_value[k as usize].get();
456        if prev != 0 && prev > self.sh.refresh() {
457            self.sh.check()?;
458            return Ok(TryAcquire::NotReady);
459        }
460        let mut image = 0u32;
461        // SAFETY: the slot's semaphore is unsignalled (its last wait completed); no fence.
462        let r = unsafe { acq(self.sh.dev, self.h, 0, self.acquire[k as usize], 0, &mut image) };
463        match r {
464            vk::SUCCESS | vk::SUBOPTIMAL_KHR => {
465                if r == vk::SUBOPTIMAL_KHR && !TURNED_IS_FINE {
466                    self.stale.set(true);
467                }
468                self.outstanding.set(true);
469                Ok(TryAcquire::Frame(Frame { image, slot: k, generation: self.generation, size: self.extent }))
470            }
471            vk::ERROR_OUT_OF_DATE_KHR => {
472                self.stale.set(true);
473                Ok(TryAcquire::Stale)
474            }
475            vk::TIMEOUT | vk::NOT_READY => Ok(TryAcquire::NotReady),
476            _ => {
477                self.sh.vk(r)?;
478                Ok(TryAcquire::NotReady)
479            }
480        }
481    }
482
483    /// the next image to draw into; None while the window is minimised or has no area
484    pub fn acquire(&mut self) -> Result<Option<Frame>> {
485        self.sh.check()?;
486        let Some(acq) = self.sh.f.acquire_next_image_khr else {
487            return Err(GpuError::Unsupported("VK_KHR_swapchain"));
488        };
489        if window_minimised(self.surf.0.window) {
490            return Ok(None);
491        }
492        if self.outstanding.get() {
493            self.stale.set(true); // the last frame was never presented
494        }
495        if window_size(self.surf.0.window) != self.extent {
496            self.stale.set(true);
497        }
498        if self.stale.get() || self.h == 0 {
499            self.rebuild()?;
500        }
501        for attempt in 0..2 {
502            if self.h == 0 {
503                return Ok(None);
504            }
505            let k = self.slot.get();
506            let prev = self.slot_value[k as usize].get();
507            if prev != 0 && !self.sh.wait_value(prev, 10_000_000_000)? {
508                return Err(GpuError::Timeout);
509            }
510            let mut image = 0u32;
511            // SAFETY: the slot's semaphore is unsignalled (its last wait completed); no fence.
512            let r = unsafe { acq(self.sh.dev, self.h, 10_000_000_000, self.acquire[k as usize], 0, &mut image) };
513            match r {
514                vk::SUCCESS | vk::SUBOPTIMAL_KHR => {
515                    if r == vk::SUBOPTIMAL_KHR && !TURNED_IS_FINE {
516                        self.stale.set(true);
517                    }
518                    self.outstanding.set(true);
519                    return Ok(Some(Frame { image, slot: k, generation: self.generation, size: self.extent }));
520                }
521                vk::ERROR_OUT_OF_DATE_KHR if attempt == 0 => self.rebuild()?,
522                vk::ERROR_OUT_OF_DATE_KHR => return Ok(None),
523                vk::TIMEOUT | vk::NOT_READY => return Err(GpuError::Timeout),
524                _ => {
525                    self.sh.vk(r)?;
526                }
527            }
528        }
529        Ok(None)
530    }
531
532    /// the frame's image as a render target (Undefined at the start, Present at the end)
533    pub fn view(&self, frame: &Frame) -> &TextureView {
534        self.images[frame.image as usize].view()
535    }
536
537    /// the frame's image (copies: SwapchainDesc::copy_src)
538    pub fn texture(&self, frame: &Frame) -> &Texture {
539        &self.images[frame.image as usize]
540    }
541
542    /// submit the frame's commands (they leave the image in Layout::Present) and show it
543    pub fn present<'a, I: IntoIterator<Item = CommandBuffer<'a>>>(&self, frame: Frame, cmds: I) -> Result<SubmitId> {
544        let sh = &self.sh;
545        let mut cbs = Vec::new();
546        let mut slots = Vec::new();
547        for c in cmds {
548            let s = c.into_slot();
549            cbs.push(s.cb);
550            slots.push(s);
551        }
552        if frame.generation != self.generation || !self.outstanding.get() || self.h == 0 {
553            for s in slots {
554                sh.give_slot(s);
555            }
556            return Err(GpuError::Invalid("present: a frame of an older swapchain"));
557        }
558        let k = frame.slot as usize;
559        let wait = vk::PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | vk::PIPELINE_STAGE_TRANSFER_BIT | vk::PIPELINE_STAGE_COMPUTE_SHADER_BIT;
560        let done = self.done[frame.image as usize];
561        let value = sh.submit(&cbs, slots, Some((self.acquire[k], wait)), Some(done))?;
562        self.slot_value[k].set(value);
563        self.slot.set((frame.slot + 1) % self.desc.frames_in_flight);
564        self.outstanding.set(false);
565        let Some(qp) = sh.f.queue_present_khr else {
566            return Err(GpuError::Unsupported("VK_KHR_swapchain"));
567        };
568        let pi = vk::PresentInfoKHR {
569            s_type: vk::STRUCTURE_TYPE_PRESENT_INFO_KHR,
570            wait_semaphore_count: 1,
571            p_wait_semaphores: &done,
572            swapchain_count: 1,
573            p_swapchains: &self.h,
574            p_image_indices: &frame.image,
575            ..vk::Zero::zero()
576        };
577        // SAFETY: the image was acquired from this swapchain; the queue is held.
578        let r = sh.with_queue(|q| unsafe { qp(q, &pi) });
579        match r {
580            vk::SUCCESS => {}
581            vk::SUBOPTIMAL_KHR if TURNED_IS_FINE => {}
582            vk::SUBOPTIMAL_KHR | vk::ERROR_OUT_OF_DATE_KHR => self.stale.set(true),
583            _ => sh.vk(r)?,
584        }
585        Ok(SubmitId(value))
586    }
587
588    /// (Linux: the display has answered for the images; here nothing to wait for)
589    #[inline]
590    pub fn prepare(&mut self) -> Result<bool> {
591        Ok(true)
592    }
593
594    /// (Linux: a frame reaches the window after the GPU finished it; here present has done that)
595    #[inline]
596    pub fn flush(&self) -> Result<bool> {
597        Ok(false)
598    }
599
600    /// (Linux: a frame is on its way to the window; here never)
601    #[inline]
602    pub fn busy(&self) -> bool {
603        false
604    }
605
606    /// when a frame that found NotReady should try again: retry (the caller's time)
607    #[inline]
608    pub fn next_look(&self, retry: u64) -> u64 {
609        retry
610    }
611
612    /// (Linux: what the next image changes, for the compositor; here the whole image counts)
613    #[inline]
614    pub fn damage(&self, _rects: Option<&[[i32; 4]]>) {}
615
616    /// rebuild at the next acquire (the window's size is also checked at every acquire)
617    pub fn resize(&mut self) {
618        self.stale.set(true);
619    }
620
621    /// another present mode from the next acquire on (FIFO when the surface lacks it)
622    pub fn set_present_mode(&mut self, m: PresentMode) {
623        self.desc.present = m;
624        self.stale.set(true);
625    }
626
627    pub fn present_mode(&self) -> PresentMode {
628        self.mode
629    }
630    pub fn format(&self) -> Format {
631        self.format
632    }
633    /// the images' size (0, 0 while the window has no area)
634    pub fn size(&self) -> (u32, u32) {
635        self.extent
636    }
637    pub fn image_count(&self) -> usize {
638        self.images.len()
639    }
640}
641
642impl Drop for Swapchain {
643    fn drop(&mut self) {
644        let _ = self.sh.queue_idle(); // presents included: their semaphores and images are free
645        self.release();
646        if self.h != 0 {
647            self.sh.defer(Garbage::Swapchain(self.h));
648        }
649    }
650}
651
652#[cfg(any(windows, target_os = "android"))]
653fn window_size(w: i32) -> (u32, u32) {
654    let (x, y) = plat::win_size(w);
655    (x.max(0) as u32, y.max(0) as u32)
656}
657
658#[cfg(any(windows, target_os = "android"))]
659fn window_minimised(w: i32) -> bool {
660    plat::win_state(w) & plat::WST_MINIMIZED != 0
661}
662
663#[cfg(not(any(windows, target_os = "android")))]
664fn window_size(_w: i32) -> (u32, u32) {
665    (0, 0)
666}
667
668#[cfg(not(any(windows, target_os = "android")))]
669fn window_minimised(_w: i32) -> bool {
670    true
671}