screen_13/
display.rs

1use {
2    super::{
3        driver::{
4            CommandBuffer, CommandBufferInfo, DescriptorPool, DescriptorPoolInfo, DriverError,
5            RenderPass, RenderPassInfo,
6            device::Device,
7            image::Image,
8            image_access_layout,
9            swapchain::{Swapchain, SwapchainImage, SwapchainInfo},
10        },
11        graph::{RenderGraph, node::SwapchainImageNode},
12        pool::Pool,
13    },
14    crate::prelude::SwapchainError,
15    ash::vk,
16    derive_builder::{Builder, UninitializedFieldError},
17    log::{trace, warn},
18    std::{
19        error::Error,
20        fmt::{Debug, Formatter},
21        slice,
22        sync::Arc,
23        thread::panicking,
24        time::Instant,
25    },
26    vk_sync::{AccessType, ImageBarrier, cmd::pipeline_barrier},
27};
28
29/// A physical display interface.
30pub struct Display {
31    exec_idx: usize,
32    execs: Box<[Execution]>,
33    queue_family_idx: u32,
34    swapchain: Swapchain,
35}
36
37impl Display {
38    /// Constructs a new `Display` object.
39    pub fn new(
40        device: &Arc<Device>,
41        swapchain: Swapchain,
42        info: impl Into<DisplayInfo>,
43    ) -> Result<Self, DriverError> {
44        let info: DisplayInfo = info.into();
45
46        assert_ne!(info.command_buffer_count, 0);
47
48        let mut execs = Vec::with_capacity(info.command_buffer_count as _);
49        for _ in 0..info.command_buffer_count {
50            let cmd_buf =
51                CommandBuffer::create(device, CommandBufferInfo::new(info.queue_family_index))?;
52            let swapchain_acquired = Device::create_semaphore(device)?;
53            let swapchain_rendered = Device::create_semaphore(device)?;
54
55            execs.push(Execution {
56                cmd_buf,
57                queue: None,
58                swapchain_acquired,
59                swapchain_rendered,
60            });
61        }
62        let execs = execs.into_boxed_slice();
63
64        Ok(Self {
65            exec_idx: info.command_buffer_count,
66            execs,
67            queue_family_idx: info.queue_family_index,
68            swapchain,
69        })
70    }
71
72    /// Gets the next available swapchain image which should be rendered to and then presented using
73    /// [`present_image`][Self::present_image].
74    pub fn acquire_next_image(&mut self) -> Result<Option<SwapchainImage>, DisplayError> {
75        self.exec_idx += 1;
76        self.exec_idx %= self.execs.len();
77        let exec = &mut self.execs[self.exec_idx];
78
79        if exec.queue.is_some() {
80            CommandBuffer::wait_until_executed(&mut exec.cmd_buf).inspect_err(|err| {
81                warn!("unable to wait for display fence: {err}");
82            })?;
83
84            exec.queue = None;
85        }
86
87        CommandBuffer::drop_fenced(&mut exec.cmd_buf);
88
89        unsafe {
90            exec.cmd_buf
91                .device
92                .reset_fences(slice::from_ref(&exec.cmd_buf.fence))
93                .map_err(|err| {
94                    warn!("unable to reset display fence: {err}");
95
96                    DriverError::InvalidData
97                })?;
98        }
99
100        let acquire_next_image = self.swapchain.acquire_next_image(exec.swapchain_acquired);
101
102        if let Err(err) = acquire_next_image {
103            warn!("unable to acquire next swapchain image: {err:?}");
104        }
105
106        let mut swapchain_image = match acquire_next_image {
107            Err(SwapchainError::DeviceLost) => Err(DisplayError::DeviceLost),
108            Err(SwapchainError::Suboptimal) => return Ok(None),
109            Err(SwapchainError::SurfaceLost) => Err(DisplayError::Driver(DriverError::InvalidData)),
110            Ok(swapchain_image) => Ok(swapchain_image),
111        }?;
112        swapchain_image.exec_idx = self.exec_idx;
113
114        Ok(Some(swapchain_image))
115    }
116
117    /// Displays the given swapchain image using passes specified in `render_graph`, if possible.
118    #[profiling::function]
119    pub fn present_image(
120        &mut self,
121        pool: &mut impl ResolverPool,
122        render_graph: RenderGraph,
123        swapchain_image: SwapchainImageNode,
124        queue_index: u32,
125    ) -> Result<(), DisplayError> {
126        trace!("present_image");
127
128        let mut resolver = render_graph.resolve();
129        let wait_dst_stage_mask = resolver.node_pipeline_stages(swapchain_image);
130
131        // The swapchain should have been written to, otherwise it would be noise and that's a panic
132        assert!(
133            !wait_dst_stage_mask.is_empty(),
134            "uninitialized swapchain image: write something each frame!",
135        );
136
137        let exec_idx = resolver.swapchain_image(swapchain_image).exec_idx;
138        let exec = &mut self.execs[exec_idx];
139
140        debug_assert!(exec.queue.is_none());
141
142        let started = Instant::now();
143
144        unsafe {
145            exec.cmd_buf
146                .device
147                .begin_command_buffer(
148                    *exec.cmd_buf,
149                    &vk::CommandBufferBeginInfo::default()
150                        .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT),
151                )
152                .map_err(|_| ())?;
153        }
154
155        // resolver.record_node_dependencies(&mut *self.pool, cmd_buf, swapchain_image)?;
156        resolver.record_node(pool, &mut exec.cmd_buf, swapchain_image)?;
157
158        {
159            let swapchain_image = resolver.swapchain_image(swapchain_image);
160            for (access, range) in Image::access(
161                swapchain_image,
162                AccessType::Present,
163                vk::ImageSubresourceRange {
164                    aspect_mask: vk::ImageAspectFlags::COLOR,
165                    base_array_layer: 0,
166                    base_mip_level: 0,
167                    layer_count: 1,
168                    level_count: 1,
169                },
170            ) {
171                trace!(
172                    "image {:?} {:?}->{:?}",
173                    **swapchain_image,
174                    access,
175                    AccessType::Present,
176                );
177
178                // Force a presentation layout transition
179                pipeline_barrier(
180                    &exec.cmd_buf.device,
181                    *exec.cmd_buf,
182                    None,
183                    &[],
184                    slice::from_ref(&ImageBarrier {
185                        previous_accesses: slice::from_ref(&access),
186                        previous_layout: image_access_layout(access),
187                        next_accesses: slice::from_ref(&AccessType::Present),
188                        next_layout: image_access_layout(AccessType::Present),
189                        discard_contents: false,
190                        src_queue_family_index: vk::QUEUE_FAMILY_IGNORED,
191                        dst_queue_family_index: vk::QUEUE_FAMILY_IGNORED,
192                        image: ***swapchain_image,
193                        range,
194                    }),
195                );
196            }
197        }
198
199        // We may have unresolved nodes; things like copies that happen after present or operations
200        // before present which use nodes that are unused in the remainder of the graph.
201        // These operations are still important, but they don't need to wait for any of the above
202        // things so we do them last
203        resolver.record_unscheduled_passes(pool, &mut exec.cmd_buf)?;
204
205        let queue =
206            exec.cmd_buf.device.queues[self.queue_family_idx as usize][queue_index as usize];
207
208        unsafe {
209            exec.cmd_buf
210                .device
211                .end_command_buffer(*exec.cmd_buf)
212                .map_err(|err| {
213                    warn!("unable to end display command buffer: {err}");
214
215                    DriverError::InvalidData
216                })?;
217            exec.cmd_buf
218                .device
219                .queue_submit(
220                    queue,
221                    slice::from_ref(
222                        &vk::SubmitInfo::default()
223                            .command_buffers(slice::from_ref(&exec.cmd_buf))
224                            .wait_semaphores(slice::from_ref(&exec.swapchain_acquired))
225                            .wait_dst_stage_mask(slice::from_ref(&wait_dst_stage_mask))
226                            .signal_semaphores(slice::from_ref(&exec.swapchain_rendered)),
227                    ),
228                    exec.cmd_buf.fence,
229                )
230                .map_err(|err| {
231                    warn!("unable to submit display command buffer: {err}");
232
233                    DriverError::InvalidData
234                })?
235        }
236
237        exec.cmd_buf.waiting = true;
238        exec.queue = Some(queue);
239
240        let elapsed = Instant::now() - started;
241        trace!("🔜🔜🔜 vkQueueSubmit took {} Ξs", elapsed.as_micros(),);
242
243        let swapchain_image =
244            SwapchainImage::clone_swapchain(resolver.swapchain_image(swapchain_image));
245
246        self.swapchain.present_image(
247            swapchain_image,
248            slice::from_ref(&exec.swapchain_rendered),
249            self.queue_family_idx,
250            queue_index,
251        );
252
253        // Store the resolved graph because it contains bindings, leases, and other shared resources
254        // that need to be kept alive until the fence is waited upon.
255        CommandBuffer::push_fenced_drop(&mut exec.cmd_buf, resolver);
256
257        Ok(())
258    }
259
260    /// Sets information about the swapchain.
261    ///
262    /// Previously acquired swapchain images should be discarded after calling this function.
263    pub fn set_swapchain_info(&mut self, info: impl Into<SwapchainInfo>) {
264        self.swapchain.set_info(info);
265    }
266
267    /// Gets information about the swapchain.
268    pub fn swapchain_info(&self) -> SwapchainInfo {
269        self.swapchain.info()
270    }
271}
272
273impl Debug for Display {
274    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
275        f.write_str("Display")
276    }
277}
278
279impl Drop for Display {
280    fn drop(&mut self) {
281        if panicking() {
282            return;
283        }
284
285        let idle = unsafe { self.execs[0].cmd_buf.device.device_wait_idle() };
286        if idle.is_err() {
287            warn!("unable to wait for device");
288
289            return;
290        }
291
292        for batch in &mut self.execs {
293            if let Some(queue) = batch.queue {
294                // Wait for presentation to stop
295                let present = unsafe { batch.cmd_buf.device.queue_wait_idle(queue) };
296                if present.is_err() {
297                    warn!("unable to wait for queue");
298
299                    continue;
300                }
301            }
302
303            unsafe {
304                batch
305                    .cmd_buf
306                    .device
307                    .destroy_semaphore(batch.swapchain_acquired, None);
308                batch
309                    .cmd_buf
310                    .device
311                    .destroy_semaphore(batch.swapchain_rendered, None);
312            }
313        }
314    }
315}
316
317/// Describes error conditions relating to physical displays.
318#[derive(Debug)]
319pub enum DisplayError {
320    /// Unrecoverable device error; must destroy this device and display and start a new one
321    DeviceLost,
322
323    /// Recoverable driver error
324    Driver(DriverError),
325}
326
327impl Error for DisplayError {}
328
329impl From<()> for DisplayError {
330    fn from(_: ()) -> Self {
331        Self::DeviceLost
332    }
333}
334
335impl From<DriverError> for DisplayError {
336    fn from(err: DriverError) -> Self {
337        Self::Driver(err)
338    }
339}
340
341impl std::fmt::Display for DisplayError {
342    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
343        write!(f, "{:?}", self)
344    }
345}
346
347/// Information used to create a [`Display`] instance.
348#[derive(Builder, Clone, Copy, Debug, Eq, Hash, PartialEq)]
349#[builder(
350    build_fn(private, name = "fallible_build", error = "DisplayInfoBuilderError"),
351    derive(Clone, Copy, Debug),
352    pattern = "owned"
353)]
354#[non_exhaustive]
355pub struct DisplayInfo {
356    /// The number of command buffers to use for image submissions.
357    ///
358    /// Generally one more than the swapchain image count is best.
359    #[builder(default = "4")]
360    command_buffer_count: usize,
361
362    /// The device queue family which will be used to submit and present images.
363    #[builder(default = "0")]
364    queue_family_index: u32,
365}
366
367impl DisplayInfo {
368    /// Converts a `DisplayInfo` into a `DisplayInfoBuilder`.
369    #[inline(always)]
370    pub fn to_builder(self) -> DisplayInfoBuilder {
371        DisplayInfoBuilder {
372            command_buffer_count: Some(self.command_buffer_count),
373            queue_family_index: Some(self.queue_family_index),
374        }
375    }
376}
377
378impl Default for DisplayInfo {
379    fn default() -> Self {
380        Self {
381            command_buffer_count: 4,
382            queue_family_index: 0,
383        }
384    }
385}
386
387impl From<DisplayInfoBuilder> for DisplayInfo {
388    fn from(info: DisplayInfoBuilder) -> Self {
389        info.build()
390    }
391}
392
393impl DisplayInfoBuilder {
394    /// Builds a new `DisplayInfo`.
395    ///
396    /// # Panics
397    ///
398    /// If any of the following values have not been set this function will panic:
399    ///
400    /// * `command_buffer_count`
401    #[inline(always)]
402    pub fn build(self) -> DisplayInfo {
403        let info = match self.fallible_build() {
404            Err(DisplayInfoBuilderError(err)) => panic!("{err}"),
405            Ok(info) => info,
406        };
407
408        assert_ne!(
409            info.command_buffer_count, 0,
410            "Field value invalid: command_buffer_count"
411        );
412
413        info
414    }
415}
416
417#[derive(Debug)]
418struct DisplayInfoBuilderError(UninitializedFieldError);
419
420impl From<UninitializedFieldError> for DisplayInfoBuilderError {
421    fn from(err: UninitializedFieldError) -> Self {
422        Self(err)
423    }
424}
425
426struct Execution {
427    cmd_buf: CommandBuffer,
428    queue: Option<vk::Queue>,
429    swapchain_acquired: vk::Semaphore,
430    swapchain_rendered: vk::Semaphore,
431}
432
433/// Combination trait which groups together all [`Pool`] traits required for a [`Resolver`]
434/// instance.
435///
436/// [`Resolver`]: crate::graph::Resolver
437#[allow(private_bounds)]
438pub trait ResolverPool:
439    Pool<DescriptorPoolInfo, DescriptorPool>
440    + Pool<RenderPassInfo, RenderPass>
441    + Pool<CommandBufferInfo, CommandBuffer>
442    + Send
443{
444}
445
446impl<T> ResolverPool for T where
447    T: Pool<DescriptorPoolInfo, DescriptorPool>
448        + Pool<RenderPassInfo, RenderPass>
449        + Pool<CommandBufferInfo, CommandBuffer>
450        + Send
451{
452}
453
454#[cfg(test)]
455mod tests {
456    use super::*;
457
458    type Info = DisplayInfo;
459    type Builder = DisplayInfoBuilder;
460
461    #[test]
462    pub fn display_info() {
463        let info = Info {
464            command_buffer_count: 42,
465            queue_family_index: 16,
466        };
467        let builder = info.to_builder().build();
468
469        assert_eq!(info, builder);
470    }
471
472    #[test]
473    pub fn display_info_builder() {
474        let info = Info {
475            command_buffer_count: 42,
476            queue_family_index: 16,
477        };
478        let builder = Builder::default()
479            .command_buffer_count(42)
480            .queue_family_index(16)
481            .build();
482
483        assert_eq!(info, builder);
484    }
485
486    #[test]
487    pub fn display_info_default() {
488        let info = Info::default();
489        let builder = Builder::default().build();
490
491        assert_eq!(info, builder);
492    }
493
494    #[test]
495    #[should_panic(expected = "Field value invalid: command_buffer_count")]
496    pub fn display_info_builder_uninit_command_buffer_count() {
497        Builder::default().command_buffer_count(0).build();
498    }
499}