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
29pub struct Display {
31 exec_idx: usize,
32 execs: Box<[Execution]>,
33 queue_family_idx: u32,
34 swapchain: Swapchain,
35}
36
37impl Display {
38 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 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 #[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 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(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 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 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 CommandBuffer::push_fenced_drop(&mut exec.cmd_buf, resolver);
256
257 Ok(())
258 }
259
260 pub fn set_swapchain_info(&mut self, info: impl Into<SwapchainInfo>) {
264 self.swapchain.set_info(info);
265 }
266
267 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 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#[derive(Debug)]
319pub enum DisplayError {
320 DeviceLost,
322
323 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#[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 #[builder(default = "4")]
360 command_buffer_count: usize,
361
362 #[builder(default = "0")]
364 queue_family_index: u32,
365}
366
367impl DisplayInfo {
368 #[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 #[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#[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}