diff --git a/examples/src/bin/async-update.rs b/examples/src/bin/async-update.rs index ae080839..69ff06a8 100644 --- a/examples/src/bin/async-update.rs +++ b/examples/src/bin/async-update.rs @@ -85,7 +85,7 @@ use vulkano::{ sampler::{Sampler, SamplerCreateInfo}, swapchain::{ acquire_next_image, AcquireError, Surface, Swapchain, SwapchainCreateInfo, - SwapchainCreationError, SwapchainPresentInfo, + SwapchainPresentInfo, }, sync::{self, FlushError, GpuFuture}, VulkanLibrary, @@ -545,21 +545,19 @@ fn main() { channel.send(()).unwrap(); } Event::RedrawEventsCleared => { - let dimensions = window.inner_size(); - if dimensions.width == 0 || dimensions.height == 0 { + let image_extent: [u32; 2] = window.inner_size().into(); + + if image_extent.contains(&0) { return; } if recreate_swapchain { - let (new_swapchain, new_images) = - match swapchain.recreate(SwapchainCreateInfo { - image_extent: dimensions.into(), + let (new_swapchain, new_images) = swapchain + .recreate(SwapchainCreateInfo { + image_extent, ..swapchain.create_info() - }) { - Ok(r) => r, - Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return, - Err(e) => panic!("failed to recreate swapchain: {e}"), - }; + }) + .expect("failed to recreate swapchain"); swapchain = new_swapchain; framebuffers = window_size_dependent_setup( diff --git a/examples/src/bin/buffer-allocator.rs b/examples/src/bin/buffer-allocator.rs index da3352d1..5cb50eec 100644 --- a/examples/src/bin/buffer-allocator.rs +++ b/examples/src/bin/buffer-allocator.rs @@ -45,7 +45,7 @@ use vulkano::{ render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, swapchain::{ acquire_next_image, AcquireError, Surface, Swapchain, SwapchainCreateInfo, - SwapchainCreationError, SwapchainPresentInfo, + SwapchainPresentInfo, }, sync::{self, FlushError, GpuFuture}, VulkanLibrary, @@ -292,23 +292,21 @@ fn main() { recreate_swapchain = true; } Event::RedrawEventsCleared => { - let dimensions = window.inner_size(); - if dimensions.width == 0 || dimensions.height == 0 { + let image_extent: [u32; 2] = window.inner_size().into(); + + if image_extent.contains(&0) { return; } previous_frame_end.as_mut().unwrap().cleanup_finished(); if recreate_swapchain { - let (new_swapchain, new_images) = - match swapchain.recreate(SwapchainCreateInfo { - image_extent: dimensions.into(), + let (new_swapchain, new_images) = swapchain + .recreate(SwapchainCreateInfo { + image_extent, ..swapchain.create_info() - }) { - Ok(r) => r, - Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return, - Err(e) => panic!("failed to recreate swapchain: {e}"), - }; + }) + .expect("failed to recreate swapchain"); swapchain = new_swapchain; framebuffers = window_size_dependent_setup( diff --git a/examples/src/bin/clear_attachments.rs b/examples/src/bin/clear_attachments.rs index f49cf544..87f74ac1 100644 --- a/examples/src/bin/clear_attachments.rs +++ b/examples/src/bin/clear_attachments.rs @@ -22,7 +22,7 @@ use vulkano::{ render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass}, swapchain::{ acquire_next_image, AcquireError, Surface, Swapchain, SwapchainCreateInfo, - SwapchainCreationError, SwapchainPresentInfo, + SwapchainPresentInfo, }, sync::{self, FlushError, GpuFuture}, VulkanLibrary, @@ -175,22 +175,21 @@ fn main() { recreate_swapchain = true; } Event::RedrawEventsCleared => { - let dimensions = window.inner_size(); - if dimensions.width == 0 || dimensions.height == 0 { + let image_extent: [u32; 2] = window.inner_size().into(); + + if image_extent.contains(&0) { return; } previous_frame_end.as_mut().unwrap().cleanup_finished(); if recreate_swapchain { - let (new_swapchain, new_images) = match swapchain.recreate(SwapchainCreateInfo { - image_extent: dimensions.into(), - ..swapchain.create_info() - }) { - Ok(r) => r, - Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return, - Err(e) => panic!("failed to recreate swapchain: {e}"), - }; + let (new_swapchain, new_images) = swapchain + .recreate(SwapchainCreateInfo { + image_extent, + ..swapchain.create_info() + }) + .expect("failed to recreate swapchain"); swapchain = new_swapchain; width = swapchain.image_extent()[0]; diff --git a/examples/src/bin/deferred/main.rs b/examples/src/bin/deferred/main.rs index ed67a158..0be05088 100644 --- a/examples/src/bin/deferred/main.rs +++ b/examples/src/bin/deferred/main.rs @@ -42,7 +42,7 @@ use vulkano::{ memory::allocator::StandardMemoryAllocator, swapchain::{ acquire_next_image, AcquireError, Surface, Swapchain, SwapchainCreateInfo, - SwapchainCreationError, SwapchainPresentInfo, + SwapchainPresentInfo, }, sync::{self, FlushError, GpuFuture}, VulkanLibrary, @@ -199,22 +199,21 @@ fn main() { recreate_swapchain = true; } Event::RedrawEventsCleared => { - let dimensions = window.inner_size(); - if dimensions.width == 0 || dimensions.height == 0 { + let image_extent: [u32; 2] = window.inner_size().into(); + + if image_extent.contains(&0) { return; } previous_frame_end.as_mut().unwrap().cleanup_finished(); if recreate_swapchain { - let (new_swapchain, new_images) = match swapchain.recreate(SwapchainCreateInfo { - image_extent: dimensions.into(), - ..swapchain.create_info() - }) { - Ok(r) => r, - Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return, - Err(e) => panic!("failed to recreate swapchain: {e}"), - }; + let (new_swapchain, new_images) = swapchain + .recreate(SwapchainCreateInfo { + image_extent, + ..swapchain.create_info() + }) + .expect("failed to recreate swapchain"); let new_images = new_images .into_iter() .map(|image| ImageView::new_default(image).unwrap()) diff --git a/examples/src/bin/gl-interop.rs b/examples/src/bin/gl-interop.rs index c06ea0cb..ebc3b581 100644 --- a/examples/src/bin/gl-interop.rs +++ b/examples/src/bin/gl-interop.rs @@ -50,10 +50,7 @@ mod linux { }, render_pass::{Framebuffer, RenderPass, Subpass}, sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo}, - swapchain::{ - AcquireError, Surface, Swapchain, SwapchainCreateInfo, SwapchainCreationError, - SwapchainPresentInfo, - }, + swapchain::{AcquireError, Surface, Swapchain, SwapchainCreateInfo, SwapchainPresentInfo}, sync::{ now, semaphore::{ @@ -287,20 +284,21 @@ mod linux { }) .unwrap(); + let image_extent: [u32; 2] = window.inner_size().into(); + + if image_extent.contains(&0) { + return; + } + previous_frame_end.as_mut().unwrap().cleanup_finished(); if recreate_swapchain { - let (new_swapchain, new_images) = - match swapchain.recreate(SwapchainCreateInfo { - image_extent: window.inner_size().into(), + let (new_swapchain, new_images) = swapchain + .recreate(SwapchainCreateInfo { + image_extent, ..swapchain.create_info() - }) { - Ok(r) => r, - Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => { - return - } - Err(e) => panic!("failed to recreate swapchain: {e}"), - }; + }) + .expect("failed to recreate swapchain"); swapchain = new_swapchain; framebuffers = window_size_dependent_setup( diff --git a/examples/src/bin/image-self-copy-blit/main.rs b/examples/src/bin/image-self-copy-blit/main.rs index 16e5f498..a6b8b21f 100644 --- a/examples/src/bin/image-self-copy-blit/main.rs +++ b/examples/src/bin/image-self-copy-blit/main.rs @@ -48,7 +48,7 @@ use vulkano::{ sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo}, swapchain::{ acquire_next_image, AcquireError, Surface, Swapchain, SwapchainCreateInfo, - SwapchainCreationError, SwapchainPresentInfo, + SwapchainPresentInfo, }, sync::{self, FlushError, GpuFuture}, VulkanLibrary, @@ -421,22 +421,21 @@ fn main() { recreate_swapchain = true; } Event::RedrawEventsCleared => { - let dimensions = window.inner_size(); - if dimensions.width == 0 || dimensions.height == 0 { + let image_extent: [u32; 2] = window.inner_size().into(); + + if image_extent.contains(&0) { return; } previous_frame_end.as_mut().unwrap().cleanup_finished(); if recreate_swapchain { - let (new_swapchain, new_images) = match swapchain.recreate(SwapchainCreateInfo { - image_extent: dimensions.into(), - ..swapchain.create_info() - }) { - Ok(r) => r, - Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return, - Err(e) => panic!("failed to recreate swapchain: {e}"), - }; + let (new_swapchain, new_images) = swapchain + .recreate(SwapchainCreateInfo { + image_extent, + ..swapchain.create_info() + }) + .expect("failed to recreate swapchain"); swapchain = new_swapchain; framebuffers = diff --git a/examples/src/bin/image/main.rs b/examples/src/bin/image/main.rs index 7d3ed079..b6582afb 100644 --- a/examples/src/bin/image/main.rs +++ b/examples/src/bin/image/main.rs @@ -46,7 +46,7 @@ use vulkano::{ sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo}, swapchain::{ acquire_next_image, AcquireError, Surface, Swapchain, SwapchainCreateInfo, - SwapchainCreationError, SwapchainPresentInfo, + SwapchainPresentInfo, }, sync::{self, FlushError, GpuFuture}, VulkanLibrary, @@ -347,22 +347,21 @@ fn main() { recreate_swapchain = true; } Event::RedrawEventsCleared => { - let dimensions = window.inner_size(); - if dimensions.width == 0 || dimensions.height == 0 { + let image_extent: [u32; 2] = window.inner_size().into(); + + if image_extent.contains(&0) { return; } previous_frame_end.as_mut().unwrap().cleanup_finished(); if recreate_swapchain { - let (new_swapchain, new_images) = match swapchain.recreate(SwapchainCreateInfo { - image_extent: dimensions.into(), - ..swapchain.create_info() - }) { - Ok(r) => r, - Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return, - Err(e) => panic!("failed to recreate swapchain: {e}"), - }; + let (new_swapchain, new_images) = swapchain + .recreate(SwapchainCreateInfo { + image_extent, + ..swapchain.create_info() + }) + .expect("failed to recreate swapchain"); swapchain = new_swapchain; framebuffers = diff --git a/examples/src/bin/immutable-sampler/main.rs b/examples/src/bin/immutable-sampler/main.rs index 96b658c1..3814f043 100644 --- a/examples/src/bin/immutable-sampler/main.rs +++ b/examples/src/bin/immutable-sampler/main.rs @@ -55,7 +55,7 @@ use vulkano::{ sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo}, swapchain::{ acquire_next_image, AcquireError, Surface, Swapchain, SwapchainCreateInfo, - SwapchainCreationError, SwapchainPresentInfo, + SwapchainPresentInfo, }, sync::{self, FlushError, GpuFuture}, VulkanLibrary, @@ -368,22 +368,21 @@ fn main() { recreate_swapchain = true; } Event::RedrawEventsCleared => { - let dimensions = window.inner_size(); - if dimensions.width == 0 || dimensions.height == 0 { + let image_extent: [u32; 2] = window.inner_size().into(); + + if image_extent.contains(&0) { return; } previous_frame_end.as_mut().unwrap().cleanup_finished(); if recreate_swapchain { - let (new_swapchain, new_images) = match swapchain.recreate(SwapchainCreateInfo { - image_extent: dimensions.into(), - ..swapchain.create_info() - }) { - Ok(r) => r, - Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return, - Err(e) => panic!("failed to recreate swapchain: {e}"), - }; + let (new_swapchain, new_images) = swapchain + .recreate(SwapchainCreateInfo { + image_extent, + ..swapchain.create_info() + }) + .expect("failed to recreate swapchain"); swapchain = new_swapchain; framebuffers = diff --git a/examples/src/bin/indirect.rs b/examples/src/bin/indirect.rs index 01b0cd7f..f196bc25 100644 --- a/examples/src/bin/indirect.rs +++ b/examples/src/bin/indirect.rs @@ -62,7 +62,7 @@ use vulkano::{ single_pass_renderpass, swapchain::{ acquire_next_image, AcquireError, Surface, Swapchain, SwapchainCreateInfo, - SwapchainCreationError, SwapchainPresentInfo, + SwapchainPresentInfo, }, sync::{self, FlushError, GpuFuture}, VulkanLibrary, @@ -384,23 +384,21 @@ fn main() { recreate_swapchain = true; } Event::RedrawEventsCleared => { - let dimensions = window.inner_size(); - if dimensions.width == 0 || dimensions.height == 0 { + let image_extent: [u32; 2] = window.inner_size().into(); + + if image_extent.contains(&0) { return; } previous_frame_end.as_mut().unwrap().cleanup_finished(); if recreate_swapchain { - let (new_swapchain, new_images) = - match swapchain.recreate(SwapchainCreateInfo { - image_extent: dimensions.into(), + let (new_swapchain, new_images) = swapchain + .recreate(SwapchainCreateInfo { + image_extent, ..swapchain.create_info() - }) { - Ok(r) => r, - Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return, - Err(e) => panic!("failed to recreate swapchain: {e}"), - }; + }) + .expect("failed to recreate swapchain"); swapchain = new_swapchain; framebuffers = window_size_dependent_setup( diff --git a/examples/src/bin/instancing.rs b/examples/src/bin/instancing.rs index 024181eb..e53eb934 100644 --- a/examples/src/bin/instancing.rs +++ b/examples/src/bin/instancing.rs @@ -43,7 +43,7 @@ use vulkano::{ single_pass_renderpass, swapchain::{ acquire_next_image, AcquireError, Surface, Swapchain, SwapchainCreateInfo, - SwapchainCreationError, SwapchainPresentInfo, + SwapchainPresentInfo, }, sync::{self, FlushError, GpuFuture}, VulkanLibrary, @@ -364,23 +364,21 @@ fn main() { recreate_swapchain = true; } Event::RedrawEventsCleared => { - let dimensions = window.inner_size(); - if dimensions.width == 0 || dimensions.height == 0 { + let image_extent: [u32; 2] = window.inner_size().into(); + + if image_extent.contains(&0) { return; } previous_frame_end.as_mut().unwrap().cleanup_finished(); if recreate_swapchain { - let (new_swapchain, new_images) = - match swapchain.recreate(SwapchainCreateInfo { - image_extent: dimensions.into(), + let (new_swapchain, new_images) = swapchain + .recreate(SwapchainCreateInfo { + image_extent, ..swapchain.create_info() - }) { - Ok(r) => r, - Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return, - Err(e) => panic!("failed to recreate swapchain: {e}"), - }; + }) + .expect("failed to recreate swapchain"); swapchain = new_swapchain; framebuffers = window_size_dependent_setup( diff --git a/examples/src/bin/multi-window.rs b/examples/src/bin/multi-window.rs index a7c5063d..489dfc65 100644 --- a/examples/src/bin/multi-window.rs +++ b/examples/src/bin/multi-window.rs @@ -46,7 +46,7 @@ use vulkano::{ render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, swapchain::{ acquire_next_image, AcquireError, Surface, Swapchain, SwapchainCreateInfo, - SwapchainCreationError, SwapchainPresentInfo, + SwapchainPresentInfo, }, sync::{self, FlushError, GpuFuture}, VulkanLibrary, @@ -415,22 +415,21 @@ fn main() { previous_frame_end, } = window_surfaces.get_mut(&window_id).unwrap(); - let dimensions = window.inner_size(); - if dimensions.width == 0 || dimensions.height == 0 { + let image_extent: [u32; 2] = window.inner_size().into(); + + if image_extent.contains(&0) { return; } previous_frame_end.as_mut().unwrap().cleanup_finished(); if *recreate_swapchain { - let (new_swapchain, new_images) = match swapchain.recreate(SwapchainCreateInfo { - image_extent: dimensions.into(), - ..swapchain.create_info() - }) { - Ok(r) => r, - Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return, - Err(e) => panic!("failed to recreate swapchain: {e}"), - }; + let (new_swapchain, new_images) = swapchain + .recreate(SwapchainCreateInfo { + image_extent, + ..swapchain.create_info() + }) + .expect("failed to recreate swapchain"); *swapchain = new_swapchain; *framebuffers = diff --git a/examples/src/bin/occlusion-query.rs b/examples/src/bin/occlusion-query.rs index 568fe720..2fec803a 100644 --- a/examples/src/bin/occlusion-query.rs +++ b/examples/src/bin/occlusion-query.rs @@ -44,7 +44,7 @@ use vulkano::{ render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, swapchain::{ acquire_next_image, AcquireError, Surface, Swapchain, SwapchainCreateInfo, - SwapchainCreationError, SwapchainPresentInfo, + SwapchainPresentInfo, }, sync::{self, FlushError, GpuFuture}, VulkanLibrary, @@ -382,22 +382,21 @@ fn main() { recreate_swapchain = true; } Event::RedrawEventsCleared => { - let dimensions = window.inner_size(); - if dimensions.width == 0 || dimensions.height == 0 { + let image_extent: [u32; 2] = window.inner_size().into(); + + if image_extent.contains(&0) { return; } previous_frame_end.as_mut().unwrap().cleanup_finished(); if recreate_swapchain { - let (new_swapchain, new_images) = match swapchain.recreate(SwapchainCreateInfo { - image_extent: dimensions.into(), - ..swapchain.create_info() - }) { - Ok(r) => r, - Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return, - Err(e) => panic!("failed to recreate swapchain: {e}"), - }; + let (new_swapchain, new_images) = swapchain + .recreate(SwapchainCreateInfo { + image_extent, + ..swapchain.create_info() + }) + .expect("failed to recreate swapchain"); swapchain = new_swapchain; framebuffers = window_size_dependent_setup( diff --git a/examples/src/bin/push-descriptors/main.rs b/examples/src/bin/push-descriptors/main.rs index 88257528..7f410860 100644 --- a/examples/src/bin/push-descriptors/main.rs +++ b/examples/src/bin/push-descriptors/main.rs @@ -44,7 +44,7 @@ use vulkano::{ sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo}, swapchain::{ acquire_next_image, AcquireError, Surface, Swapchain, SwapchainCreateInfo, - SwapchainCreationError, SwapchainPresentInfo, + SwapchainPresentInfo, }, sync::{self, FlushError, GpuFuture}, VulkanLibrary, @@ -341,22 +341,21 @@ fn main() { recreate_swapchain = true; } Event::RedrawEventsCleared => { - let dimensions = window.inner_size(); - if dimensions.width == 0 || dimensions.height == 0 { + let image_extent: [u32; 2] = window.inner_size().into(); + + if image_extent.contains(&0) { return; } previous_frame_end.as_mut().unwrap().cleanup_finished(); if recreate_swapchain { - let (new_swapchain, new_images) = match swapchain.recreate(SwapchainCreateInfo { - image_extent: dimensions.into(), - ..swapchain.create_info() - }) { - Ok(r) => r, - Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return, - Err(e) => panic!("failed to recreate swapchain: {e}"), - }; + let (new_swapchain, new_images) = swapchain + .recreate(SwapchainCreateInfo { + image_extent, + ..swapchain.create_info() + }) + .expect("failed to recreate swapchain"); swapchain = new_swapchain; framebuffers = diff --git a/examples/src/bin/runtime-shader/main.rs b/examples/src/bin/runtime-shader/main.rs index fed4efa5..ffde1850 100644 --- a/examples/src/bin/runtime-shader/main.rs +++ b/examples/src/bin/runtime-shader/main.rs @@ -52,7 +52,7 @@ use vulkano::{ shader::ShaderModule, swapchain::{ acquire_next_image, AcquireError, Surface, Swapchain, SwapchainCreateInfo, - SwapchainCreationError, SwapchainPresentInfo, + SwapchainPresentInfo, }, sync::{self, FlushError, GpuFuture}, VulkanLibrary, @@ -312,22 +312,21 @@ fn main() { recreate_swapchain = true; } Event::RedrawEventsCleared => { - let dimensions = window.inner_size(); - if dimensions.width == 0 || dimensions.height == 0 { + let image_extent: [u32; 2] = window.inner_size().into(); + + if image_extent.contains(&0) { return; } previous_frame_end.as_mut().unwrap().cleanup_finished(); if recreate_swapchain { - let (new_swapchain, new_images) = match swapchain.recreate(SwapchainCreateInfo { - image_extent: dimensions.into(), - ..swapchain.create_info() - }) { - Ok(r) => r, - Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return, - Err(e) => panic!("failed to recreate swapchain: {e}"), - }; + let (new_swapchain, new_images) = swapchain + .recreate(SwapchainCreateInfo { + image_extent, + ..swapchain.create_info() + }) + .expect("failed to recreate swapchain"); swapchain = new_swapchain; framebuffers = diff --git a/examples/src/bin/runtime_array/main.rs b/examples/src/bin/runtime_array/main.rs index 7ff6be86..a4e8b572 100644 --- a/examples/src/bin/runtime_array/main.rs +++ b/examples/src/bin/runtime_array/main.rs @@ -47,7 +47,7 @@ use vulkano::{ sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo}, swapchain::{ acquire_next_image, AcquireError, Surface, Swapchain, SwapchainCreateInfo, - SwapchainCreationError, SwapchainPresentInfo, + SwapchainPresentInfo, }, sync::{self, FlushError, GpuFuture}, VulkanLibrary, @@ -455,22 +455,21 @@ fn main() { recreate_swapchain = true; } Event::RedrawEventsCleared => { - let dimensions = window.inner_size(); - if dimensions.width == 0 || dimensions.height == 0 { + let image_extent: [u32; 2] = window.inner_size().into(); + + if image_extent.contains(&0) { return; } previous_frame_end.as_mut().unwrap().cleanup_finished(); if recreate_swapchain { - let (new_swapchain, new_images) = match swapchain.recreate(SwapchainCreateInfo { - image_extent: dimensions.into(), - ..swapchain.create_info() - }) { - Ok(r) => r, - Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return, - Err(e) => panic!("failed to recreate swapchain: {e}"), - }; + let (new_swapchain, new_images) = swapchain + .recreate(SwapchainCreateInfo { + image_extent, + ..swapchain.create_info() + }) + .expect("failed to recreate swapchain"); swapchain = new_swapchain; framebuffers = diff --git a/examples/src/bin/simple-particles.rs b/examples/src/bin/simple-particles.rs index 864bee3f..c55cb525 100644 --- a/examples/src/bin/simple-particles.rs +++ b/examples/src/bin/simple-particles.rs @@ -524,8 +524,9 @@ fn main() { *control_flow = ControlFlow::Exit; } Event::RedrawEventsCleared => { - let dimensions = window.inner_size(); - if dimensions.width == 0 || dimensions.height == 0 { + let image_extent: [u32; 2] = window.inner_size().into(); + + if image_extent.contains(&0) { return; } diff --git a/examples/src/bin/teapot/main.rs b/examples/src/bin/teapot/main.rs index c10e8f09..6da8e1f5 100644 --- a/examples/src/bin/teapot/main.rs +++ b/examples/src/bin/teapot/main.rs @@ -49,7 +49,7 @@ use vulkano::{ shader::EntryPoint, swapchain::{ acquire_next_image, AcquireError, Surface, Swapchain, SwapchainCreateInfo, - SwapchainCreationError, SwapchainPresentInfo, + SwapchainPresentInfo, }, sync::{self, FlushError, GpuFuture}, VulkanLibrary, @@ -276,23 +276,21 @@ fn main() { recreate_swapchain = true; } Event::RedrawEventsCleared => { - let dimensions = window.inner_size(); - if dimensions.width == 0 || dimensions.height == 0 { + let image_extent: [u32; 2] = window.inner_size().into(); + + if image_extent.contains(&0) { return; } previous_frame_end.as_mut().unwrap().cleanup_finished(); if recreate_swapchain { - let (new_swapchain, new_images) = - match swapchain.recreate(SwapchainCreateInfo { - image_extent: dimensions.into(), + let (new_swapchain, new_images) = swapchain + .recreate(SwapchainCreateInfo { + image_extent, ..swapchain.create_info() - }) { - Ok(r) => r, - Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return, - Err(e) => panic!("failed to recreate swapchain: {e}"), - }; + }) + .expect("failed to recreate swapchain"); swapchain = new_swapchain; let (new_pipeline, new_framebuffers) = window_size_dependent_setup( diff --git a/examples/src/bin/tessellation.rs b/examples/src/bin/tessellation.rs index 1abb321f..b1338d32 100644 --- a/examples/src/bin/tessellation.rs +++ b/examples/src/bin/tessellation.rs @@ -52,7 +52,7 @@ use vulkano::{ render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, swapchain::{ acquire_next_image, AcquireError, Surface, Swapchain, SwapchainCreateInfo, - SwapchainCreationError, SwapchainPresentInfo, + SwapchainPresentInfo, }, sync::{self, FlushError, GpuFuture}, VulkanLibrary, @@ -424,22 +424,21 @@ fn main() { recreate_swapchain = true; } Event::RedrawEventsCleared => { - let dimensions = window.inner_size(); - if dimensions.width == 0 || dimensions.height == 0 { + let image_extent: [u32; 2] = window.inner_size().into(); + + if image_extent.contains(&0) { return; } previous_frame_end.as_mut().unwrap().cleanup_finished(); if recreate_swapchain { - let (new_swapchain, new_images) = match swapchain.recreate(SwapchainCreateInfo { - image_extent: dimensions.into(), - ..swapchain.create_info() - }) { - Ok(r) => r, - Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return, - Err(e) => panic!("failed to recreate swapchain: {e}"), - }; + let (new_swapchain, new_images) = swapchain + .recreate(SwapchainCreateInfo { + image_extent, + ..swapchain.create_info() + }) + .expect("failed to recreate swapchain"); swapchain = new_swapchain; framebuffers = diff --git a/examples/src/bin/texture_array/main.rs b/examples/src/bin/texture_array/main.rs index 338a378c..70d64638 100644 --- a/examples/src/bin/texture_array/main.rs +++ b/examples/src/bin/texture_array/main.rs @@ -46,7 +46,7 @@ use vulkano::{ sampler::{Sampler, SamplerCreateInfo}, swapchain::{ acquire_next_image, AcquireError, Surface, Swapchain, SwapchainCreateInfo, - SwapchainCreationError, SwapchainPresentInfo, + SwapchainPresentInfo, }, sync::{self, FlushError, GpuFuture}, VulkanLibrary, @@ -352,22 +352,21 @@ fn main() { recreate_swapchain = true; } Event::RedrawEventsCleared => { - let dimensions = window.inner_size(); - if dimensions.width == 0 || dimensions.height == 0 { + let image_extent: [u32; 2] = window.inner_size().into(); + + if image_extent.contains(&0) { return; } previous_frame_end.as_mut().unwrap().cleanup_finished(); if recreate_swapchain { - let (new_swapchain, new_images) = match swapchain.recreate(SwapchainCreateInfo { - image_extent: dimensions.into(), - ..swapchain.create_info() - }) { - Ok(r) => r, - Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return, - Err(e) => panic!("failed to recreate swapchain: {e}"), - }; + let (new_swapchain, new_images) = swapchain + .recreate(SwapchainCreateInfo { + image_extent, + ..swapchain.create_info() + }) + .expect("failed to recreate swapchain"); swapchain = new_swapchain; framebuffers = diff --git a/examples/src/bin/triangle-v1_3.rs b/examples/src/bin/triangle-v1_3.rs index e2d4dd4c..3eec9407 100644 --- a/examples/src/bin/triangle-v1_3.rs +++ b/examples/src/bin/triangle-v1_3.rs @@ -52,7 +52,7 @@ use vulkano::{ render_pass::{AttachmentLoadOp, AttachmentStoreOp}, swapchain::{ acquire_next_image, AcquireError, Surface, Swapchain, SwapchainCreateInfo, - SwapchainCreationError, SwapchainPresentInfo, + SwapchainPresentInfo, }, sync::{self, FlushError, GpuFuture}, Version, VulkanLibrary, @@ -527,8 +527,9 @@ fn main() { Event::RedrawEventsCleared => { // Do not draw the frame when the screen dimensions are zero. On Windows, this can // occur when minimizing the application. - let dimensions = window.inner_size(); - if dimensions.width == 0 || dimensions.height == 0 { + let image_extent: [u32; 2] = window.inner_size().into(); + + if image_extent.contains(&0) { return; } @@ -542,18 +543,12 @@ fn main() { // window size. In this example that includes the swapchain, the framebuffers and // the dynamic state viewport. if recreate_swapchain { - let (new_swapchain, new_images) = - match swapchain.recreate(SwapchainCreateInfo { - image_extent: dimensions.into(), + let (new_swapchain, new_images) = swapchain + .recreate(SwapchainCreateInfo { + image_extent, ..swapchain.create_info() - }) { - Ok(r) => r, - // This error tends to happen when the user is manually resizing the - // window. Simply restarting the loop is the easiest way to fix this - // issue. - Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return, - Err(e) => panic!("failed to recreate swapchain: {e}"), - }; + }) + .expect("failed to recreate swapchain"); swapchain = new_swapchain; diff --git a/examples/src/bin/triangle.rs b/examples/src/bin/triangle.rs index 34d44366..19c83a2d 100644 --- a/examples/src/bin/triangle.rs +++ b/examples/src/bin/triangle.rs @@ -46,7 +46,7 @@ use vulkano::{ render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, swapchain::{ acquire_next_image, AcquireError, Surface, Swapchain, SwapchainCreateInfo, - SwapchainCreationError, SwapchainPresentInfo, + SwapchainPresentInfo, }, sync::{self, FlushError, GpuFuture}, VulkanLibrary, @@ -522,8 +522,9 @@ fn main() { Event::RedrawEventsCleared => { // Do not draw the frame when the screen dimensions are zero. On Windows, this can // occur when minimizing the application. - let dimensions = window.inner_size(); - if dimensions.width == 0 || dimensions.height == 0 { + let image_extent: [u32; 2] = window.inner_size().into(); + + if image_extent.contains(&0) { return; } @@ -539,18 +540,12 @@ fn main() { if recreate_swapchain { // Use the new dimensions of the window. - let (new_swapchain, new_images) = - match swapchain.recreate(SwapchainCreateInfo { - image_extent: dimensions.into(), + let (new_swapchain, new_images) = swapchain + .recreate(SwapchainCreateInfo { + image_extent, ..swapchain.create_info() - }) { - Ok(r) => r, - // This error tends to happen when the user is manually resizing the - // window. Simply restarting the loop is the easiest way to fix this - // issue. - Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return, - Err(e) => panic!("failed to recreate swapchain: {e}"), - }; + }) + .expect("failed to recreate swapchain"); swapchain = new_swapchain; diff --git a/vulkano-util/src/renderer.rs b/vulkano-util/src/renderer.rs index a32c1cb2..99527433 100644 --- a/vulkano-util/src/renderer.rs +++ b/vulkano-util/src/renderer.rs @@ -18,8 +18,7 @@ use vulkano::{ }, memory::allocator::StandardMemoryAllocator, swapchain::{ - self, AcquireError, Surface, Swapchain, SwapchainCreateInfo, SwapchainCreationError, - SwapchainPresentInfo, + self, AcquireError, Surface, Swapchain, SwapchainCreateInfo, SwapchainPresentInfo, }, sync::{self, FlushError, GpuFuture}, }; @@ -337,17 +336,21 @@ impl VulkanoWindowRenderer { /// Recreates swapchain images and image views which follow the window size. fn recreate_swapchain_and_views(&mut self) { - let dimensions: [u32; 2] = self.window().inner_size().into(); - let (new_swapchain, new_images) = match self.swapchain.recreate(SwapchainCreateInfo { - image_extent: dimensions, - // Use present mode from current state - present_mode: self.present_mode, - ..self.swapchain.create_info() - }) { - Ok(r) => r, - Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return, - Err(e) => panic!("failed to recreate swapchain: {e}"), - }; + let image_extent: [u32; 2] = self.window().inner_size().into(); + + if image_extent.contains(&0) { + return; + } + + let (new_swapchain, new_images) = self + .swapchain + .recreate(SwapchainCreateInfo { + image_extent, + // Use present mode from current state + present_mode: self.present_mode, + ..self.swapchain.create_info() + }) + .expect("failed to recreate swapchain"); self.swapchain = new_swapchain; let new_images = new_images diff --git a/vulkano-win/src/lib.rs b/vulkano-win/src/lib.rs index e23c27a9..c2dba833 100644 --- a/vulkano-win/src/lib.rs +++ b/vulkano-win/src/lib.rs @@ -14,7 +14,7 @@ `Surface::from_window` instead" )] #![doc(html_logo_url = "https://raw.githubusercontent.com/vulkano-rs/vulkano/master/logo.png")] -#![allow(clippy::missing_safety_doc)] +#![allow(clippy::missing_safety_doc, clippy::result_large_err)] #![warn(rust_2018_idioms, rust_2021_compatibility)] #[cfg(feature = "raw-window-handle")] diff --git a/vulkano-win/src/raw_window_handle.rs b/vulkano-win/src/raw_window_handle.rs index 1d208a78..396b49ee 100644 --- a/vulkano-win/src/raw_window_handle.rs +++ b/vulkano-win/src/raw_window_handle.rs @@ -6,17 +6,14 @@ use raw_window_handle::{ HasRawDisplayHandle, HasRawWindowHandle, RawDisplayHandle, RawWindowHandle, }; use std::{any::Any, sync::Arc}; -use vulkano::{ - instance::Instance, - swapchain::{Surface, SurfaceCreationError}, -}; +use vulkano::{instance::Instance, swapchain::Surface, VulkanError}; /// Creates a Vulkan surface from a generic window which implements `HasRawWindowHandle` and thus /// can reveal the OS-dependent handle. pub fn create_surface_from_handle( window: Arc, instance: Arc, -) -> Result, SurfaceCreationError> { +) -> Result, VulkanError> { unsafe { match window.raw_window_handle() { RawWindowHandle::AndroidNdk(h) => { @@ -85,7 +82,7 @@ pub fn create_surface_from_handle( pub unsafe fn create_surface_from_handle_ref( window: &(impl HasRawWindowHandle + HasRawDisplayHandle), instance: Arc, -) -> Result, SurfaceCreationError> { +) -> Result, VulkanError> { unsafe { match window.raw_window_handle() { RawWindowHandle::AndroidNdk(h) => { diff --git a/vulkano-win/src/winit.rs b/vulkano-win/src/winit.rs index d3770635..20ffb45f 100644 --- a/vulkano-win/src/winit.rs +++ b/vulkano-win/src/winit.rs @@ -5,8 +5,8 @@ use std::{ }; use vulkano::{ instance::{Instance, InstanceExtensions}, - swapchain::{Surface, SurfaceCreationError}, - VulkanLibrary, + swapchain::Surface, + VulkanError, VulkanLibrary, }; use winit::{ error::OsError as WindowCreationError, @@ -37,7 +37,7 @@ pub fn required_extensions(library: &VulkanLibrary) -> InstanceExtensions { pub fn create_surface_from_winit( window: Arc, instance: Arc, -) -> Result, SurfaceCreationError> { +) -> Result, VulkanError> { unsafe { winit_to_surface(instance, window) } } @@ -65,7 +65,8 @@ impl VkSurfaceBuild for WindowBuilder { #[derive(Debug)] pub enum CreationError { /// Error when creating the surface. - SurfaceCreationError(SurfaceCreationError), + VulkanError(VulkanError), + /// Error when creating the window. WindowCreationError(WindowCreationError), } @@ -73,7 +74,7 @@ pub enum CreationError { impl Error for CreationError { fn source(&self) -> Option<&(dyn Error + 'static)> { match self { - CreationError::SurfaceCreationError(err) => Some(err), + CreationError::VulkanError(err) => Some(err), CreationError::WindowCreationError(err) => Some(err), } } @@ -85,16 +86,16 @@ impl Display for CreationError { f, "{}", match self { - CreationError::SurfaceCreationError(_) => "error while creating the surface", + CreationError::VulkanError(_) => "error while creating the surface", CreationError::WindowCreationError(_) => "error while creating the window", } ) } } -impl From for CreationError { - fn from(err: SurfaceCreationError) -> CreationError { - CreationError::SurfaceCreationError(err) +impl From for CreationError { + fn from(err: VulkanError) -> CreationError { + CreationError::VulkanError(err) } } @@ -108,7 +109,7 @@ impl From for CreationError { unsafe fn winit_to_surface( instance: Arc, window: Arc, -) -> Result, SurfaceCreationError> { +) -> Result, VulkanError> { use raw_window_handle::HasRawWindowHandle; use raw_window_handle::RawWindowHandle::AndroidNdk; if let AndroidNdk(handle) = window.raw_window_handle() { @@ -127,7 +128,7 @@ unsafe fn winit_to_surface( unsafe fn winit_to_surface( instance: Arc, window: Arc, -) -> Result, SurfaceCreationError> { +) -> Result, VulkanError> { use winit::platform::{wayland::WindowExtWayland, x11::WindowExtX11}; match (window.wayland_display(), window.wayland_surface()) { @@ -193,7 +194,7 @@ pub(crate) unsafe fn get_metal_layer_macos(view: *mut std::ffi::c_void) -> *mut unsafe fn winit_to_surface( instance: Arc, window: Arc, -) -> Result, SurfaceCreationError> { +) -> Result, VulkanError> { use winit::platform::macos::WindowExtMacOS; let layer = get_metal_layer_macos(window.ns_view()); Surface::from_mac_os(instance, layer as *const (), Some(window)) @@ -224,7 +225,7 @@ pub(crate) unsafe fn get_metal_layer_ios(view: *mut std::ffi::c_void) -> IOSMeta unsafe fn winit_to_surface( instance: Arc, window: Arc, -) -> Result, SurfaceCreationError> { +) -> Result, VulkanError> { use winit::platform::ios::WindowExtIOS; let layer = get_metal_layer_ios(window.ui_view()); Surface::from_ios(instance, layer, Some(window)) @@ -234,7 +235,7 @@ unsafe fn winit_to_surface( unsafe fn winit_to_surface( instance: Arc, window: Arc, -) -> Result, SurfaceCreationError> { +) -> Result, VulkanError> { use winit::platform::windows::WindowExtWindows; Surface::from_win32( diff --git a/vulkano/src/device/physical.rs b/vulkano/src/device/physical.rs index 9853cbc1..992ae7a0 100644 --- a/vulkano/src/device/physical.rs +++ b/vulkano/src/device/physical.rs @@ -1387,11 +1387,31 @@ impl PhysicalDevice { .map_err(|err| err.add_context("surface_info"))?; let &SurfaceInfo { + present_mode, full_screen_exclusive, win32_monitor, _ne: _, } = surface_info; + if let Some(present_mode) = present_mode { + let mut present_modes = unsafe { + self.surface_present_modes_unchecked(surface) + .map_err(|_err| ValidationError { + context: "PhysicalDevice::surface_present_modes".into(), + problem: "returned an error".into(), + ..Default::default() + })? + }; + + if !present_modes.any(|mode| mode == present_mode) { + return Err(ValidationError { + problem: "`surface_info.present_mode` is not supported for `surface`".into(), + vuids: &["VUID-VkSurfacePresentModeEXT-presentMode-07780"], + ..Default::default() + }); + } + } + match ( surface.api() == SurfaceApi::Win32 && full_screen_exclusive == FullScreenExclusive::ApplicationControlled, @@ -1433,6 +1453,7 @@ impl PhysicalDevice { /* Input */ let SurfaceInfo { + present_mode, full_screen_exclusive, win32_monitor, _ne: _, @@ -1442,10 +1463,21 @@ impl PhysicalDevice { surface: surface.handle(), ..Default::default() }; + let mut present_mode_vk = None; let mut full_screen_exclusive_info_vk = None; let mut full_screen_exclusive_win32_info_vk = None; - if self.supported_extensions().ext_full_screen_exclusive && win32_monitor.is_some() { + if let Some(present_mode) = present_mode { + let next = present_mode_vk.insert(ash::vk::SurfacePresentModeEXT { + present_mode: present_mode.into(), + ..Default::default() + }); + + next.p_next = info_vk.p_next as *mut _; + info_vk.p_next = next as *const _ as *const _; + } + + if full_screen_exclusive != FullScreenExclusive::Default { let next = full_screen_exclusive_info_vk.insert(ash::vk::SurfaceFullScreenExclusiveInfoEXT { full_screen_exclusive: full_screen_exclusive.into(), @@ -1472,7 +1504,11 @@ impl PhysicalDevice { let mut capabilities_vk = ash::vk::SurfaceCapabilities2KHR::default(); let mut capabilities_full_screen_exclusive_vk = None; - let mut protected_capabilities_vk = None; + let mut capabilities_present_modes_vk = + [ash::vk::PresentModeKHR::default(); PresentMode::COUNT]; + let mut capabilities_present_mode_compatibility_vk = None; + let mut capabilities_present_scaling_vk = None; + let mut capabilities_protected_vk = None; if full_screen_exclusive_info_vk.is_some() { let next = capabilities_full_screen_exclusive_vk @@ -1482,12 +1518,35 @@ impl PhysicalDevice { capabilities_vk.p_next = next as *mut _ as *mut _; } + if present_mode.is_some() { + { + let next = capabilities_present_mode_compatibility_vk.insert( + ash::vk::SurfacePresentModeCompatibilityEXT { + present_mode_count: capabilities_present_modes_vk.len() as u32, + p_present_modes: capabilities_present_modes_vk.as_mut_ptr(), + ..Default::default() + }, + ); + + next.p_next = capabilities_vk.p_next as *mut _; + capabilities_vk.p_next = next as *mut _ as *mut _; + } + + { + let next = capabilities_present_scaling_vk + .insert(ash::vk::SurfacePresentScalingCapabilitiesEXT::default()); + + next.p_next = capabilities_vk.p_next as *mut _; + capabilities_vk.p_next = next as *mut _ as *mut _; + } + } + if self .instance .enabled_extensions() .khr_surface_protected_capabilities { - let next = protected_capabilities_vk + let next = capabilities_protected_vk .insert(ash::vk::SurfaceProtectedCapabilitiesKHR::default()); next.p_next = capabilities_vk.p_next as *mut _; @@ -1521,22 +1580,19 @@ impl PhysicalDevice { Ok(SurfaceCapabilities { min_image_count: capabilities_vk.surface_capabilities.min_image_count, - max_image_count: if capabilities_vk.surface_capabilities.max_image_count == 0 { - None - } else { - Some(capabilities_vk.surface_capabilities.max_image_count) - }, - current_extent: if capabilities_vk.surface_capabilities.current_extent.width - == 0xffffffff - && capabilities_vk.surface_capabilities.current_extent.height == 0xffffffff - { - None - } else { - Some([ - capabilities_vk.surface_capabilities.current_extent.width, - capabilities_vk.surface_capabilities.current_extent.height, - ]) - }, + max_image_count: (capabilities_vk.surface_capabilities.max_image_count != 0) + .then_some(capabilities_vk.surface_capabilities.max_image_count), + current_extent: (!matches!( + capabilities_vk.surface_capabilities.current_extent, + ash::vk::Extent2D { + width: u32::MAX, + height: u32::MAX + } + )) + .then_some([ + capabilities_vk.surface_capabilities.current_extent.width, + capabilities_vk.surface_capabilities.current_extent.height, + ]), min_image_extent: [ capabilities_vk.surface_capabilities.min_image_extent.width, capabilities_vk.surface_capabilities.min_image_extent.height, @@ -1561,14 +1617,75 @@ impl PhysicalDevice { .surface_capabilities .supported_composite_alpha .into(), - supported_usage_flags: { - let usage = - ImageUsage::from(capabilities_vk.surface_capabilities.supported_usage_flags); - debug_assert!(usage.intersects(ImageUsage::COLOR_ATTACHMENT)); // specs say that this must be true - usage - }, + supported_usage_flags: ImageUsage::from( + capabilities_vk.surface_capabilities.supported_usage_flags, + ), - supports_protected: protected_capabilities_vk + compatible_present_modes: capabilities_present_mode_compatibility_vk.map_or_else( + Default::default, + |capabilities_present_mode_compatibility_vk| { + capabilities_present_modes_vk + [..capabilities_present_mode_compatibility_vk.present_mode_count as usize] + .iter() + .copied() + .map(PresentMode::try_from) + .filter_map(Result::ok) + .collect() + }, + ), + + supported_present_scaling: capabilities_present_scaling_vk + .as_ref() + .map_or_else(Default::default, |c| c.supported_present_scaling.into()), + supported_present_gravity: capabilities_present_scaling_vk.as_ref().map_or_else( + Default::default, + |c| { + [ + c.supported_present_gravity_x.into(), + c.supported_present_gravity_y.into(), + ] + }, + ), + min_scaled_image_extent: capabilities_present_scaling_vk.as_ref().map_or( + Some([ + capabilities_vk.surface_capabilities.min_image_extent.width, + capabilities_vk.surface_capabilities.min_image_extent.height, + ]), + |c| { + (!matches!( + c.min_scaled_image_extent, + ash::vk::Extent2D { + width: u32::MAX, + height: u32::MAX, + } + )) + .then_some([ + c.min_scaled_image_extent.width, + c.min_scaled_image_extent.height, + ]) + }, + ), + max_scaled_image_extent: capabilities_present_scaling_vk.as_ref().map_or( + Some([ + capabilities_vk.surface_capabilities.max_image_extent.width, + capabilities_vk.surface_capabilities.max_image_extent.height, + ]), + |c| { + (!matches!( + c.max_scaled_image_extent, + ash::vk::Extent2D { + width: u32::MAX, + height: u32::MAX, + } + )) + .then_some([ + c.max_scaled_image_extent.width, + c.max_scaled_image_extent.height, + ]) + }, + ), + + supports_protected: capabilities_protected_vk .map_or(false, |c| c.supports_protected != 0), full_screen_exclusive_supported: capabilities_full_screen_exclusive_vk @@ -1630,12 +1747,36 @@ impl PhysicalDevice { }); } + surface_info + .validate(self) + .map_err(|err| err.add_context("surface_info"))?; + let &SurfaceInfo { + present_mode, full_screen_exclusive, win32_monitor, _ne: _, } = surface_info; + if let Some(present_mode) = present_mode { + let mut present_modes = unsafe { + self.surface_present_modes_unchecked(surface) + .map_err(|_err| ValidationError { + context: "PhysicalDevice::surface_present_modes".into(), + problem: "returned an error".into(), + ..Default::default() + })? + }; + + if !present_modes.any(|mode| mode == present_mode) { + return Err(ValidationError { + problem: "`surface_info.present_mode` is not supported for `surface`".into(), + vuids: &["VUID-VkSurfacePresentModeEXT-presentMode-07780"], + ..Default::default() + }); + } + } + if !self .instance .enabled_extensions() @@ -1706,46 +1847,52 @@ impl PhysicalDevice { (self.handle, surface_info), |(_, surface_info)| { let &SurfaceInfo { + present_mode, full_screen_exclusive, win32_monitor, _ne: _, } = surface_info; - let mut surface_full_screen_exclusive_info = (full_screen_exclusive - != FullScreenExclusive::Default) - .then(|| ash::vk::SurfaceFullScreenExclusiveInfoEXT { - full_screen_exclusive: full_screen_exclusive.into(), - ..Default::default() - }); - - let mut surface_full_screen_exclusive_win32_info = - win32_monitor.map(|win32_monitor| { - ash::vk::SurfaceFullScreenExclusiveWin32InfoEXT { - hmonitor: win32_monitor.0, - ..Default::default() - } - }); - - let mut surface_info2 = ash::vk::PhysicalDeviceSurfaceInfo2KHR { + let mut info_vk = ash::vk::PhysicalDeviceSurfaceInfo2KHR { surface: surface.handle(), ..Default::default() }; + let mut present_mode_vk = None; + let mut full_screen_exclusive_info_vk = None; + let mut full_screen_exclusive_win32_info_vk = None; - if let Some(surface_full_screen_exclusive_info) = - surface_full_screen_exclusive_info.as_mut() - { - surface_full_screen_exclusive_info.p_next = surface_info2.p_next as *mut _; - surface_info2.p_next = - surface_full_screen_exclusive_info as *const _ as *const _; + if let Some(present_mode) = present_mode { + let next = present_mode_vk.insert(ash::vk::SurfacePresentModeEXT { + present_mode: present_mode.into(), + ..Default::default() + }); + + next.p_next = info_vk.p_next as *mut _; + info_vk.p_next = next as *const _ as *const _; } - if let Some(surface_full_screen_exclusive_win32_info) = - surface_full_screen_exclusive_win32_info.as_mut() - { - surface_full_screen_exclusive_win32_info.p_next = - surface_info2.p_next as *mut _; - surface_info2.p_next = - surface_full_screen_exclusive_win32_info as *const _ as *const _; + if full_screen_exclusive != FullScreenExclusive::Default { + let next = full_screen_exclusive_info_vk.insert( + ash::vk::SurfaceFullScreenExclusiveInfoEXT { + full_screen_exclusive: full_screen_exclusive.into(), + ..Default::default() + }, + ); + + next.p_next = info_vk.p_next as *mut _; + info_vk.p_next = next as *const _ as *const _; + } + + if let Some(win32_monitor) = win32_monitor { + let next = full_screen_exclusive_win32_info_vk.insert( + ash::vk::SurfaceFullScreenExclusiveWin32InfoEXT { + hmonitor: win32_monitor.0, + ..Default::default() + }, + ); + + next.p_next = info_vk.p_next as *mut _; + info_vk.p_next = next as *const _ as *const _; } let fns = self.instance.fns(); @@ -1755,40 +1902,40 @@ impl PhysicalDevice { .enabled_extensions() .khr_get_surface_capabilities2 { - let surface_format2s = loop { + let surface_format2s_vk = loop { let mut count = 0; (fns.khr_get_surface_capabilities2 .get_physical_device_surface_formats2_khr)( self.handle(), - &surface_info2, + &info_vk, &mut count, ptr::null_mut(), ) .result() .map_err(RuntimeError::from)?; - let mut surface_format2s = + let mut surface_format2s_vk = vec![ash::vk::SurfaceFormat2KHR::default(); count as usize]; let result = (fns .khr_get_surface_capabilities2 .get_physical_device_surface_formats2_khr)( self.handle(), - &surface_info2, + &info_vk, &mut count, - surface_format2s.as_mut_ptr(), + surface_format2s_vk.as_mut_ptr(), ); match result { ash::vk::Result::SUCCESS => { - surface_format2s.set_len(count as usize); - break surface_format2s; + surface_format2s_vk.set_len(count as usize); + break surface_format2s_vk; } ash::vk::Result::INCOMPLETE => (), err => return Err(RuntimeError::from(err)), } }; - Ok(surface_format2s + Ok(surface_format2s_vk .into_iter() .filter_map(|surface_format2| { (surface_format2.surface_format.format.try_into().ok()) diff --git a/vulkano/src/device/queue.rs b/vulkano/src/device/queue.rs index c879c704..1d0d7c99 100644 --- a/vulkano/src/device/queue.rs +++ b/vulkano/src/device/queue.rs @@ -529,8 +529,8 @@ impl<'a> QueueGuard<'a> { states: &mut States<'_>, ) -> Result>, RuntimeError> { let PresentInfo { - ref wait_semaphores, - ref swapchain_infos, + wait_semaphores, + swapchain_infos, _ne: _, } = present_info; @@ -542,11 +542,13 @@ impl<'a> QueueGuard<'a> { let mut swapchains_vk: SmallVec<[_; 4]> = SmallVec::with_capacity(swapchain_infos.len()); let mut image_indices_vk: SmallVec<[_; 4]> = SmallVec::with_capacity(swapchain_infos.len()); let mut present_ids_vk: SmallVec<[_; 4]> = SmallVec::with_capacity(swapchain_infos.len()); + let mut present_modes_vk: SmallVec<[_; 4]> = SmallVec::with_capacity(swapchain_infos.len()); let mut present_regions_vk: SmallVec<[_; 4]> = SmallVec::with_capacity(swapchain_infos.len()); let mut rectangles_vk: SmallVec<[_; 4]> = SmallVec::with_capacity(swapchain_infos.len()); let mut has_present_ids = false; + let mut has_present_modes = false; let mut has_present_regions = false; for swapchain_info in swapchain_infos { @@ -554,6 +556,7 @@ impl<'a> QueueGuard<'a> { ref swapchain, image_index, present_id, + present_mode, ref present_regions, _ne: _, } = swapchain_info; @@ -561,6 +564,7 @@ impl<'a> QueueGuard<'a> { swapchains_vk.push(swapchain.handle()); image_indices_vk.push(image_index); present_ids_vk.push(present_id.map_or(0, u64::from)); + present_modes_vk.push(present_mode.map_or_else(Default::default, Into::into)); present_regions_vk.push(ash::vk::PresentRegionKHR::default()); rectangles_vk.push( present_regions @@ -573,6 +577,10 @@ impl<'a> QueueGuard<'a> { has_present_ids = true; } + if present_mode.is_some() { + has_present_modes = true; + } + if !present_regions.is_empty() { has_present_regions = true; } @@ -589,6 +597,7 @@ impl<'a> QueueGuard<'a> { ..Default::default() }; let mut present_id_info_vk = None; + let mut present_mode_info_vk = None; let mut present_region_info_vk = None; if has_present_ids { @@ -602,6 +611,17 @@ impl<'a> QueueGuard<'a> { info_vk.p_next = next as *const _ as *const _; } + if has_present_modes { + let next = present_mode_info_vk.insert(ash::vk::SwapchainPresentModeInfoEXT { + swapchain_count: present_modes_vk.len() as u32, + p_present_modes: present_modes_vk.as_ptr(), + ..Default::default() + }); + + next.p_next = info_vk.p_next as _; + info_vk.p_next = next as *const _ as *const _; + } + if has_present_regions { for (present_regions_vk, rectangles_vk) in (present_regions_vk.iter_mut()).zip(rectangles_vk.iter()) diff --git a/vulkano/src/image/swapchain.rs b/vulkano/src/image/swapchain.rs index be11b827..784dbaff 100644 --- a/vulkano/src/image/swapchain.rs +++ b/vulkano/src/image/swapchain.rs @@ -15,7 +15,6 @@ use super::{ use crate::{ device::{Device, DeviceOwned}, swapchain::Swapchain, - OomError, }; use std::{ hash::{Hash, Hasher}, @@ -45,10 +44,10 @@ impl SwapchainImage { handle: ash::vk::Image, swapchain: Arc, image_index: u32, - ) -> Result, OomError> { - Ok(Arc::new(SwapchainImage { + ) -> Arc { + Arc::new(SwapchainImage { inner: Arc::new(Image::from_swapchain(handle, swapchain, image_index)), - })) + }) } /// Returns the swapchain this image belongs to. diff --git a/vulkano/src/macros.rs b/vulkano/src/macros.rs index dccb9ad9..2b37283d 100644 --- a/vulkano/src/macros.rs +++ b/vulkano/src/macros.rs @@ -591,11 +591,16 @@ macro_rules! vulkan_enum { )+ } - $( - impl $ty { + impl $ty { + #[allow(dead_code)] + pub(crate) const COUNT: usize = [ + $(ash::vk::$ty_ffi::$flag_name_ffi.as_raw()),+ + ].len(); + + $( $($impls)* - } - )? + )? + } impl From<$ty> for ash::vk::$ty_ffi { #[inline] @@ -653,6 +658,11 @@ macro_rules! vulkan_enum { } impl $ty { + #[allow(dead_code)] + pub(crate) const COUNT: usize = [ + $(ash::vk::$ty_ffi::$flag_name_ffi.as_raw()),+ + ].len(); + #[allow(dead_code)] #[inline] pub(crate) fn validate_device( diff --git a/vulkano/src/pipeline/graphics/viewport.rs b/vulkano/src/pipeline/graphics/viewport.rs index 304003ef..0204e0ce 100644 --- a/vulkano/src/pipeline/graphics/viewport.rs +++ b/vulkano/src/pipeline/graphics/viewport.rs @@ -521,13 +521,13 @@ impl From<&Viewport> for ash::vk::Viewport { } } -/// State of a single scissor box. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] +/// A two-dimensional subregion. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Scissor { - /// Coordinates in pixels of the top-left hand corner of the box. + /// Coordinates of the top-left hand corner of the box. pub offset: [u32; 2], - /// Dimensions in pixels of the box. + /// Dimensions of the box. pub extent: [u32; 2], } @@ -565,3 +565,13 @@ impl From<&Scissor> for ash::vk::Rect2D { } } } + +impl From for Scissor { + #[inline] + fn from(val: ash::vk::Rect2D) -> Self { + Scissor { + offset: [val.offset.x as u32, val.offset.y as u32], + extent: [val.extent.width, val.extent.height], + } + } +} diff --git a/vulkano/src/swapchain/acquire_present.rs b/vulkano/src/swapchain/acquire_present.rs new file mode 100644 index 00000000..5e0c71e2 --- /dev/null +++ b/vulkano/src/swapchain/acquire_present.rs @@ -0,0 +1,1025 @@ +// Copyright (c) 2016 The vulkano developers +// Licensed under the Apache License, Version 2.0 +// or the MIT +// license , +// at your option. All files in the project carrying such +// notice may not be copied, modified, or distributed except +// according to those terms. + +use super::{PresentMode, Swapchain}; +use crate::{ + buffer::Buffer, + device::{Device, DeviceOwned, Queue}, + image::{sys::Image, ImageLayout}, + sync::{ + fence::{Fence, FenceError}, + future::{AccessCheckError, AccessError, FlushError, GpuFuture, SubmitAnyBuilder}, + semaphore::{Semaphore, SemaphoreError}, + }, + DeviceSize, OomError, RequirementNotMet, Requires, RequiresAllOf, RequiresOneOf, RuntimeError, + VulkanObject, +}; +use smallvec::smallvec; +use std::{ + error::Error, + fmt::{Debug, Display, Error as FmtError, Formatter}, + mem::MaybeUninit, + num::NonZeroU64, + ops::Range, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, + thread, + time::Duration, +}; + +/// Tries to take ownership of an image in order to draw on it. +/// +/// The function returns the index of the image in the array of images that was returned +/// when creating the swapchain, plus a future that represents the moment when the image will +/// become available from the GPU (which may not be *immediately*). +/// +/// If you try to draw on an image without acquiring it first, the execution will block. (TODO +/// behavior may change). +/// +/// The second field in the tuple in the Ok result is a bool represent if the acquisition was +/// suboptimal. In this case the acquired image is still usable, but the swapchain should be +/// recreated as the Surface's properties no longer match the swapchain. +pub fn acquire_next_image( + swapchain: Arc, + timeout: Option, +) -> Result<(u32, bool, SwapchainAcquireFuture), AcquireError> { + let semaphore = Arc::new(Semaphore::from_pool(swapchain.device.clone())?); + let fence = Fence::from_pool(swapchain.device.clone())?; + + let AcquiredImage { + image_index, + suboptimal, + } = { + // Check that this is not an old swapchain. From specs: + // > swapchain must not have been replaced by being passed as the + // > VkSwapchainCreateInfoKHR::oldSwapchain value to vkCreateSwapchainKHR + let retired = swapchain.is_retired.lock(); + if *retired { + return Err(AcquireError::OutOfDate); + } + + let acquire_result = + unsafe { acquire_next_image_raw(&swapchain, timeout, Some(&semaphore), Some(&fence)) }; + + if let &Err(AcquireError::FullScreenExclusiveModeLost) = &acquire_result { + swapchain + .full_screen_exclusive_held + .store(false, Ordering::SeqCst); + } + + acquire_result? + }; + + Ok(( + image_index, + suboptimal, + SwapchainAcquireFuture { + swapchain, + semaphore: Some(semaphore), + fence: Some(fence), + image_index, + finished: AtomicBool::new(false), + }, + )) +} + +/// Unsafe variant of `acquire_next_image`. +/// +/// # Safety +/// +/// - The semaphore and/or the fence must be kept alive until it is signaled. +/// - The swapchain must not have been replaced by being passed as the old swapchain when creating +/// a new one. +pub unsafe fn acquire_next_image_raw( + swapchain: &Swapchain, + timeout: Option, + semaphore: Option<&Semaphore>, + fence: Option<&Fence>, +) -> Result { + let fns = swapchain.device.fns(); + + let timeout_ns = if let Some(timeout) = timeout { + timeout + .as_secs() + .saturating_mul(1_000_000_000) + .saturating_add(timeout.subsec_nanos() as u64) + } else { + u64::MAX + }; + + let mut out = MaybeUninit::uninit(); + let result = (fns.khr_swapchain.acquire_next_image_khr)( + swapchain.device.handle(), + swapchain.handle, + timeout_ns, + semaphore + .map(|s| s.handle()) + .unwrap_or(ash::vk::Semaphore::null()), + fence.map(|f| f.handle()).unwrap_or(ash::vk::Fence::null()), + out.as_mut_ptr(), + ); + + let suboptimal = match result { + ash::vk::Result::SUCCESS => false, + ash::vk::Result::SUBOPTIMAL_KHR => true, + ash::vk::Result::NOT_READY => return Err(AcquireError::Timeout), + ash::vk::Result::TIMEOUT => return Err(AcquireError::Timeout), + err => return Err(RuntimeError::from(err).into()), + }; + + if let Some(semaphore) = semaphore { + let mut state = semaphore.state(); + state.swapchain_acquire(); + } + + if let Some(fence) = fence { + let mut state = fence.state(); + state.import_swapchain_acquire(); + } + + Ok(AcquiredImage { + image_index: out.assume_init(), + suboptimal, + }) +} + +pub struct AcquiredImage { + pub image_index: u32, + pub suboptimal: bool, +} + +/// Represents the moment when the GPU will have access to a swapchain image. +#[must_use] +pub struct SwapchainAcquireFuture { + swapchain: Arc, + image_index: u32, + // Semaphore that is signalled when the acquire is complete. Empty if the acquire has already + // happened. + semaphore: Option>, + // Fence that is signalled when the acquire is complete. Empty if the acquire has already + // happened. + fence: Option, + finished: AtomicBool, +} + +impl SwapchainAcquireFuture { + /// Returns the index of the image in the list of images returned when creating the swapchain. + pub fn image_index(&self) -> u32 { + self.image_index + } + + /// Returns the corresponding swapchain. + pub fn swapchain(&self) -> &Arc { + &self.swapchain + } + + /// Blocks the current thread until the swapchain image has been acquired, or timeout + /// + /// If timeout is `None`, will potentially block forever + /// + /// You still need to join with this future for present to work + pub fn wait(&self, timeout: Option) -> Result<(), FenceError> { + match &self.fence { + Some(fence) => fence.wait(timeout), + None => Ok(()), + } + } +} + +unsafe impl GpuFuture for SwapchainAcquireFuture { + fn cleanup_finished(&mut self) {} + + unsafe fn build_submission(&self) -> Result { + if let Some(ref semaphore) = self.semaphore { + let sem = smallvec![semaphore.clone()]; + Ok(SubmitAnyBuilder::SemaphoresWait(sem)) + } else { + Ok(SubmitAnyBuilder::Empty) + } + } + + fn flush(&self) -> Result<(), FlushError> { + Ok(()) + } + + unsafe fn signal_finished(&self) { + self.finished.store(true, Ordering::SeqCst); + } + + fn queue_change_allowed(&self) -> bool { + true + } + + fn queue(&self) -> Option> { + None + } + + fn check_buffer_access( + &self, + _buffer: &Buffer, + _range: Range, + _exclusive: bool, + _queue: &Queue, + ) -> Result<(), AccessCheckError> { + Err(AccessCheckError::Unknown) + } + + fn check_image_access( + &self, + image: &Image, + _range: Range, + _exclusive: bool, + expected_layout: ImageLayout, + _queue: &Queue, + ) -> Result<(), AccessCheckError> { + if self.swapchain.index_of_image(image) != Some(self.image_index) { + return Err(AccessCheckError::Unknown); + } + + if !self.swapchain.images[self.image_index as usize] + .layout_initialized + .load(Ordering::Relaxed) + && expected_layout != ImageLayout::Undefined + { + return Err(AccessCheckError::Denied(AccessError::ImageNotInitialized { + requested: expected_layout, + })); + } + + if expected_layout != ImageLayout::Undefined && expected_layout != ImageLayout::PresentSrc { + return Err(AccessCheckError::Denied( + AccessError::UnexpectedImageLayout { + allowed: ImageLayout::PresentSrc, + requested: expected_layout, + }, + )); + } + + Ok(()) + } + + #[inline] + fn check_swapchain_image_acquired( + &self, + swapchain: &Swapchain, + image_index: u32, + before: bool, + ) -> Result<(), AccessCheckError> { + if before { + Ok(()) + } else { + if swapchain == self.swapchain.as_ref() && image_index == self.image_index { + Ok(()) + } else { + Err(AccessCheckError::Unknown) + } + } + } +} + +impl Drop for SwapchainAcquireFuture { + fn drop(&mut self) { + if thread::panicking() { + return; + } + + if let Some(fence) = &self.fence { + fence.wait(None).unwrap(); // TODO: handle error? + self.semaphore = None; + } + + // TODO: if this future is destroyed without being presented, then eventually acquiring + // a new image will block forever ; difficulty: hard + } +} + +unsafe impl DeviceOwned for SwapchainAcquireFuture { + fn device(&self) -> &Arc { + &self.swapchain.device + } +} + +/// Error that can happen when calling `acquire_next_image`. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[repr(u32)] +pub enum AcquireError { + /// Not enough memory. + OomError(OomError), + + /// The connection to the device has been lost. + DeviceLost, + + /// The timeout of the function has been reached before an image was available. + Timeout, + + /// The surface is no longer accessible and must be recreated. + SurfaceLost, + + /// The swapchain has lost or doesn't have full-screen exclusivity possibly for + /// implementation-specific reasons outside of the application’s control. + FullScreenExclusiveModeLost, + + /// The surface has changed in a way that makes the swapchain unusable. You must query the + /// surface's new properties and recreate a new swapchain if you want to continue drawing. + OutOfDate, + + /// Error during fence creation. + FenceError(FenceError), + + /// Error during semaphore creation. + SemaphoreError(SemaphoreError), +} + +impl Error for AcquireError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + AcquireError::OomError(err) => Some(err), + _ => None, + } + } +} + +impl Display for AcquireError { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { + write!( + f, + "{}", + match self { + AcquireError::OomError(_) => "not enough memory", + AcquireError::DeviceLost => "the connection to the device has been lost", + AcquireError::Timeout => "no image is available for acquiring yet", + AcquireError::SurfaceLost => "the surface of this swapchain is no longer valid", + AcquireError::OutOfDate => "the swapchain needs to be recreated", + AcquireError::FullScreenExclusiveModeLost => { + "the swapchain no longer has full-screen exclusivity" + } + AcquireError::FenceError(_) => "error creating fence", + AcquireError::SemaphoreError(_) => "error creating semaphore", + } + ) + } +} + +impl From for AcquireError { + fn from(err: FenceError) -> Self { + AcquireError::FenceError(err) + } +} + +impl From for AcquireError { + fn from(err: SemaphoreError) -> Self { + AcquireError::SemaphoreError(err) + } +} + +impl From for AcquireError { + fn from(err: OomError) -> AcquireError { + AcquireError::OomError(err) + } +} + +impl From for AcquireError { + fn from(err: RuntimeError) -> AcquireError { + match err { + err @ RuntimeError::OutOfHostMemory => AcquireError::OomError(OomError::from(err)), + err @ RuntimeError::OutOfDeviceMemory => AcquireError::OomError(OomError::from(err)), + RuntimeError::DeviceLost => AcquireError::DeviceLost, + RuntimeError::SurfaceLost => AcquireError::SurfaceLost, + RuntimeError::OutOfDate => AcquireError::OutOfDate, + RuntimeError::FullScreenExclusiveModeLost => AcquireError::FullScreenExclusiveModeLost, + _ => panic!("unexpected error: {:?}", err), + } + } +} + +/// Presents an image on the screen. +/// +/// The actual behavior depends on the present mode that you passed when creating the swapchain. +pub fn present( + before: F, + queue: Arc, + swapchain_info: SwapchainPresentInfo, +) -> PresentFuture +where + F: GpuFuture, +{ + assert!(swapchain_info.image_index < swapchain_info.swapchain.image_count()); + + // TODO: restore this check with a dummy ImageAccess implementation + /*let swapchain_image = me.images.lock().unwrap().get(index).unwrap().0.upgrade().unwrap(); // TODO: return error instead + // Normally if `check_image_access` returns false we're supposed to call the `gpu_access` + // function on the image instead. But since we know that this method on `SwapchainImage` + // always returns false anyway (by design), we don't need to do it. + assert!(before.check_image_access(&swapchain_image, ImageLayout::PresentSrc, true, &queue).is_ok()); // TODO: return error instead*/ + + PresentFuture { + previous: before, + queue, + swapchain_info, + flushed: AtomicBool::new(false), + finished: AtomicBool::new(false), + } +} + +/// Parameters to execute present operations on a queue. +#[derive(Clone, Debug)] +pub struct PresentInfo { + /// The semaphores to wait for before beginning the execution of the present operations. + /// + /// The default value is empty. + pub wait_semaphores: Vec>, + + /// The present operations to perform. + /// + /// The default value is empty. + pub swapchain_infos: Vec, + + pub _ne: crate::NonExhaustive, +} + +impl Default for PresentInfo { + #[inline] + fn default() -> Self { + Self { + wait_semaphores: Vec::new(), + swapchain_infos: Vec::new(), + _ne: crate::NonExhaustive(()), + } + } +} + +/// Parameters for a single present operation on a swapchain. +#[derive(Clone, Debug)] +pub struct SwapchainPresentInfo { + /// The swapchain to present to. + /// + /// There is no default value. + pub swapchain: Arc, + + /// The index of the swapchain image to present to. + /// + /// The image must have been acquired first; this is the index that `acquire_next_image` + /// returns. + /// + /// There is no default value. + pub image_index: u32, + + /* TODO: enable + /// The fence to signal when the presentation has completed. + /// + /// If this is not `None`, then the + /// [`ext_swapchain_maintenance1`](crate::device::DeviceExtensions::ext_swapchain_maintenance1) + /// extension must be enabled on the device. + /// + /// The default value is `None`. + pub fence: Option>, + */ + /// An id used to identify this present operation. + /// + /// If `present_id` is `Some`, the [`present_id`](crate::device::Features::present_id) feature + /// must be enabled on the device. The id must be greater than any id previously used for + /// `swapchain`. If a swapchain is recreated, this resets. + /// + /// The default value is `None`. + pub present_id: Option, + + /// The new present mode to use for presenting. This mode will be used for the current + /// present, and any future presents where this value is `None`. + /// + /// If this is not `None`, then the provided present mode must be one of the present modes + /// specified with [`present_modes`](SwapchainCreateInfo::present_modes) when creating the + /// swapchain. + /// + /// The default value is `None`. + pub present_mode: Option, + + /// An optimization hint to the implementation, that only some parts of the swapchain image are + /// going to be updated by the present operation. + /// + /// If `present_regions` is not empty, then the + /// [`khr_incremental_present`](crate::device::DeviceExtensions::khr_incremental_present) + /// extension must be enabled on the device. The implementation will update the provided + /// regions of the swapchain image, and _may_ ignore the other areas. However, as this is just + /// a hint, the Vulkan implementation is free to ignore the regions altogether and update + /// everything. + /// + /// If `present_regions` is empty, that means that all of the swapchain image must be updated. + /// + /// The default value is empty. + pub present_regions: Vec, + + pub _ne: crate::NonExhaustive, +} + +impl SwapchainPresentInfo { + /// Returns a `SwapchainPresentInfo` with the specified `swapchain` and `image_index`. + #[inline] + pub fn swapchain_image_index(swapchain: Arc, image_index: u32) -> Self { + Self { + swapchain, + image_index, + present_id: None, + present_mode: None, + present_regions: Vec::new(), + _ne: crate::NonExhaustive(()), + } + } +} + +/// Represents a rectangular region on an image layer. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct RectangleLayer { + /// Coordinates in pixels of the top-left hand corner of the rectangle. + pub offset: [u32; 2], + + /// Dimensions in pixels of the rectangle. + pub extent: [u32; 2], + + /// The layer of the image. For images with only one layer, the value of layer must be 0. + pub layer: u32, +} + +impl RectangleLayer { + /// Returns true if this rectangle layer is compatible with swapchain. + #[inline] + pub fn is_compatible_with(&self, swapchain: &Swapchain) -> bool { + self.offset[0] + self.extent[0] <= swapchain.image_extent()[0] + && self.offset[1] + self.extent[1] <= swapchain.image_extent()[1] + && self.layer < swapchain.image_array_layers() + } +} + +impl From<&RectangleLayer> for ash::vk::RectLayerKHR { + #[inline] + fn from(val: &RectangleLayer) -> Self { + ash::vk::RectLayerKHR { + offset: ash::vk::Offset2D { + x: val.offset[0] as i32, + y: val.offset[1] as i32, + }, + extent: ash::vk::Extent2D { + width: val.extent[0], + height: val.extent[1], + }, + layer: val.layer, + } + } +} + +/// Represents a swapchain image being presented on the screen. +#[must_use = "Dropping this object will immediately block the thread until the GPU has finished processing the submission"] +pub struct PresentFuture

+where + P: GpuFuture, +{ + previous: P, + queue: Arc, + swapchain_info: SwapchainPresentInfo, + // True if `flush()` has been called on the future, which means that the present command has + // been submitted. + flushed: AtomicBool, + // True if `signal_finished()` has been called on the future, which means that the future has + // been submitted and has already been processed by the GPU. + finished: AtomicBool, +} + +impl

PresentFuture

+where + P: GpuFuture, +{ + /// Returns the index of the image in the list of images returned when creating the swapchain. + pub fn image_id(&self) -> u32 { + self.swapchain_info.image_index + } + + /// Returns the corresponding swapchain. + pub fn swapchain(&self) -> &Arc { + &self.swapchain_info.swapchain + } +} + +unsafe impl

GpuFuture for PresentFuture

+where + P: GpuFuture, +{ + fn cleanup_finished(&mut self) { + self.previous.cleanup_finished(); + } + + unsafe fn build_submission(&self) -> Result { + if self.flushed.load(Ordering::SeqCst) { + return Ok(SubmitAnyBuilder::Empty); + } + + let mut swapchain_info = self.swapchain_info.clone(); + debug_assert!(swapchain_info.image_index < swapchain_info.swapchain.image_count()); + let device = swapchain_info.swapchain.device(); + + if !device.enabled_features().present_id { + swapchain_info.present_id = None; + } + + if device.enabled_extensions().khr_incremental_present { + for rectangle in &swapchain_info.present_regions { + assert!(rectangle.is_compatible_with(swapchain_info.swapchain.as_ref())); + } + } else { + swapchain_info.present_regions = Default::default(); + } + + let _queue = self.previous.queue(); + + // TODO: if the swapchain image layout is not PRESENT, should add a transition command + // buffer + + Ok(match self.previous.build_submission()? { + SubmitAnyBuilder::Empty => SubmitAnyBuilder::QueuePresent(PresentInfo { + swapchain_infos: vec![self.swapchain_info.clone()], + ..Default::default() + }), + SubmitAnyBuilder::SemaphoresWait(semaphores) => { + SubmitAnyBuilder::QueuePresent(PresentInfo { + wait_semaphores: semaphores.into_iter().collect(), + swapchain_infos: vec![self.swapchain_info.clone()], + ..Default::default() + }) + } + SubmitAnyBuilder::CommandBuffer(_, _) => { + self.previous.flush()?; + + SubmitAnyBuilder::QueuePresent(PresentInfo { + swapchain_infos: vec![self.swapchain_info.clone()], + ..Default::default() + }) + } + SubmitAnyBuilder::BindSparse(_, _) => { + self.previous.flush()?; + + SubmitAnyBuilder::QueuePresent(PresentInfo { + swapchain_infos: vec![self.swapchain_info.clone()], + ..Default::default() + }) + } + SubmitAnyBuilder::QueuePresent(mut present_info) => { + if present_info.swapchain_infos.first().map_or(false, |prev| { + prev.present_mode.is_some() != self.swapchain_info.present_mode.is_some() + }) { + // If the present mode Option variants don't match, create a new command. + self.previous.flush()?; + + SubmitAnyBuilder::QueuePresent(PresentInfo { + swapchain_infos: vec![self.swapchain_info.clone()], + ..Default::default() + }) + } else { + // Otherwise, add our swapchain to the previous. + present_info + .swapchain_infos + .push(self.swapchain_info.clone()); + + SubmitAnyBuilder::QueuePresent(present_info) + } + } + }) + } + + fn flush(&self) -> Result<(), FlushError> { + unsafe { + // If `flushed` already contains `true`, then `build_submission` will return `Empty`. + + let build_submission_result = self.build_submission(); + self.flushed.store(true, Ordering::SeqCst); + + match build_submission_result? { + SubmitAnyBuilder::Empty => Ok(()), + SubmitAnyBuilder::QueuePresent(present_info) => { + let PresentInfo { + wait_semaphores: _, + swapchain_infos, + _ne: _, + } = &present_info; + + let has_present_mode = swapchain_infos + .first() + .map_or(false, |first| first.present_mode.is_some()); + + // VUID-VkPresentIdKHR-presentIds-04999 + for swapchain_info in swapchain_infos { + let &SwapchainPresentInfo { + ref swapchain, + image_index: _, + present_id, + present_regions: _, + present_mode, + _ne: _, + } = swapchain_info; + + if present_id.map_or(false, |present_id| { + !swapchain.try_claim_present_id(present_id) + }) { + return Err(FlushError::PresentIdLessThanOrEqual); + } + + if let Some(present_mode) = present_mode { + assert!(has_present_mode); + + // VUID-VkSwapchainPresentModeInfoEXT-pPresentModes-07761 + if !swapchain.present_modes().contains(&present_mode) { + return Err(FlushError::PresentModeNotValid); + } + } else { + assert!(!has_present_mode); + } + } + + match self.previous.check_swapchain_image_acquired( + &self.swapchain_info.swapchain, + self.swapchain_info.image_index, + true, + ) { + Ok(_) => (), + Err(AccessCheckError::Unknown) => { + return Err(AccessError::SwapchainImageNotAcquired.into()) + } + Err(AccessCheckError::Denied(e)) => return Err(e.into()), + } + + Ok(self + .queue + .with(|mut q| q.present_unchecked(present_info))? + .map(|r| r.map(|_| ())) + .fold(Ok(()), Result::and)?) + } + _ => unreachable!(), + } + } + } + + unsafe fn signal_finished(&self) { + self.flushed.store(true, Ordering::SeqCst); + self.finished.store(true, Ordering::SeqCst); + self.previous.signal_finished(); + } + + fn queue_change_allowed(&self) -> bool { + false + } + + fn queue(&self) -> Option> { + debug_assert!(match self.previous.queue() { + None => true, + Some(q) => q == self.queue, + }); + + Some(self.queue.clone()) + } + + fn check_buffer_access( + &self, + buffer: &Buffer, + range: Range, + exclusive: bool, + queue: &Queue, + ) -> Result<(), AccessCheckError> { + self.previous + .check_buffer_access(buffer, range, exclusive, queue) + } + + fn check_image_access( + &self, + image: &Image, + range: Range, + exclusive: bool, + expected_layout: ImageLayout, + queue: &Queue, + ) -> Result<(), AccessCheckError> { + if self.swapchain_info.swapchain.index_of_image(image) + == Some(self.swapchain_info.image_index) + { + // This future presents the swapchain image, which "unlocks" it. Therefore any attempt + // to use this swapchain image afterwards shouldn't get granted automatic access. + // Instead any attempt to access the image afterwards should get an authorization from + // a later swapchain acquire future. Hence why we return `Unknown` here. + Err(AccessCheckError::Unknown) + } else { + self.previous + .check_image_access(image, range, exclusive, expected_layout, queue) + } + } + + #[inline] + fn check_swapchain_image_acquired( + &self, + swapchain: &Swapchain, + image_index: u32, + before: bool, + ) -> Result<(), AccessCheckError> { + if before { + self.previous + .check_swapchain_image_acquired(swapchain, image_index, false) + } else if swapchain == self.swapchain_info.swapchain.as_ref() + && image_index == self.swapchain_info.image_index + { + Err(AccessError::SwapchainImageNotAcquired.into()) + } else { + self.previous + .check_swapchain_image_acquired(swapchain, image_index, false) + } + } +} + +unsafe impl

DeviceOwned for PresentFuture

+where + P: GpuFuture, +{ + fn device(&self) -> &Arc { + self.queue.device() + } +} + +impl

Drop for PresentFuture

+where + P: GpuFuture, +{ + fn drop(&mut self) { + if thread::panicking() { + return; + } + + unsafe { + if !*self.flushed.get_mut() { + // Flushing may fail, that's okay. We will still wait for the queue later, so any + // previous futures that were flushed correctly will still be waited upon. + self.flush().ok(); + } + + if !*self.finished.get_mut() { + // Block until the queue finished. + self.queue().unwrap().with(|mut q| q.wait_idle()).unwrap(); + self.previous.signal_finished(); + } + } + } +} + +/// Wait for an image to be presented to the user. Must be used with a `present_id` given to +/// `present_with_id`. +/// +/// Returns a bool to represent if the presentation was suboptimal. In this case the swapchain is +/// still usable, but the swapchain should be recreated as the Surface's properties no longer match +/// the swapchain. +pub fn wait_for_present( + swapchain: Arc, + present_id: u64, + timeout: Option, +) -> Result { + let retired = swapchain.is_retired.lock(); + + // VUID-vkWaitForPresentKHR-swapchain-04997 + if *retired { + return Err(PresentWaitError::OutOfDate); + } + + if present_id == 0 { + return Err(PresentWaitError::PresentIdZero); + } + + // VUID-vkWaitForPresentKHR-presentWait-06234 + if !swapchain.device.enabled_features().present_wait { + return Err(PresentWaitError::RequirementNotMet { + required_for: "`wait_for_present`", + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature("present_wait")])]), + }); + } + + let timeout_ns = timeout.map(|dur| dur.as_nanos() as u64).unwrap_or(0); + + let result = unsafe { + (swapchain.device.fns().khr_present_wait.wait_for_present_khr)( + swapchain.device.handle(), + swapchain.handle, + present_id, + timeout_ns, + ) + }; + + match result { + ash::vk::Result::SUCCESS => Ok(false), + ash::vk::Result::SUBOPTIMAL_KHR => Ok(true), + ash::vk::Result::TIMEOUT => Err(PresentWaitError::Timeout), + err => { + let err = RuntimeError::from(err).into(); + + if let PresentWaitError::FullScreenExclusiveModeLost = &err { + swapchain + .full_screen_exclusive_held + .store(false, Ordering::SeqCst); + } + + Err(err) + } + } +} + +/// Error that can happen when calling `acquire_next_image`. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[repr(u32)] +pub enum PresentWaitError { + /// Not enough memory. + OomError(OomError), + + /// The connection to the device has been lost. + DeviceLost, + + /// The surface has changed in a way that makes the swapchain unusable. You must query the + /// surface's new properties and recreate a new swapchain if you want to continue drawing. + OutOfDate, + + /// The surface is no longer accessible and must be recreated. + SurfaceLost, + + /// The swapchain has lost or doesn't have full-screen exclusivity possibly for + /// implementation-specific reasons outside of the application’s control. + FullScreenExclusiveModeLost, + + /// The timeout of the function has been reached before the present occured. + Timeout, + + RequirementNotMet { + required_for: &'static str, + requires_one_of: RequiresOneOf, + }, + + /// Present id of zero is invalid. + PresentIdZero, +} + +impl Error for PresentWaitError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + Self::OomError(err) => Some(err), + _ => None, + } + } +} + +impl Display for PresentWaitError { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { + match self { + Self::OomError(e) => write!(f, "{}", e), + Self::DeviceLost => write!(f, "the connection to the device has been lost"), + Self::Timeout => write!(f, "no image is available for acquiring yet"), + Self::SurfaceLost => write!(f, "the surface of this swapchain is no longer valid"), + Self::OutOfDate => write!(f, "the swapchain needs to be recreated"), + Self::FullScreenExclusiveModeLost => { + write!(f, "the swapchain no longer has full-screen exclusivity") + } + Self::RequirementNotMet { + required_for, + requires_one_of, + } => write!( + f, + "a requirement was not met for: {}; requires one of: {}", + required_for, requires_one_of, + ), + Self::PresentIdZero => write!(f, "present id of zero is invalid"), + } + } +} + +impl From for PresentWaitError { + fn from(err: OomError) -> PresentWaitError { + Self::OomError(err) + } +} + +impl From for PresentWaitError { + fn from(err: RequirementNotMet) -> Self { + Self::RequirementNotMet { + required_for: err.required_for, + requires_one_of: err.requires_one_of, + } + } +} + +impl From for PresentWaitError { + fn from(err: RuntimeError) -> PresentWaitError { + match err { + err @ RuntimeError::OutOfHostMemory => Self::OomError(OomError::from(err)), + err @ RuntimeError::OutOfDeviceMemory => Self::OomError(OomError::from(err)), + RuntimeError::DeviceLost => Self::DeviceLost, + RuntimeError::SurfaceLost => Self::SurfaceLost, + RuntimeError::OutOfDate => Self::OutOfDate, + RuntimeError::FullScreenExclusiveModeLost => Self::FullScreenExclusiveModeLost, + _ => panic!("unexpected error: {:?}", err), + } + } +} diff --git a/vulkano/src/swapchain/mod.rs b/vulkano/src/swapchain/mod.rs index e3918de0..74d9db1e 100644 --- a/vulkano/src/swapchain/mod.rs +++ b/vulkano/src/swapchain/mod.rs @@ -321,159 +321,1908 @@ //! } //! ``` -pub use self::{ - surface::{ - ColorSpace, CompositeAlpha, CompositeAlphas, PresentMode, Surface, SurfaceApi, - SurfaceCapabilities, SurfaceCreationError, SurfaceInfo, SurfaceTransform, - SurfaceTransforms, - }, - swapchain::{ - acquire_next_image, acquire_next_image_raw, present, wait_for_present, AcquireError, - AcquiredImage, FullScreenExclusive, FullScreenExclusiveError, PresentFuture, - PresentWaitError, Swapchain, SwapchainAcquireFuture, SwapchainCreateInfo, - SwapchainCreationError, Win32Monitor, - }, -}; +pub use self::{acquire_present::*, surface::*}; #[cfg(target_os = "ios")] pub use surface::IOSMetalLayer; -use crate::sync::semaphore::Semaphore; -use std::{ - num::NonZeroU64, - sync::{atomic::AtomicBool, Arc}, -}; - +mod acquire_present; pub mod display; mod surface; -mod swapchain; -/// Parameters to execute present operations on a queue. +use crate::{ + device::{Device, DeviceOwned}, + format::Format, + image::{sys::Image, ImageFormatInfo, ImageTiling, ImageType, ImageUsage, SwapchainImage}, + macros::{impl_id_counter, vulkan_bitflags, vulkan_bitflags_enum, vulkan_enum}, + sync::Sharing, + OomError, Requires, RequiresAllOf, RequiresOneOf, RuntimeError, ValidationError, VulkanError, + VulkanObject, +}; +use parking_lot::Mutex; +use smallvec::SmallVec; +use std::{ + error::Error, + fmt::{Debug, Display, Error as FmtError, Formatter}, + mem::MaybeUninit, + num::NonZeroU64, + ptr, + sync::{ + atomic::{AtomicBool, AtomicU64, Ordering}, + Arc, + }, +}; + +/// Contains the swapping system and the images that can be shown on a surface. +pub struct Swapchain { + handle: ash::vk::SwapchainKHR, + device: Arc, + surface: Arc, + id: NonZeroU64, + + flags: SwapchainCreateFlags, + min_image_count: u32, + image_format: Format, + image_color_space: ColorSpace, + image_extent: [u32; 2], + image_array_layers: u32, + image_usage: ImageUsage, + image_sharing: Sharing>, + pre_transform: SurfaceTransform, + composite_alpha: CompositeAlpha, + present_mode: PresentMode, + present_modes: SmallVec<[PresentMode; PresentMode::COUNT]>, + clipped: bool, + scaling_behavior: Option, + present_gravity: Option<[PresentGravity; 2]>, + full_screen_exclusive: FullScreenExclusive, + win32_monitor: Option, + + prev_present_id: AtomicU64, + + // Whether full-screen exclusive is currently held. + full_screen_exclusive_held: AtomicBool, + + // The images of this swapchain. + images: Vec, + + // If true, that means we have tried to use this swapchain to recreate a new swapchain. The + // current swapchain can no longer be used for anything except presenting already-acquired + // images. + // + // We use a `Mutex` instead of an `AtomicBool` because we want to keep that locked while + // we acquire the image. + is_retired: Mutex, +} + +#[derive(Debug)] +struct ImageEntry { + handle: ash::vk::Image, + layout_initialized: AtomicBool, +} + +impl Swapchain { + /// Creates a new `Swapchain`. + /// + /// This function returns the swapchain plus a list of the images that belong to the + /// swapchain. The order in which the images are returned is important for the + /// `acquire_next_image` and `present` functions. + /// + /// # Panics + /// + /// - Panics if the device and the surface don't belong to the same instance. + /// - Panics if `create_info.usage` is empty. + /// + // TODO: isn't it unsafe to take the surface through an Arc when it comes to vulkano-win? + #[inline] + pub fn new( + device: Arc, + surface: Arc, + create_info: SwapchainCreateInfo, + ) -> Result<(Arc, Vec>), VulkanError> { + Self::validate_new_inner(&device, &surface, &create_info)?; + + unsafe { Ok(Self::new_unchecked(device, surface, create_info)?) } + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + #[inline] + pub unsafe fn new_unchecked( + device: Arc, + surface: Arc, + create_info: SwapchainCreateInfo, + ) -> Result<(Arc, Vec>), RuntimeError> { + let (handle, image_handles) = + Self::new_inner_unchecked(&device, &surface, &create_info, None)?; + + Ok(Self::from_handle( + device, + handle, + image_handles, + surface, + create_info, + )) + } + + /// Creates a new swapchain from this one. + /// + /// Use this when a swapchain has become invalidated, such as due to window resizes. + /// + /// # Panics + /// + /// - Panics if `create_info.usage` is empty. + pub fn recreate( + self: &Arc, + create_info: SwapchainCreateInfo, + ) -> Result<(Arc, Vec>), VulkanError> { + Self::validate_new_inner(&self.device, &self.surface, &create_info)?; + + { + let mut is_retired = self.is_retired.lock(); + + // The swapchain has already been used to create a new one. + if *is_retired { + return Err(ValidationError { + context: "self".into(), + problem: "has already been used to recreate a swapchain".into(), + vuids: &["VUID-VkSwapchainCreateInfoKHR-oldSwapchain-01933"], + ..Default::default() + } + .into()); + } else { + // According to the documentation of VkSwapchainCreateInfoKHR: + // + // > Upon calling vkCreateSwapchainKHR with a oldSwapchain that is not VK_NULL_HANDLE, + // > any images not acquired by the application may be freed by the implementation, + // > which may occur even if creation of the new swapchain fails. + // + // Therefore, we set retired to true and keep it to true even if the call to `vkCreateSwapchainKHR` below fails. + *is_retired = true; + } + } + + unsafe { Ok(self.recreate_unchecked(create_info)?) } + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn recreate_unchecked( + self: &Arc, + create_info: SwapchainCreateInfo, + ) -> Result<(Arc, Vec>), RuntimeError> { + // According to the documentation of VkSwapchainCreateInfoKHR: + // + // > Upon calling vkCreateSwapchainKHR with a oldSwapchain that is not VK_NULL_HANDLE, + // > any images not acquired by the application may be freed by the implementation, + // > which may occur even if creation of the new swapchain fails. + // + // Therefore, we set retired to true and keep it to true, + // even if the call to `vkCreateSwapchainKHR` below fails. + *self.is_retired.lock() = true; + + let (handle, image_handles) = unsafe { + Self::new_inner_unchecked(&self.device, &self.surface, &create_info, Some(self))? + }; + + let (mut swapchain, swapchain_images) = Self::from_handle( + self.device.clone(), + handle, + image_handles, + self.surface.clone(), + create_info, + ); + + if self.full_screen_exclusive == FullScreenExclusive::ApplicationControlled { + Arc::get_mut(&mut swapchain) + .unwrap() + .full_screen_exclusive_held = + AtomicBool::new(self.full_screen_exclusive_held.load(Ordering::Relaxed)) + }; + + Ok((swapchain, swapchain_images)) + } + + fn validate_new_inner( + device: &Device, + surface: &Surface, + create_info: &SwapchainCreateInfo, + ) -> Result<(), ValidationError> { + if !device.enabled_extensions().khr_swapchain { + return Err(ValidationError { + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension( + "khr_swapchain", + )])]), + ..Default::default() + }); + } + + create_info + .validate(device) + .map_err(|err| err.add_context("create_info"))?; + + let &SwapchainCreateInfo { + flags: _, + min_image_count, + image_format, + image_color_space, + image_extent, + image_array_layers, + image_usage, + image_sharing: _, + pre_transform, + composite_alpha, + present_mode, + ref present_modes, + clipped: _, + scaling_behavior, + present_gravity, + full_screen_exclusive, + win32_monitor, + _ne: _, + } = create_info; + + assert_eq!(device.instance(), surface.instance()); + + // VUID-VkSwapchainCreateInfoKHR-surface-01270 + if !device + .active_queue_family_indices() + .iter() + .any(|&index| unsafe { + device + .physical_device() + .surface_support_unchecked(index, surface) + .unwrap_or_default() + }) + { + return Err(ValidationError { + context: "surface".into(), + problem: "is not supported by the physical device".into(), + vuids: &["VUID-VkSwapchainCreateInfoKHR-surface-01270"], + ..Default::default() + }); + } + + let surface_capabilities = unsafe { + device + .physical_device() + .surface_capabilities_unchecked( + surface, + SurfaceInfo { + present_mode: (device.enabled_extensions().ext_swapchain_maintenance1) + .then_some(present_mode), + full_screen_exclusive, + win32_monitor, + ..Default::default() + }, + ) + .map_err(|_err| ValidationError { + context: "PhysicalDevice::surface_capabilities".into(), + problem: "returned an error".into(), + ..Default::default() + })? + }; + let surface_formats = unsafe { + device + .physical_device() + .surface_formats_unchecked( + surface, + SurfaceInfo { + present_mode: (device.enabled_extensions().ext_swapchain_maintenance1) + .then_some(present_mode), + full_screen_exclusive, + win32_monitor, + ..Default::default() + }, + ) + .map_err(|_err| ValidationError { + context: "PhysicalDevice::surface_formats".into(), + problem: "returned an error".into(), + ..Default::default() + })? + }; + let surface_present_modes: SmallVec<[_; PresentMode::COUNT]> = unsafe { + device + .physical_device() + .surface_present_modes_unchecked(surface) + .map_err(|_err| ValidationError { + context: "PhysicalDevice::surface_present_modes".into(), + problem: "returned an error".into(), + ..Default::default() + })? + .collect() + }; + + if surface_capabilities + .max_image_count + .map_or(false, |c| min_image_count > c) + { + return Err(ValidationError { + problem: "`create_info.min_image_count` is greater than the `max_image_count` \ + value of the capabilities of `surface`" + .into(), + vuids: &["VUID-VkSwapchainCreateInfoKHR-minImageCount-01272"], + ..Default::default() + }); + } + + if min_image_count < surface_capabilities.min_image_count { + return Err(ValidationError { + problem: "`create_info.min_image_count` is less than the `min_image_count` \ + value of the capabilities of `surface`" + .into(), + vuids: &["VUID-VkSwapchainCreateInfoKHR-presentMode-02839"], + ..Default::default() + }); + } + + if let Some(image_format) = image_format { + if !surface_formats + .iter() + .any(|&fc| fc == (image_format, image_color_space)) + { + return Err(ValidationError { + problem: "the combination of `create_info.image_format` and \ + `create_info.image_color_space` is not supported for `surface`" + .into(), + vuids: &["VUID-VkSwapchainCreateInfoKHR-imageFormat-01273"], + ..Default::default() + }); + } + } + + if image_array_layers > surface_capabilities.max_image_array_layers { + return Err(ValidationError { + problem: "`create_info.image_array_layers` is greater than the \ + `max_image_array_layers` value of the capabilities of `surface`" + .into(), + vuids: &["VUID-VkSwapchainCreateInfoKHR-imageArrayLayers-01275"], + ..Default::default() + }); + } + + if matches!( + present_mode, + PresentMode::Immediate + | PresentMode::Mailbox + | PresentMode::Fifo + | PresentMode::FifoRelaxed + ) && !surface_capabilities + .supported_usage_flags + .contains(image_usage) + { + return Err(ValidationError { + problem: "`create_info.present_mode` is `PresentMode::Immediate`, \ + `PresentMode::Mailbox`, `PresentMode::Fifo` or `PresentMode::FifoRelaxed`, \ + and `create_info.image_usage` contains flags that are not set in \ + the `supported_usage_flags` value of the capabilities of `surface`" + .into(), + vuids: &["VUID-VkSwapchainCreateInfoKHR-presentMode-01427"], + ..Default::default() + }); + } + + if !surface_capabilities + .supported_transforms + .contains_enum(pre_transform) + { + return Err(ValidationError { + problem: "`create_info.pre_transform` is not present in the \ + `supported_transforms` value of the capabilities of `surface`" + .into(), + vuids: &["VUID-VkSwapchainCreateInfoKHR-preTransform-01279"], + ..Default::default() + }); + } + + if !surface_capabilities + .supported_composite_alpha + .contains_enum(composite_alpha) + { + return Err(ValidationError { + problem: "`create_info.composite_alpha` is not present in the \ + `supported_composite_alpha` value of the capabilities of `surface`" + .into(), + vuids: &["VUID-VkSwapchainCreateInfoKHR-compositeAlpha-01280"], + ..Default::default() + }); + } + + if !surface_present_modes.contains(&present_mode) { + return Err(ValidationError { + problem: "`create_info.present_mode` is not supported for `surface`".into(), + vuids: &["VUID-VkSwapchainCreateInfoKHR-presentMode-01281"], + ..Default::default() + }); + } + + if present_modes.is_empty() { + if let Some(scaling_behavior) = scaling_behavior { + if !surface_capabilities + .supported_present_scaling + .contains_enum(scaling_behavior) + { + return Err(ValidationError { + problem: "`create_info.scaling_behavior` is not present in the \ + `supported_present_scaling` value of the \ + capabilities of `surface`" + .into(), + vuids: &[ + "VUID-VkSwapchainPresentScalingCreateInfoEXT-scalingBehavior-07770", + ], + ..Default::default() + }); + } + } + + if let Some(present_gravity) = present_gravity { + for (axis_index, (present_gravity, supported_present_gravity)) in present_gravity + .into_iter() + .zip(surface_capabilities.supported_present_gravity) + .enumerate() + { + if !supported_present_gravity.contains_enum(present_gravity) { + return Err(ValidationError { + problem: format!( + "`create_info.present_gravity[{0}]` is not present in the \ + `supported_present_gravity[{0}]` value of the \ + capabilities of `surface`", + axis_index, + ) + .into(), + vuids: &[ + "VUID-VkSwapchainPresentScalingCreateInfoEXT-presentGravityX-07772", + "VUID-VkSwapchainPresentScalingCreateInfoEXT-presentGravityY-07774", + ], + ..Default::default() + }); + } + } + } + } else { + for (index, &present_mode) in present_modes.iter().enumerate() { + if !surface_present_modes.contains(&present_mode) { + return Err(ValidationError { + problem: format!( + "`create_info.present_modes[{}]` is not supported for `surface`", + index, + ) + .into(), + vuids: &["VUID-VkSwapchainPresentModesCreateInfoEXT-None-07762"], + ..Default::default() + }); + } + + if !surface_capabilities + .compatible_present_modes + .contains(&present_mode) + { + return Err(ValidationError { + problem: format!( + "`create_info.present_modes[{}]` is not present in the \ + `compatible_present_modes` value of the \ + capabilities of `surface`", + index, + ) + .into(), + vuids: &["VUID-VkSwapchainPresentModesCreateInfoEXT-pPresentModes-07763"], + ..Default::default() + }); + } + + if scaling_behavior.is_some() || present_gravity.is_some() { + let surface_capabilities = unsafe { + device + .physical_device() + .surface_capabilities_unchecked( + surface, + SurfaceInfo { + present_mode: Some(present_mode), + full_screen_exclusive, + win32_monitor, + ..Default::default() + }, + ) + .map_err(|_err| ValidationError { + context: "PhysicalDevice::surface_capabilities".into(), + problem: "returned an error".into(), + ..Default::default() + })? + }; + + if let Some(scaling_behavior) = scaling_behavior { + if !surface_capabilities + .supported_present_scaling + .contains_enum(scaling_behavior) + { + return Err(ValidationError { + problem: format!( + "`create_info.scaling_behavior` is not present in the \ + `supported_present_scaling` value of the \ + capabilities of `surface` for \ + `create_info.present_modes[{}]`", + index, + ) + .into(), + vuids: &[ + "VUID-VkSwapchainPresentScalingCreateInfoEXT-scalingBehavior-07771", + ], + ..Default::default() + }); + } + } + + if let Some(present_gravity) = present_gravity { + for (axis_index, (present_gravity, supported_present_gravity)) in + present_gravity + .into_iter() + .zip(surface_capabilities.supported_present_gravity) + .enumerate() + { + if !supported_present_gravity.contains_enum(present_gravity) { + return Err(ValidationError { + problem: format!( + "`create_info.present_gravity[{0}]` is not present in the \ + `supported_present_gravity[{0}]` value of the \ + capabilities of `surface` for \ + `create_info.present_modes[{1}]`", + axis_index, index, + ) + .into(), + vuids: &[ + "VUID-VkSwapchainPresentScalingCreateInfoEXT-presentGravityX-07773", + "VUID-VkSwapchainPresentScalingCreateInfoEXT-presentGravityY-07775", + ], + ..Default::default() + }); + } + } + } + } + } + } + + if scaling_behavior.is_some() { + if let Some(min_scaled_image_extent) = surface_capabilities.min_scaled_image_extent { + if image_extent[0] < min_scaled_image_extent[0] + || image_extent[1] < min_scaled_image_extent[1] + { + return Err(ValidationError { + problem: "`scaling_behavior` is `Some`, and an element of \ + `create_info.image_extent` is less than the corresponding element \ + of the `min_scaled_image_extent` value of the \ + capabilities of `surface`" + .into(), + vuids: &["VUID-VkSwapchainCreateInfoKHR-pNext-07782"], + ..Default::default() + }); + } + } + + if let Some(max_scaled_image_extent) = surface_capabilities.max_scaled_image_extent { + if image_extent[0] > max_scaled_image_extent[0] + || image_extent[1] > max_scaled_image_extent[1] + { + return Err(ValidationError { + problem: "`scaling_behavior` is `Some`, and an element of \ + `create_info.image_extent` is greater than the corresponding element \ + of the `max_scaled_image_extent` value of the \ + capabilities of `surface`" + .into(), + vuids: &["VUID-VkSwapchainCreateInfoKHR-pNext-07782"], + ..Default::default() + }); + } + } + } else { + /* + This check is in the spec, but in practice leads to race conditions. + The window can be resized between calling `surface_capabilities` to get the + min/max extent, and then creating the swapchain. + + See this discussion: + https://github.com/KhronosGroup/Vulkan-Docs/issues/1144 + + if image_extent[0] < surface_capabilities.min_image_extent[0] + || image_extent[1] < surface_capabilities.min_image_extent[1] + { + return Err(ValidationError { + problem: "`scaling_behavior` is `Some`, and an element of \ + `create_info.image_extent` is less than the corresponding element \ + of the `min_image_extent` value of the \ + capabilities of `surface`" + .into(), + vuids: &["VUID-VkSwapchainCreateInfoKHR-pNext-07781"], + ..Default::default() + }); + } + + if image_extent[0] > surface_capabilities.max_image_extent[0] + || image_extent[1] > surface_capabilities.max_image_extent[1] + { + return Err(ValidationError { + problem: "`scaling_behavior` is `Some`, and an element of \ + `create_info.image_extent` is greater than the corresponding element \ + of the `max_image_extent` value of the \ + capabilities of `surface`" + .into(), + vuids: &["VUID-VkSwapchainCreateInfoKHR-pNext-07781"], + ..Default::default() + }); + } + */ + } + + if surface.api() == SurfaceApi::Win32 + && full_screen_exclusive == FullScreenExclusive::ApplicationControlled + { + if win32_monitor.is_none() { + return Err(ValidationError { + problem: "`surface` is a Win32 surface, and \ + `create_info.full_screen_exclusive` is \ + `FullScreenExclusive::ApplicationControlled`, but \ + `create_info.win32_monitor` is `None`" + .into(), + vuids: &["VUID-VkSwapchainCreateInfoKHR-pNext-02679"], + ..Default::default() + }); + } + } else { + if win32_monitor.is_some() { + return Err(ValidationError { + problem: "`surface` is not a Win32 surface, or \ + `create_info.full_screen_exclusive` is not \ + `FullScreenExclusive::ApplicationControlled`, but \ + `create_info.win32_monitor` is `Some`" + .into(), + ..Default::default() + }); + } + } + + Ok(()) + } + + unsafe fn new_inner_unchecked( + device: &Device, + surface: &Surface, + create_info: &SwapchainCreateInfo, + old_swapchain: Option<&Swapchain>, + ) -> Result<(ash::vk::SwapchainKHR, Vec), RuntimeError> { + let &SwapchainCreateInfo { + flags, + min_image_count, + image_format, + image_color_space, + image_extent, + image_array_layers, + image_usage, + ref image_sharing, + pre_transform, + composite_alpha, + present_mode, + ref present_modes, + clipped, + scaling_behavior, + present_gravity, + full_screen_exclusive, + win32_monitor, + _ne: _, + } = create_info; + + let (image_sharing_mode_vk, queue_family_index_count_vk, p_queue_family_indices_vk) = + match image_sharing { + Sharing::Exclusive => (ash::vk::SharingMode::EXCLUSIVE, 0, ptr::null()), + Sharing::Concurrent(ref ids) => ( + ash::vk::SharingMode::CONCURRENT, + ids.len() as u32, + ids.as_ptr(), + ), + }; + + let mut create_info_vk = ash::vk::SwapchainCreateInfoKHR { + flags: flags.into(), + surface: surface.handle(), + min_image_count, + image_format: image_format.unwrap().into(), + image_color_space: image_color_space.into(), + image_extent: ash::vk::Extent2D { + width: image_extent[0], + height: image_extent[1], + }, + image_array_layers, + image_usage: image_usage.into(), + image_sharing_mode: image_sharing_mode_vk, + queue_family_index_count: queue_family_index_count_vk, + p_queue_family_indices: p_queue_family_indices_vk, + pre_transform: pre_transform.into(), + composite_alpha: composite_alpha.into(), + present_mode: present_mode.into(), + clipped: clipped as ash::vk::Bool32, + old_swapchain: old_swapchain.map_or(ash::vk::SwapchainKHR::null(), |os| os.handle), + ..Default::default() + }; + let mut present_modes_info_vk = None; + let present_modes_vk: SmallVec<[ash::vk::PresentModeKHR; PresentMode::COUNT]>; + let mut present_scaling_info_vk = None; + let mut full_screen_exclusive_info_vk = None; + let mut full_screen_exclusive_win32_info_vk = None; + + if !present_modes.is_empty() { + present_modes_vk = present_modes.iter().copied().map(Into::into).collect(); + + let next = present_modes_info_vk.insert(ash::vk::SwapchainPresentModesCreateInfoEXT { + present_mode_count: present_modes_vk.len() as u32, + p_present_modes: present_modes_vk.as_ptr(), + ..Default::default() + }); + + next.p_next = create_info_vk.p_next as *mut _; + create_info_vk.p_next = next as *const _ as *const _; + } + + if scaling_behavior.is_some() || present_gravity.is_some() { + let [present_gravity_x, present_gravity_y] = + present_gravity.map_or_else(Default::default, |pg| pg.map(Into::into)); + let next = + present_scaling_info_vk.insert(ash::vk::SwapchainPresentScalingCreateInfoEXT { + scaling_behavior: scaling_behavior.map_or_else(Default::default, Into::into), + present_gravity_x, + present_gravity_y, + ..Default::default() + }); + + next.p_next = create_info_vk.p_next as *mut _; + create_info_vk.p_next = next as *const _ as *const _; + } + + if full_screen_exclusive != FullScreenExclusive::Default { + let next = + full_screen_exclusive_info_vk.insert(ash::vk::SurfaceFullScreenExclusiveInfoEXT { + full_screen_exclusive: full_screen_exclusive.into(), + ..Default::default() + }); + + next.p_next = create_info_vk.p_next as *mut _; + create_info_vk.p_next = next as *const _ as *const _; + } + + if let Some(Win32Monitor(hmonitor)) = win32_monitor { + let next = full_screen_exclusive_win32_info_vk.insert( + ash::vk::SurfaceFullScreenExclusiveWin32InfoEXT { + hmonitor, + ..Default::default() + }, + ); + + next.p_next = create_info_vk.p_next as *mut _; + create_info_vk.p_next = next as *const _ as *const _; + } + + let fns = device.fns(); + + let handle = { + let mut output = MaybeUninit::uninit(); + (fns.khr_swapchain.create_swapchain_khr)( + device.handle(), + &create_info_vk, + ptr::null(), + output.as_mut_ptr(), + ) + .result() + .map_err(RuntimeError::from)?; + output.assume_init() + }; + + let image_handles = loop { + let mut count = 0; + (fns.khr_swapchain.get_swapchain_images_khr)( + device.handle(), + handle, + &mut count, + ptr::null_mut(), + ) + .result() + .map_err(RuntimeError::from)?; + + let mut images = Vec::with_capacity(count as usize); + let result = (fns.khr_swapchain.get_swapchain_images_khr)( + device.handle(), + handle, + &mut count, + images.as_mut_ptr(), + ); + + match result { + ash::vk::Result::SUCCESS => { + images.set_len(count as usize); + break images; + } + ash::vk::Result::INCOMPLETE => (), + err => return Err(RuntimeError::from(err)), + } + }; + + Ok((handle, image_handles)) + } + + /// Creates a new `Swapchain` from a raw object handle. + /// + /// # Safety + /// + /// - `handle` and `image_handles` must be valid Vulkan object handles created from `device`. + /// - `handle` must not be retired. + /// - `image_handles` must be swapchain images owned by `handle`, + /// in the same order as they were returned by `vkGetSwapchainImagesKHR`. + /// - `surface` and `create_info` must match the info used to create the object. + pub unsafe fn from_handle( + device: Arc, + handle: ash::vk::SwapchainKHR, + image_handles: impl IntoIterator, + surface: Arc, + create_info: SwapchainCreateInfo, + ) -> (Arc, Vec>) { + let SwapchainCreateInfo { + flags, + min_image_count, + image_format, + image_color_space, + image_extent, + image_array_layers, + image_usage, + image_sharing, + pre_transform, + composite_alpha, + present_mode, + present_modes, + clipped, + scaling_behavior, + present_gravity, + full_screen_exclusive, + win32_monitor, + _ne: _, + } = create_info; + + let swapchain = Arc::new(Swapchain { + handle, + device, + surface, + id: Self::next_id(), + + flags, + min_image_count, + image_format: image_format.unwrap(), + image_color_space, + image_extent, + image_array_layers, + image_usage, + image_sharing, + pre_transform, + composite_alpha, + present_mode, + present_modes, + clipped, + scaling_behavior, + present_gravity, + full_screen_exclusive, + win32_monitor, + + prev_present_id: Default::default(), + full_screen_exclusive_held: AtomicBool::new(false), + images: image_handles + .into_iter() + .map(|handle| ImageEntry { + handle, + layout_initialized: AtomicBool::new(false), + }) + .collect(), + is_retired: Mutex::new(false), + }); + + let swapchain_images = swapchain + .images + .iter() + .enumerate() + .map(|(image_index, entry)| unsafe { + SwapchainImage::from_handle(entry.handle, swapchain.clone(), image_index as u32) + }) + .collect(); + + (swapchain, swapchain_images) + } + + /// Returns the creation parameters of the swapchain. + #[inline] + pub fn create_info(&self) -> SwapchainCreateInfo { + SwapchainCreateInfo { + flags: self.flags, + min_image_count: self.min_image_count, + image_format: Some(self.image_format), + image_color_space: self.image_color_space, + image_extent: self.image_extent, + image_array_layers: self.image_array_layers, + image_usage: self.image_usage, + image_sharing: self.image_sharing.clone(), + pre_transform: self.pre_transform, + composite_alpha: self.composite_alpha, + present_mode: self.present_mode, + present_modes: self.present_modes.clone(), + clipped: self.clipped, + scaling_behavior: self.scaling_behavior, + present_gravity: self.present_gravity, + full_screen_exclusive: self.full_screen_exclusive, + win32_monitor: self.win32_monitor, + _ne: crate::NonExhaustive(()), + } + } + + /// Returns the surface that the swapchain was created from. + #[inline] + pub fn surface(&self) -> &Arc { + &self.surface + } + + /// Returns the flags that the swapchain was created with. + #[inline] + pub fn flags(&self) -> SwapchainCreateFlags { + self.flags + } + + /// If `image` is one of the images of this swapchain, returns its index within the swapchain. + #[inline] + pub fn index_of_image(&self, image: &Image) -> Option { + self.images + .iter() + .position(|entry| entry.handle == image.handle()) + .map(|i| i as u32) + } + + /// Returns the number of images of the swapchain. + #[inline] + pub fn image_count(&self) -> u32 { + self.images.len() as u32 + } + + /// Returns the format of the images of the swapchain. + #[inline] + pub fn image_format(&self) -> Format { + self.image_format + } + + /// Returns the color space of the images of the swapchain. + #[inline] + pub fn image_color_space(&self) -> ColorSpace { + self.image_color_space + } + + /// Returns the extent of the images of the swapchain. + #[inline] + pub fn image_extent(&self) -> [u32; 2] { + self.image_extent + } + + /// Returns the number of array layers of the images of the swapchain. + #[inline] + pub fn image_array_layers(&self) -> u32 { + self.image_array_layers + } + + /// Returns the usage of the images of the swapchain. + #[inline] + pub fn image_usage(&self) -> ImageUsage { + self.image_usage + } + + /// Returns the sharing of the images of the swapchain. + #[inline] + pub fn image_sharing(&self) -> &Sharing> { + &self.image_sharing + } + + #[inline] + pub(crate) unsafe fn full_screen_exclusive_held(&self) -> &AtomicBool { + &self.full_screen_exclusive_held + } + + #[inline] + pub(crate) unsafe fn try_claim_present_id(&self, present_id: NonZeroU64) -> bool { + let present_id = u64::from(present_id); + self.prev_present_id.fetch_max(present_id, Ordering::SeqCst) < present_id + } + + /// Returns the pre-transform that was passed when creating the swapchain. + #[inline] + pub fn pre_transform(&self) -> SurfaceTransform { + self.pre_transform + } + + /// Returns the alpha mode that was passed when creating the swapchain. + #[inline] + pub fn composite_alpha(&self) -> CompositeAlpha { + self.composite_alpha + } + + /// Returns the present mode that was passed when creating the swapchain. + #[inline] + pub fn present_mode(&self) -> PresentMode { + self.present_mode + } + + /// Returns the alternative present modes that were passed when creating the swapchain. + #[inline] + pub fn present_modes(&self) -> &[PresentMode] { + &self.present_modes + } + + /// Returns the value of `clipped` that was passed when creating the swapchain. + #[inline] + pub fn clipped(&self) -> bool { + self.clipped + } + + /// Returns the scaling behavior that was passed when creating the swapchain. + #[inline] + pub fn scaling_behavior(&self) -> Option { + self.scaling_behavior + } + + /// Returns the scaling behavior that was passed when creating the swapchain. + #[inline] + pub fn present_gravity(&self) -> Option<[PresentGravity; 2]> { + self.present_gravity + } + + /// Returns the value of 'full_screen_exclusive` that was passed when creating the swapchain. + #[inline] + pub fn full_screen_exclusive(&self) -> FullScreenExclusive { + self.full_screen_exclusive + } + + /// Acquires full-screen exclusivity. + /// + /// The swapchain must have been created with [`FullScreenExclusive::ApplicationControlled`], + /// and must not already hold full-screen exclusivity. Full-screen exclusivity is held until + /// either the `release_full_screen_exclusive` is called, or if any of the the other `Swapchain` + /// functions return `FullScreenExclusiveLost`. + #[inline] + pub fn acquire_full_screen_exclusive(&self) -> Result<(), FullScreenExclusiveError> { + if self.full_screen_exclusive != FullScreenExclusive::ApplicationControlled { + return Err(FullScreenExclusiveError::NotApplicationControlled); + } + + if self.full_screen_exclusive_held.swap(true, Ordering::SeqCst) { + return Err(FullScreenExclusiveError::DoubleAcquire); + } + + unsafe { + let fns = self.device.fns(); + (fns.ext_full_screen_exclusive + .acquire_full_screen_exclusive_mode_ext)( + self.device.handle(), self.handle + ) + .result() + .map_err(RuntimeError::from)?; + } + + Ok(()) + } + + /// Releases full-screen exclusivity. + /// + /// The swapchain must have been created with [`FullScreenExclusive::ApplicationControlled`], + /// and must currently hold full-screen exclusivity. + #[inline] + pub fn release_full_screen_exclusive(&self) -> Result<(), FullScreenExclusiveError> { + if self.full_screen_exclusive != FullScreenExclusive::ApplicationControlled { + return Err(FullScreenExclusiveError::NotApplicationControlled); + } + + if !self + .full_screen_exclusive_held + .swap(false, Ordering::SeqCst) + { + return Err(FullScreenExclusiveError::DoubleRelease); + } + + unsafe { + let fns = self.device.fns(); + (fns.ext_full_screen_exclusive + .release_full_screen_exclusive_mode_ext)( + self.device.handle(), self.handle + ) + .result() + .map_err(RuntimeError::from)?; + } + + Ok(()) + } + + /// `FullScreenExclusive::AppControlled` is not the active full-screen exclusivity mode, + /// then this function will always return false. If true is returned the swapchain + /// is in `FullScreenExclusive::AppControlled` full-screen exclusivity mode and exclusivity + /// is currently acquired. + #[inline] + pub fn is_full_screen_exclusive(&self) -> bool { + if self.full_screen_exclusive != FullScreenExclusive::ApplicationControlled { + false + } else { + self.full_screen_exclusive_held.load(Ordering::SeqCst) + } + } + + // This method is necessary to allow `SwapchainImage`s to signal when they have been + // transitioned out of their initial `undefined` image layout. + // + // See the `ImageAccess::layout_initialized` method documentation for more details. + pub(crate) fn image_layout_initialized(&self, image_index: u32) { + let image_entry = self.images.get(image_index as usize); + if let Some(image_entry) = image_entry { + image_entry + .layout_initialized + .store(true, Ordering::Relaxed); + } + } + + pub(crate) fn is_image_layout_initialized(&self, image_index: u32) -> bool { + let image_entry = self.images.get(image_index as usize); + if let Some(image_entry) = image_entry { + image_entry.layout_initialized.load(Ordering::Relaxed) + } else { + false + } + } +} + +impl Drop for Swapchain { + #[inline] + fn drop(&mut self) { + unsafe { + let fns = self.device.fns(); + (fns.khr_swapchain.destroy_swapchain_khr)( + self.device.handle(), + self.handle, + ptr::null(), + ); + } + } +} + +unsafe impl VulkanObject for Swapchain { + type Handle = ash::vk::SwapchainKHR; + + #[inline] + fn handle(&self) -> Self::Handle { + self.handle + } +} + +unsafe impl DeviceOwned for Swapchain { + #[inline] + fn device(&self) -> &Arc { + &self.device + } +} + +impl_id_counter!(Swapchain); + +impl Debug for Swapchain { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { + let Self { + handle, + device, + surface, + id: _, + + flags, + min_image_count, + image_format, + image_color_space, + image_extent, + image_array_layers, + image_usage, + image_sharing, + pre_transform, + composite_alpha, + present_mode, + present_modes, + clipped, + scaling_behavior, + present_gravity, + full_screen_exclusive, + win32_monitor, + + prev_present_id, + full_screen_exclusive_held, + images, + is_retired, + } = self; + + f.debug_struct("Swapchain") + .field("handle", &handle) + .field("device", &device.handle()) + .field("surface", &surface.handle()) + .field("flags", &flags) + .field("min_image_count", min_image_count) + .field("image_format", image_format) + .field("image_color_space", image_color_space) + .field("image_extent", image_extent) + .field("image_array_layers", image_array_layers) + .field("image_usage", image_usage) + .field("image_sharing", image_sharing) + .field("pre_transform", pre_transform) + .field("composite_alpha", composite_alpha) + .field("present_mode", present_mode) + .field("present_modes", present_modes) + .field("clipped", clipped) + .field("scaling_behavior", scaling_behavior) + .field("present_gravity", present_gravity) + .field("full_screen_exclusive", full_screen_exclusive) + .field("win32_monitor", win32_monitor) + .field("prev_present_id", prev_present_id) + .field("full_screen_exclusive_held", full_screen_exclusive_held) + .field("images", images) + .field("retired", is_retired) + .finish() + } +} + +/// Parameters to create a new `Swapchain`. +/// +/// Many of the values here must be supported by the physical device. +/// [`PhysicalDevice`](crate::device::physical::PhysicalDevice) has several +/// methods to query what is supported. #[derive(Clone, Debug)] -pub struct PresentInfo { - /// The semaphores to wait for before beginning the execution of the present operations. +pub struct SwapchainCreateInfo { + /// Additional properties of the swapchain. /// /// The default value is empty. - pub wait_semaphores: Vec>, + pub flags: SwapchainCreateFlags, - /// The present operations to perform. + /// The minimum number of images that will be created. + /// + /// The implementation is allowed to create more than this number, but never less. + /// + /// The default value is `2`. + pub min_image_count: u32, + + /// The format of the created images. + /// + /// The default value is `None`, which must be overridden. + pub image_format: Option, + + /// The color space of the created images. + /// + /// The default value is [`ColorSpace::SrgbNonLinear`]. + pub image_color_space: ColorSpace, + + /// The extent of the created images. + /// + /// Both values must be greater than zero. Note that on some platforms, + /// [`SurfaceCapabilities::current_extent`] will be zero if the surface is minimized. + /// Care must be taken to check for this, to avoid trying to create a zero-size swapchain. + /// + /// The default value is `[0, 0]`, which must be overridden. + pub image_extent: [u32; 2], + + /// The number of array layers of the created images. + /// + /// The default value is `1`. + pub image_array_layers: u32, + + /// How the created images will be used. + /// + /// The default value is [`ImageUsage::empty()`], which must be overridden. + pub image_usage: ImageUsage, + + /// Whether the created images can be shared across multiple queues, or are limited to a single + /// queue. + /// + /// The default value is [`Sharing::Exclusive`]. + pub image_sharing: Sharing>, + + /// The transform that should be applied to an image before it is presented. + /// + /// The default value is [`SurfaceTransform::Identity`]. + pub pre_transform: SurfaceTransform, + + /// How alpha values of the pixels in the image are to be treated. + /// + /// The default value is [`CompositeAlpha::Opaque`]. + pub composite_alpha: CompositeAlpha, + + /// How the swapchain should behave when multiple images are waiting in the queue to be + /// presented. + /// + /// The default value is [`PresentMode::Fifo`]. + pub present_mode: PresentMode, + + /// Alternative present modes that can be used with this swapchain. The mode specified in + /// `present_mode` is the default mode, but can be changed for future present operations by + /// specifying it when presenting. + /// + /// If this is not empty, then the + /// [`ext_swapchain_maintenance1`](crate::device::DeviceExtensions::ext_swapchain_maintenance1) + /// extension must be enabled on the device. + /// It must always contain the mode specified in `present_mode`. /// /// The default value is empty. - pub swapchain_infos: Vec, + pub present_modes: SmallVec<[PresentMode; PresentMode::COUNT]>, + + /// Whether the implementation is allowed to discard rendering operations that affect regions of + /// the surface which aren't visible. This is important to take into account if your fragment + /// shader has side-effects or if you want to read back the content of the image afterwards. + /// + /// The default value is `true`. + pub clipped: bool, + + /// The scaling method to use when the surface is not the same size as the swapchain image. + /// + /// `None` means the behavior is implementation defined. + /// + /// If this is `Some`, then the + /// [`ext_swapchain_maintenance1`](crate::device::DeviceExtensions::ext_swapchain_maintenance1) + /// extension must be enabled on the device. + /// + /// The default value is `None`. + pub scaling_behavior: Option, + + /// The horizontal and vertical alignment to use when the swapchain image, after applying + /// scaling, does not fill the whole surface. + /// + /// `None` means the behavior is implementation defined. + /// + /// If this is `Some`, then the + /// [`ext_swapchain_maintenance1`](crate::device::DeviceExtensions::ext_swapchain_maintenance1) + /// extension must be enabled on the device. + /// + /// The default value is `None`. + pub present_gravity: Option<[PresentGravity; 2]>, + + /// How full-screen exclusivity is to be handled. + /// + /// If set to anything other than [`FullScreenExclusive::Default`], then the + /// [`ext_full_screen_exclusive`](crate::device::DeviceExtensions::ext_full_screen_exclusive) + /// extension must be enabled on the device. + /// + /// The default value is [`FullScreenExclusive::Default`]. + pub full_screen_exclusive: FullScreenExclusive, + + /// For Win32 surfaces, if `full_screen_exclusive` is + /// [`FullScreenExclusive::ApplicationControlled`], this specifies the monitor on which + /// full-screen exclusivity should be used. + /// + /// For this case, the value must be `Some`, and for all others it must be `None`. + /// + /// The default value is `None`. + pub win32_monitor: Option, pub _ne: crate::NonExhaustive, } -impl Default for PresentInfo { +impl Default for SwapchainCreateInfo { #[inline] fn default() -> Self { Self { - wait_semaphores: Vec::new(), - swapchain_infos: Vec::new(), + flags: SwapchainCreateFlags::empty(), + min_image_count: 2, + image_format: None, + image_color_space: ColorSpace::SrgbNonLinear, + image_extent: [0, 0], + image_array_layers: 1, + image_usage: ImageUsage::empty(), + image_sharing: Sharing::Exclusive, + pre_transform: SurfaceTransform::Identity, + composite_alpha: CompositeAlpha::Opaque, + present_mode: PresentMode::Fifo, + present_modes: SmallVec::new(), + clipped: true, + scaling_behavior: None, + present_gravity: None, + full_screen_exclusive: FullScreenExclusive::Default, + win32_monitor: None, _ne: crate::NonExhaustive(()), } } } -/// Parameters for a single present operation on a swapchain. -#[derive(Clone, Debug)] -pub struct SwapchainPresentInfo { - /// The swapchain to present to. - /// - /// There is no default value. - pub swapchain: Arc, +impl SwapchainCreateInfo { + pub(crate) fn validate(&self, device: &Device) -> Result<(), ValidationError> { + let &Self { + flags, + min_image_count: _, + image_format, + image_color_space, + image_extent, + image_array_layers, + image_usage, + ref image_sharing, + pre_transform, + composite_alpha, + present_mode, + ref present_modes, + clipped: _, + scaling_behavior, + present_gravity, + full_screen_exclusive, + win32_monitor: _, + _ne: _, + } = self; - /// The index of the swapchain image to present to. - /// - /// The image must have been acquired first; this is the index that `acquire_next_image` - /// returns. - /// - /// There is no default value. - pub image_index: u32, + flags + .validate_device(device) + .map_err(|err| ValidationError { + context: "flags".into(), + vuids: &["VUID-VkSwapchainCreateInfoKHR-flags-parameter"], + ..ValidationError::from_requirement(err) + })?; - /// An id used to identify this present operation. - /// - /// If `present_id` is `Some`, the [`present_id`](crate::device::Features::present_id) feature - /// must be enabled on the device. The id must be greater than any id previously used for - /// `swapchain`. If a swapchain is recreated, this resets. - /// - /// The default value is `None`. - pub present_id: Option, + let image_format = image_format.ok_or(ValidationError { + context: "image_format".into(), + problem: "is `None`".into(), + vuids: &["VUID-VkSwapchainCreateInfoKHR-imageFormat-parameter"], + ..Default::default() + })?; - /// Am optimization hint to the implementation, that only some parts of the swapchain image are - /// going to be updated by the present operation. - /// - /// If `present_regions` is not empty, the - /// [`khr_incremental_present`](crate::device::DeviceExtensions::khr_incremental_present) - /// extension must be enabled on the device. The implementation will update the provided - /// regions of the swapchain image, and _may_ ignore the other areas. However, as this is just - /// a hint, the Vulkan implementation is free to ignore the regions altogether and update - /// everything. - /// - /// If `present_regions` is empty, that means that all of the swapchain image must be updated. - /// - /// The default value is empty. - pub present_regions: Vec, + image_format + .validate_device(device) + .map_err(|err| ValidationError { + context: "image_format".into(), + vuids: &["VUID-VkSwapchainCreateInfoKHR-imageFormat-parameter"], + ..ValidationError::from_requirement(err) + })?; - pub _ne: crate::NonExhaustive, + image_color_space + .validate_device(device) + .map_err(|err| ValidationError { + context: "image_color_space".into(), + vuids: &["VUID-VkSwapchainCreateInfoKHR-imageColorSpace-parameter"], + ..ValidationError::from_requirement(err) + })?; + + image_usage + .validate_device(device) + .map_err(|err| ValidationError { + context: "image_usage".into(), + vuids: &["VUID-VkSwapchainCreateInfoKHR-imageUsage-parameter"], + ..ValidationError::from_requirement(err) + })?; + + if image_usage.is_empty() { + return Err(ValidationError { + context: "image_usage".into(), + problem: "is empty".into(), + vuids: &["VUID-VkSwapchainCreateInfoKHR-imageUsage-requiredbitmask"], + ..Default::default() + }); + } + + pre_transform + .validate_device(device) + .map_err(|err| ValidationError { + context: "pre_transform".into(), + vuids: &["VUID-VkSwapchainCreateInfoKHR-preTransform-parameter"], + ..ValidationError::from_requirement(err) + })?; + + composite_alpha + .validate_device(device) + .map_err(|err| ValidationError { + context: "composite_alpha".into(), + vuids: &["VUID-VkSwapchainCreateInfoKHR-compositeAlpha-parameter"], + ..ValidationError::from_requirement(err) + })?; + + present_mode + .validate_device(device) + .map_err(|err| ValidationError { + context: "present_mode".into(), + vuids: &["VUID-VkSwapchainCreateInfoKHR-presentMode-parameter"], + ..ValidationError::from_requirement(err) + })?; + + if image_extent.contains(&0) { + return Err(ValidationError { + context: "image_extent".into(), + problem: "one or more elements are zero".into(), + vuids: &["VUID-VkSwapchainCreateInfoKHR-imageExtent-01689"], + ..Default::default() + }); + } + + if image_array_layers == 0 { + return Err(ValidationError { + context: "image_array_layers".into(), + problem: "is zero".into(), + vuids: &["VUID-VkSwapchainCreateInfoKHR-imageArrayLayers-01275"], + ..Default::default() + }); + } + + match image_sharing { + Sharing::Exclusive => (), + Sharing::Concurrent(queue_family_indices) => { + if queue_family_indices.len() < 2 { + return Err(ValidationError { + context: "image_sharing".into(), + problem: "is `Sharing::Concurrent`, and contains less than 2 \ + queue family indices" + .into(), + vuids: &["VUID-VkSwapchainCreateInfoKHR-imageSharingMode-01278"], + ..Default::default() + }); + } + + let queue_family_count = + device.physical_device().queue_family_properties().len() as u32; + + for (index, &queue_family_index) in queue_family_indices.iter().enumerate() { + if queue_family_indices[..index].contains(&queue_family_index) { + return Err(ValidationError { + problem: format!( + "the queue family index in the list at index {} is contained in \ + the list more than once", + index, + ) + .into(), + vuids: &["VUID-VkSwapchainCreateInfoKHR-imageSharingMode-01428"], + ..Default::default() + }); + } + + if queue_family_index >= queue_family_count { + return Err(ValidationError { + context: format!("queue_family_indices[{}]", index).into(), + problem: "is not less than the number of queue families in the \ + physical device" + .into(), + vuids: &["VUID-VkSwapchainCreateInfoKHR-imageSharingMode-01428"], + ..Default::default() + }); + } + } + } + }; + + let image_format_properties = unsafe { + device + .physical_device() + .image_format_properties_unchecked(ImageFormatInfo { + format: Some(image_format), + image_type: ImageType::Dim2d, + tiling: ImageTiling::Optimal, + usage: image_usage, + ..Default::default() + }) + .map_err(|_err| ValidationError { + context: "PhysicalDevice::image_format_properties".into(), + problem: "returned an error".into(), + ..Default::default() + })? + }; + + if image_format_properties.is_none() { + return Err(ValidationError { + problem: "the combination of `image_format` and `image_usage` is not supported \ + for images by the physical device" + .into(), + vuids: &["VUID-VkSwapchainCreateInfoKHR-imageFormat-01778"], + ..Default::default() + }); + } + + if !present_modes.is_empty() { + if !device.enabled_extensions().ext_swapchain_maintenance1 { + return Err(ValidationError { + context: "present_modes".into(), + problem: "is not empty".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension( + "ext_swapchain_maintenance1", + )])]), + ..Default::default() + }); + } + + for (index, &present_mode) in present_modes.iter().enumerate() { + present_mode + .validate_device(device) + .map_err(|err| ValidationError { + context: format!("present_modes[{}]", index).into(), + vuids: &[ + "VUID-VkSwapchainPresentModesCreateInfoEXT-pPresentModes-parameter", + ], + ..ValidationError::from_requirement(err) + })?; + } + + if !present_modes.contains(&present_mode) { + return Err(ValidationError { + problem: "`present_modes` is not empty, but does not contain `present_mode`" + .into(), + vuids: &["VUID-VkSwapchainPresentModesCreateInfoEXT-presentMode-07764"], + ..Default::default() + }); + } + } + + if let Some(scaling_behavior) = scaling_behavior { + if !device.enabled_extensions().ext_swapchain_maintenance1 { + return Err(ValidationError { + context: "scaling_behavior".into(), + problem: "is `Some`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension( + "ext_swapchain_maintenance1", + )])]), + ..Default::default() + }); + } + + scaling_behavior + .validate_device(device) + .map_err(|err| ValidationError { + context: "scaling_behavior".into(), + vuids: &[ + "VUID-VkSwapchainPresentScalingCreateInfoEXT-scalingBehavior-parameter", + ], + ..ValidationError::from_requirement(err) + })?; + + // VUID-VkSwapchainPresentScalingCreateInfoEXT-scalingBehavior-07767 + // Ensured by the use of an enum. + } + + if let Some(present_gravity) = present_gravity { + if !device.enabled_extensions().ext_swapchain_maintenance1 { + return Err(ValidationError { + context: "present_gravity".into(), + problem: "is `Some`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension( + "ext_swapchain_maintenance1", + )])]), + ..Default::default() + }); + } + + for (axis_index, present_gravity) in present_gravity.into_iter().enumerate() { + present_gravity + .validate_device(device) + .map_err(|err| ValidationError { + context: format!("present_gravity[{}]", axis_index).into(), + vuids: &[ + "VUID-VkSwapchainPresentScalingCreateInfoEXT-presentGravityX-parameter", + "VUID-VkSwapchainPresentScalingCreateInfoEXT-presentGravityY-parameter", + ], + ..ValidationError::from_requirement(err) + })?; + } + + // VUID-VkSwapchainPresentScalingCreateInfoEXT-presentGravityX-07765 + // VUID-VkSwapchainPresentScalingCreateInfoEXT-presentGravityX-07766 + // Ensured by the use of an array of enums wrapped in `Option`. + + // VUID-VkSwapchainPresentScalingCreateInfoEXT-presentGravityX-07768 + // VUID-VkSwapchainPresentScalingCreateInfoEXT-presentGravityY-07769 + // Ensured by the use of an enum. + } + + if full_screen_exclusive != FullScreenExclusive::Default { + if !device.enabled_extensions().ext_full_screen_exclusive { + return Err(ValidationError { + context: "full_screen_exclusive".into(), + problem: "is not `FullScreenExclusive::Default`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension( + "ext_full_screen_exclusive", + )])]), + ..Default::default() + }); + } + + full_screen_exclusive + .validate_device(device) + .map_err(|err| ValidationError { + context: "full_screen_exclusive".into(), + vuids: &[ + "VUID-VkSurfaceFullScreenExclusiveInfoEXT-fullScreenExclusive-parameter", + ], + ..ValidationError::from_requirement(err) + })?; + } + + Ok(()) + } } -impl SwapchainPresentInfo { - /// Returns a `SwapchainPresentInfo` with the specified `swapchain` and `image_index`. - #[inline] - pub fn swapchain_image_index(swapchain: Arc, image_index: u32) -> Self { - Self { - swapchain, - image_index, - present_id: None, - present_regions: Vec::new(), - _ne: crate::NonExhaustive(()), +vulkan_bitflags! { + #[non_exhaustive] + + /// Flags specifying additional properties of a swapchain. + SwapchainCreateFlags = SwapchainCreateFlagsKHR(u32); + + /* TODO: enable + // TODO: document + SPLIT_INSTANCE_BIND_REGIONS = SPLIT_INSTANCE_BIND_REGIONS { + // Provided by VK_VERSION_1_1 with VK_KHR_swapchain, VK_KHR_device_group with VK_KHR_swapchain + },*/ + + /* TODO: enable + // TODO: document + PROTECTED = PROTECTED { + // Provided by VK_VERSION_1_1 with VK_KHR_swapchain + },*/ + + /* TODO: enable + // TODO: document + MUTABLE_FORMAT = MUTABLE_FORMAT { + device_extensions: [khr_swapchain_mutable_format], + },*/ + + /* TODO: enable + // TODO: document + DEFERRED_MEMORY_ALLOCATION = DEFERRED_MEMORY_ALLOCATION_EXT { + device_extensions: [ext_swapchain_maintenance1], + },*/ +} + +vulkan_bitflags_enum! { + #[non_exhaustive] + + /// A set of [`PresentScaling`] values. + PresentScalingFlags, + + /// The way a swapchain image is scaled, if it does not exactly fit the surface. + PresentScaling, + + = PresentScalingFlagsEXT(u32); + + /// No scaling is performed; one swapchain image pixel maps to one surface pixel. + ONE_TO_ONE, OneToOne = ONE_TO_ONE, + + /// Both axes of the image are scaled equally, without changing the aspect ratio of the image, + /// to the largest size in which both axes fit inside the surface. + ASPECT_RATIO_STRETCH, AspectRatioStretch = ASPECT_RATIO_STRETCH, + + /// Each axis of the image is scaled independently to fit the surface, + /// which may change the aspect ratio of the image. + STRETCH, Stretch = STRETCH, +} + +vulkan_bitflags_enum! { + #[non_exhaustive] + + /// A set of [`PresentGravity`] values. + PresentGravityFlags, + + /// The way a swapchain image is aligned, if it does not exactly fit the surface. + PresentGravity, + + = PresentGravityFlagsEXT(u32); + + /// Aligned to the top or left side of the surface. + MIN, Min = MIN, + + /// Aligned to the bottom or right side of the surface. + MAX, Max = MAX, + + /// Aligned to the middle of the surface. + CENTERED, Centered = CENTERED, +} + +vulkan_enum! { + #[non_exhaustive] + + /// The way full-screen exclusivity is handled. + FullScreenExclusive = FullScreenExclusiveEXT(i32); + + /// Indicates that the driver should determine the appropriate full-screen method + /// by whatever means it deems appropriate. + Default = DEFAULT, + + /// Indicates that the driver may use full-screen exclusive mechanisms when available. + /// Such mechanisms may result in better performance and/or the availability of + /// different presentation capabilities, but may require a more disruptive transition + // during swapchain initialization, first presentation and/or destruction. + Allowed = ALLOWED, + + /// Indicates that the driver should avoid using full-screen mechanisms which rely + /// on disruptive transitions. + Disallowed = DISALLOWED, + + /// Indicates the application will manage full-screen exclusive mode by using the + /// [`Swapchain::acquire_full_screen_exclusive()`] and + /// [`Swapchain::release_full_screen_exclusive()`] functions. + ApplicationControlled = APPLICATION_CONTROLLED, +} + +/// A wrapper around a Win32 monitor handle. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct Win32Monitor(pub(crate) ash::vk::HMONITOR); + +impl Win32Monitor { + /// Wraps a Win32 monitor handle. + /// + /// # Safety + /// + /// - `hmonitor` must be a valid handle as returned by the Win32 API. + pub unsafe fn new(hmonitor: *const T) -> Self { + Self(hmonitor as _) + } +} + +// Winit's `MonitorHandle` is Send on Win32, so this seems safe. +unsafe impl Send for Win32Monitor {} +unsafe impl Sync for Win32Monitor {} + +/// Error that can happen when calling `Swapchain::acquire_full_screen_exclusive` or +/// `Swapchain::release_full_screen_exclusive`. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum FullScreenExclusiveError { + /// Not enough memory. + OomError(OomError), + + /// Operation could not be completed for driver specific reasons. + InitializationFailed, + + /// The surface is no longer accessible and must be recreated. + SurfaceLost, + + /// Full-screen exclusivity is already acquired. + DoubleAcquire, + + /// Full-screen exclusivity is not currently acquired. + DoubleRelease, + + /// The swapchain is not in full-screen exclusive application controlled mode. + NotApplicationControlled, +} + +impl Error for FullScreenExclusiveError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + FullScreenExclusiveError::OomError(err) => Some(err), + _ => None, } } } -/// Represents a rectangular region on an image layer. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct RectangleLayer { - /// Coordinates in pixels of the top-left hand corner of the rectangle. - pub offset: [u32; 2], - - /// Dimensions in pixels of the rectangle. - pub extent: [u32; 2], - - /// The layer of the image. For images with only one layer, the value of layer must be 0. - pub layer: u32, -} - -impl RectangleLayer { - /// Returns true if this rectangle layer is compatible with swapchain. - #[inline] - pub fn is_compatible_with(&self, swapchain: &Swapchain) -> bool { - self.offset[0] + self.extent[0] <= swapchain.image_extent()[0] - && self.offset[1] + self.extent[1] <= swapchain.image_extent()[1] - && self.layer < swapchain.image_array_layers() +impl Display for FullScreenExclusiveError { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { + write!( + f, + "{}", + match self { + FullScreenExclusiveError::OomError(_) => "not enough memory", + FullScreenExclusiveError::SurfaceLost => { + "the surface of this swapchain is no longer valid" + } + FullScreenExclusiveError::InitializationFailed => { + "operation could not be completed for driver specific reasons" + } + FullScreenExclusiveError::DoubleAcquire => + "full-screen exclusivity is already acquired", + FullScreenExclusiveError::DoubleRelease => + "full-screen exclusivity is not acquired", + FullScreenExclusiveError::NotApplicationControlled => { + "the swapchain is not in full-screen exclusive application controlled mode" + } + } + ) } } -impl From<&RectangleLayer> for ash::vk::RectLayerKHR { - #[inline] - fn from(val: &RectangleLayer) -> Self { - ash::vk::RectLayerKHR { - offset: ash::vk::Offset2D { - x: val.offset[0] as i32, - y: val.offset[1] as i32, - }, - extent: ash::vk::Extent2D { - width: val.extent[0], - height: val.extent[1], - }, - layer: val.layer, +impl From for FullScreenExclusiveError { + fn from(err: RuntimeError) -> FullScreenExclusiveError { + match err { + err @ RuntimeError::OutOfHostMemory => { + FullScreenExclusiveError::OomError(OomError::from(err)) + } + err @ RuntimeError::OutOfDeviceMemory => { + FullScreenExclusiveError::OomError(OomError::from(err)) + } + RuntimeError::SurfaceLost => FullScreenExclusiveError::SurfaceLost, + RuntimeError::InitializationFailed => FullScreenExclusiveError::InitializationFailed, + _ => panic!("unexpected error: {:?}", err), } } } -/// Internal trait so that creating/destroying a swapchain can access the surface's "has_swapchain" -/// flag. -// TODO: use pub(crate) maybe? -unsafe trait SurfaceSwapchainLock { - fn flag(&self) -> &AtomicBool; +impl From for FullScreenExclusiveError { + fn from(err: OomError) -> FullScreenExclusiveError { + FullScreenExclusiveError::OomError(err) + } } diff --git a/vulkano/src/swapchain/surface.rs b/vulkano/src/swapchain/surface.rs index e0c57876..9a2cb435 100644 --- a/vulkano/src/swapchain/surface.rs +++ b/vulkano/src/swapchain/surface.rs @@ -7,7 +7,7 @@ // notice may not be copied, modified, or distributed except // according to those terms. -use super::{FullScreenExclusive, Win32Monitor}; +use super::{FullScreenExclusive, PresentGravityFlags, PresentScalingFlags, Win32Monitor}; use crate::{ cache::OnceCache, device::physical::PhysicalDevice, @@ -15,25 +15,23 @@ use crate::{ image::ImageUsage, instance::{Instance, InstanceExtensions}, macros::{impl_id_counter, vulkan_bitflags_enum, vulkan_enum}, - swapchain::{ - display::{DisplayMode, DisplayPlane}, - SurfaceSwapchainLock, - }, - OomError, Requires, RequiresAllOf, RequiresOneOf, RuntimeError, ValidationError, VulkanObject, + swapchain::display::{DisplayMode, DisplayPlane}, + Requires, RequiresAllOf, RequiresOneOf, RuntimeError, ValidationError, VulkanError, + VulkanObject, }; #[cfg(any(target_os = "macos", target_os = "ios"))] use objc::{class, msg_send, runtime::Object, sel, sel_impl}; use raw_window_handle::{ HasRawDisplayHandle, HasRawWindowHandle, RawDisplayHandle, RawWindowHandle, }; +use smallvec::SmallVec; use std::{ any::Any, - error::Error, - fmt::{Debug, Display, Error as FmtError, Formatter}, + fmt::{Debug, Error as FmtError, Formatter}, mem::MaybeUninit, num::NonZeroU64, ptr, - sync::{atomic::AtomicBool, Arc}, + sync::Arc, }; /// Represents a surface on the screen. @@ -45,9 +43,6 @@ pub struct Surface { id: NonZeroU64, api: SurfaceApi, object: Option>, - // If true, a swapchain has been associated to this surface, and that any new swapchain - // creation should be forbidden. - has_swapchain: AtomicBool, // FIXME: This field is never set. #[cfg(target_os = "ios")] metal_layer: IOSMetalLayer, @@ -88,7 +83,7 @@ impl Surface { pub fn from_window( instance: Arc, window: Arc, - ) -> Result, SurfaceCreationError> { + ) -> Result, VulkanError> { let mut surface = unsafe { Self::from_window_ref(instance, &*window) }?; Arc::get_mut(&mut surface).unwrap().object = Some(window); @@ -104,7 +99,7 @@ impl Surface { pub unsafe fn from_window_ref( instance: Arc, window: &(impl HasRawWindowHandle + HasRawDisplayHandle), - ) -> Result, SurfaceCreationError> { + ) -> Result, VulkanError> { match (window.raw_window_handle(), window.raw_display_handle()) { (RawWindowHandle::AndroidNdk(window), RawDisplayHandle::Android(_display)) => { Self::from_android(instance, window.a_native_window, None) @@ -159,7 +154,6 @@ impl Surface { id: Self::next_id(), api, object, - has_swapchain: AtomicBool::new(false), #[cfg(target_os = "ios")] metal_layer: IOSMetalLayer::new(std::ptr::null_mut(), std::ptr::null_mut()), surface_formats: OnceCache::new(), @@ -175,19 +169,19 @@ impl Surface { pub fn headless( instance: Arc, object: Option>, - ) -> Result, SurfaceCreationError> { + ) -> Result, VulkanError> { Self::validate_headless(&instance)?; unsafe { Ok(Self::headless_unchecked(instance, object)?) } } - fn validate_headless(instance: &Instance) -> Result<(), SurfaceCreationError> { + fn validate_headless(instance: &Instance) -> Result<(), ValidationError> { if !instance.enabled_extensions().ext_headless_surface { - return Err(SurfaceCreationError::RequirementNotMet { - required_for: "`Surface::headless`", + return Err(ValidationError { requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::InstanceExtension( "ext_headless_surface", )])]), + ..Default::default() }); } @@ -235,7 +229,7 @@ impl Surface { pub fn from_display_plane( display_mode: &DisplayMode, plane: &DisplayPlane, - ) -> Result, SurfaceCreationError> { + ) -> Result, VulkanError> { Self::validate_from_display_plane(display_mode, plane)?; unsafe { Ok(Self::from_display_plane_unchecked(display_mode, plane)?) } @@ -244,7 +238,7 @@ impl Surface { fn validate_from_display_plane( display_mode: &DisplayMode, plane: &DisplayPlane, - ) -> Result<(), SurfaceCreationError> { + ) -> Result<(), ValidationError> { if !display_mode .display() .physical_device() @@ -252,11 +246,11 @@ impl Surface { .enabled_extensions() .khr_display { - return Err(SurfaceCreationError::RequirementNotMet { - required_for: "`Surface::from_display_plane`", + return Err(ValidationError { requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::InstanceExtension( "khr_display", )])]), + ..Default::default() }); } @@ -325,7 +319,7 @@ impl Surface { instance: Arc, window: *const W, object: Option>, - ) -> Result, SurfaceCreationError> { + ) -> Result, VulkanError> { Self::validate_from_android(&instance, window)?; Ok(Self::from_android_unchecked(instance, window, object)?) @@ -334,13 +328,13 @@ impl Surface { fn validate_from_android( instance: &Instance, _window: *const W, - ) -> Result<(), SurfaceCreationError> { + ) -> Result<(), ValidationError> { if !instance.enabled_extensions().khr_android_surface { - return Err(SurfaceCreationError::RequirementNotMet { - required_for: "`Surface::from_android`", + return Err(ValidationError { requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::InstanceExtension( "khr_android_surface", )])]), + ..Default::default() }); } @@ -397,7 +391,7 @@ impl Surface { dfb: *const D, surface: *const S, object: Option>, - ) -> Result, SurfaceCreationError> { + ) -> Result, VulkanError> { Self::validate_from_directfb(&instance, dfb, surface)?; Ok(Self::from_directfb_unchecked( @@ -409,13 +403,13 @@ impl Surface { instance: &Instance, _dfb: *const D, _surface: *const S, - ) -> Result<(), SurfaceCreationError> { + ) -> Result<(), ValidationError> { if !instance.enabled_extensions().ext_directfb_surface { - return Err(SurfaceCreationError::RequirementNotMet { - required_for: "`Surface::from_directfb`", + return Err(ValidationError { requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::InstanceExtension( "ext_directfb_surface", )])]), + ..Default::default() }); } @@ -475,7 +469,7 @@ impl Surface { instance: Arc, image_pipe_handle: ash::vk::zx_handle_t, object: Option>, - ) -> Result, SurfaceCreationError> { + ) -> Result, VulkanError> { Self::validate_from_fuchsia_image_pipe(&instance, image_pipe_handle)?; Ok(Self::from_fuchsia_image_pipe_unchecked( @@ -488,13 +482,13 @@ impl Surface { fn validate_from_fuchsia_image_pipe( instance: &Instance, _image_pipe_handle: ash::vk::zx_handle_t, - ) -> Result<(), SurfaceCreationError> { + ) -> Result<(), ValidationError> { if !instance.enabled_extensions().fuchsia_imagepipe_surface { - return Err(SurfaceCreationError::RequirementNotMet { - required_for: "`Surface::from_fuchsia_image_pipe`", + return Err(ValidationError { requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::InstanceExtension( "fuchsia_imagepipe_surface", )])]), + ..Default::default() }); } @@ -550,7 +544,7 @@ impl Surface { instance: Arc, stream_descriptor: ash::vk::GgpStreamDescriptor, object: Option>, - ) -> Result, SurfaceCreationError> { + ) -> Result, VulkanError> { Self::validate_from_ggp_stream_descriptor(&instance, stream_descriptor)?; Ok(Self::from_ggp_stream_descriptor_unchecked( @@ -563,13 +557,13 @@ impl Surface { fn validate_from_ggp_stream_descriptor( instance: &Instance, _stream_descriptor: ash::vk::GgpStreamDescriptor, - ) -> Result<(), SurfaceCreationError> { + ) -> Result<(), ValidationError> { if !instance.enabled_extensions().ggp_stream_descriptor_surface { - return Err(SurfaceCreationError::RequirementNotMet { - required_for: "`Surface::from_ggp_stream_descriptor`", + return Err(ValidationError { requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::InstanceExtension( "ggp_stream_descriptor_surface", )])]), + ..Default::default() }); } @@ -627,7 +621,7 @@ impl Surface { instance: Arc, metal_layer: IOSMetalLayer, object: Option>, - ) -> Result, SurfaceCreationError> { + ) -> Result, VulkanError> { Self::validate_from_ios(&instance, &metal_layer)?; Ok(Self::from_ios_unchecked(instance, metal_layer, object)?) @@ -637,13 +631,13 @@ impl Surface { fn validate_from_ios( instance: &Instance, _metal_layer: &IOSMetalLayer, - ) -> Result<(), SurfaceCreationError> { + ) -> Result<(), ValidationError> { if !instance.enabled_extensions().mvk_ios_surface { - return Err(SurfaceCreationError::RequirementNotMet { - required_for: "`Surface::from_ios`", + return Err(ValidationError { requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::InstanceExtension( "mvk_ios_surface", )])]), + ..Default::default() }); } @@ -704,7 +698,7 @@ impl Surface { instance: Arc, view: *const V, object: Option>, - ) -> Result, SurfaceCreationError> { + ) -> Result, VulkanError> { Self::validate_from_mac_os(&instance, view)?; Ok(Self::from_mac_os_unchecked(instance, view, object)?) @@ -714,13 +708,13 @@ impl Surface { fn validate_from_mac_os( instance: &Instance, _view: *const V, - ) -> Result<(), SurfaceCreationError> { + ) -> Result<(), ValidationError> { if !instance.enabled_extensions().mvk_macos_surface { - return Err(SurfaceCreationError::RequirementNotMet { - required_for: "`Surface::from_mac_os`", + return Err(ValidationError { requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::InstanceExtension( "mvk_macos_surface", )])]), + ..Default::default() }); } @@ -779,7 +773,7 @@ impl Surface { instance: Arc, layer: *const L, object: Option>, - ) -> Result, SurfaceCreationError> { + ) -> Result, VulkanError> { Self::validate_from_metal(&instance, layer)?; Ok(Self::from_metal_unchecked(instance, layer, object)?) @@ -788,13 +782,13 @@ impl Surface { fn validate_from_metal( instance: &Instance, _layer: *const L, - ) -> Result<(), SurfaceCreationError> { + ) -> Result<(), ValidationError> { if !instance.enabled_extensions().ext_metal_surface { - return Err(SurfaceCreationError::RequirementNotMet { - required_for: "`Surface::from_metal`", + return Err(ValidationError { requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::InstanceExtension( "ext_metal_surface", )])]), + ..Default::default() }); } @@ -848,7 +842,7 @@ impl Surface { context: *const C, window: *const W, object: Option>, - ) -> Result, SurfaceCreationError> { + ) -> Result, VulkanError> { Self::validate_from_qnx_screen(&instance, context, window)?; Ok(Self::from_qnx_screen_unchecked( @@ -860,13 +854,13 @@ impl Surface { instance: &Instance, _context: *const C, _window: *const W, - ) -> Result<(), SurfaceCreationError> { + ) -> Result<(), ValidationError> { if !instance.enabled_extensions().qnx_screen_surface { - return Err(SurfaceCreationError::RequirementNotMet { - required_for: "`Surface::from_qnx_screen`", + return Err(ValidationError { requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::InstanceExtension( "qnx_screen_surface", )])]), + ..Default::default() }); } @@ -926,22 +920,19 @@ impl Surface { instance: Arc, window: *const W, object: Option>, - ) -> Result, SurfaceCreationError> { + ) -> Result, VulkanError> { Self::validate_from_vi(&instance, window)?; Ok(Self::from_vi_unchecked(instance, window, object)?) } - fn validate_from_vi( - instance: &Instance, - _window: *const W, - ) -> Result<(), SurfaceCreationError> { + fn validate_from_vi(instance: &Instance, _window: *const W) -> Result<(), ValidationError> { if !instance.enabled_extensions().nn_vi_surface { - return Err(SurfaceCreationError::RequirementNotMet { - required_for: "`Surface::from_vi`", + return Err(ValidationError { requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::InstanceExtension( "nn_vi_surface", )])]), + ..Default::default() }); } @@ -1000,7 +991,7 @@ impl Surface { display: *const D, surface: *const S, object: Option>, - ) -> Result, SurfaceCreationError> { + ) -> Result, VulkanError> { Self::validate_from_wayland(&instance, display, surface)?; Ok(Self::from_wayland_unchecked( @@ -1012,13 +1003,13 @@ impl Surface { instance: &Instance, _display: *const D, _surface: *const S, - ) -> Result<(), SurfaceCreationError> { + ) -> Result<(), ValidationError> { if !instance.enabled_extensions().khr_wayland_surface { - return Err(SurfaceCreationError::RequirementNotMet { - required_for: "`Surface::from_wayland`", + return Err(ValidationError { requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::InstanceExtension( "khr_wayland_surface", )])]), + ..Default::default() }); } @@ -1082,7 +1073,7 @@ impl Surface { hinstance: *const I, hwnd: *const W, object: Option>, - ) -> Result, SurfaceCreationError> { + ) -> Result, VulkanError> { Self::validate_from_win32(&instance, hinstance, hwnd)?; Ok(Self::from_win32_unchecked( @@ -1094,13 +1085,13 @@ impl Surface { instance: &Instance, _hinstance: *const I, _hwnd: *const W, - ) -> Result<(), SurfaceCreationError> { + ) -> Result<(), ValidationError> { if !instance.enabled_extensions().khr_win32_surface { - return Err(SurfaceCreationError::RequirementNotMet { - required_for: "`Surface::from_win32`", + return Err(ValidationError { requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::InstanceExtension( "khr_win32_surface", )])]), + ..Default::default() }); } @@ -1164,7 +1155,7 @@ impl Surface { connection: *const C, window: ash::vk::xcb_window_t, object: Option>, - ) -> Result, SurfaceCreationError> { + ) -> Result, VulkanError> { Self::validate_from_xcb(&instance, connection, window)?; Ok(Self::from_xcb_unchecked( @@ -1176,13 +1167,13 @@ impl Surface { instance: &Instance, _connection: *const C, _window: ash::vk::xcb_window_t, - ) -> Result<(), SurfaceCreationError> { + ) -> Result<(), ValidationError> { if !instance.enabled_extensions().khr_xcb_surface { - return Err(SurfaceCreationError::RequirementNotMet { - required_for: "`Surface::from_xcb`", + return Err(ValidationError { requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::InstanceExtension( "khr_xcb_surface", )])]), + ..Default::default() }); } @@ -1246,7 +1237,7 @@ impl Surface { display: *const D, window: ash::vk::Window, object: Option>, - ) -> Result, SurfaceCreationError> { + ) -> Result, VulkanError> { Self::validate_from_xlib(&instance, display, window)?; Ok(Self::from_xlib_unchecked( @@ -1258,13 +1249,13 @@ impl Surface { instance: &Instance, _display: *const D, _window: ash::vk::Window, - ) -> Result<(), SurfaceCreationError> { + ) -> Result<(), ValidationError> { if !instance.enabled_extensions().khr_xlib_surface { - return Err(SurfaceCreationError::RequirementNotMet { - required_for: "`Surface::from_xlib`", + return Err(ValidationError { requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::InstanceExtension( "khr_xlib_surface", )])]), + ..Default::default() }); } @@ -1379,7 +1370,6 @@ impl Debug for Surface { instance, api, object: _, - has_swapchain, .. } = self; @@ -1388,18 +1378,10 @@ impl Debug for Surface { .field("instance", instance) .field("api", api) .field("window", &()) - .field("has_swapchain", &has_swapchain) .finish() } } -unsafe impl SurfaceSwapchainLock for Surface { - #[inline] - fn flag(&self) -> &AtomicBool { - &self.has_swapchain - } -} - /// Get sublayer from iOS main view (ui_view). The sublayer is created as `CAMetalLayer`. #[cfg(target_os = "ios")] unsafe fn get_metal_layer_ios(ui_view: *mut std::ffi::c_void) -> IOSMetalLayer { @@ -1447,63 +1429,6 @@ unsafe fn get_metal_layer_macos(ns_view: *mut std::ffi::c_void) -> *mut Object { } } -/// Error that can happen when creating a surface. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum SurfaceCreationError { - /// Not enough memory. - OomError(OomError), - - RequirementNotMet { - required_for: &'static str, - requires_one_of: RequiresOneOf, - }, -} - -impl Error for SurfaceCreationError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - match self { - SurfaceCreationError::OomError(err) => Some(err), - _ => None, - } - } -} - -impl Display for SurfaceCreationError { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { - match self { - Self::OomError(_) => write!(f, "not enough memory available"), - Self::RequirementNotMet { - required_for, - requires_one_of, - } => write!( - f, - "a requirement was not met for: {}; requires one of: {}", - required_for, requires_one_of, - ), - } - } -} - -impl From for SurfaceCreationError { - fn from(err: OomError) -> SurfaceCreationError { - SurfaceCreationError::OomError(err) - } -} - -impl From for SurfaceCreationError { - fn from(err: RuntimeError) -> SurfaceCreationError { - match err { - err @ RuntimeError::OutOfHostMemory => { - SurfaceCreationError::OomError(OomError::from(err)) - } - err @ RuntimeError::OutOfDeviceMemory => { - SurfaceCreationError::OomError(OomError::from(err)) - } - _ => panic!("unexpected error: {:?}", err), - } - } -} - /// The windowing API that was used to construct a surface. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] #[non_exhaustive] @@ -1882,8 +1807,21 @@ vulkan_enum! { /// [`PhysicalDevice::surface_formats`]: crate::device::physical::PhysicalDevice::surface_formats #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct SurfaceInfo { + /// If this is `Some`, the + /// [`ext_surface_maintenance1`](crate::instance::InstanceExtensions::ext_surface_maintenance1) + /// extension must be enabled on the instance. + pub present_mode: Option, + + /// If this is not [`FullScreenExclusive::Default`], the + /// [`ext_full_screen_exclusive`](crate::device::DeviceExtensions::ext_full_screen_exclusive) + /// extension must be supported by the physical device. pub full_screen_exclusive: FullScreenExclusive, + + /// If `full_screen_exclusive` is [`FullScreenExclusive::ApplicationControlled`], and the + /// surface being queried is a Win32 surface, then this must be `Some`. Otherwise, it must be + /// `None`. pub win32_monitor: Option, + pub _ne: crate::NonExhaustive, } @@ -1891,6 +1829,7 @@ impl Default for SurfaceInfo { #[inline] fn default() -> Self { Self { + present_mode: None, full_screen_exclusive: FullScreenExclusive::Default, win32_monitor: None, _ne: crate::NonExhaustive(()), @@ -1901,15 +1840,41 @@ impl Default for SurfaceInfo { impl SurfaceInfo { pub(crate) fn validate(&self, physical_device: &PhysicalDevice) -> Result<(), ValidationError> { let &Self { + present_mode, full_screen_exclusive, win32_monitor: _, _ne: _, } = self; - if !physical_device - .supported_extensions() - .ext_full_screen_exclusive - && full_screen_exclusive != FullScreenExclusive::Default + if let Some(present_mode) = present_mode { + if !physical_device + .instance() + .enabled_extensions() + .ext_surface_maintenance1 + { + return Err(ValidationError { + context: "present_mode".into(), + problem: "is `Some`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[ + Requires::InstanceExtension("ext_surface_maintenance1"), + ])]), + ..Default::default() + }); + } + + present_mode + .validate_physical_device(physical_device) + .map_err(|err| ValidationError { + context: "present_mode".into(), + vuids: &["VUID-VkSurfacePresentModeEXT-presentMode-parameter"], + ..ValidationError::from_requirement(err) + })?; + } + + if full_screen_exclusive != FullScreenExclusive::Default + && !physical_device + .supported_extensions() + .ext_full_screen_exclusive { return Err(ValidationError { context: "full_screen_exclusive".into(), @@ -1972,8 +1937,9 @@ pub struct SurfaceCapabilities { /// you may still get out of memory errors. pub max_image_count: Option, - /// The current dimensions of the surface. `None` means that the surface's dimensions will - /// depend on the dimensions of the swapchain that you are going to create. + /// The current dimensions of the surface. + /// + /// `None` means that the surface's dimensions will depend on the dimensions of the swapchain. pub current_extent: Option<[u32; 2]>, /// Minimum width and height of a swapchain that uses this surface. @@ -1998,6 +1964,49 @@ pub struct SurfaceCapabilities { /// the `color_attachment` usage is guaranteed to be supported. pub supported_usage_flags: ImageUsage, + /// When [`SurfaceInfo::present_mode`] is provided, + /// lists that present mode and any modes that are compatible with that present mode. + /// + /// If [`SurfaceInfo::present_mode`] was not provided, the value will be empty. + pub compatible_present_modes: SmallVec<[PresentMode; PresentMode::COUNT]>, + + /// When [`SurfaceInfo::present_mode`] is provided, + /// the supported present scaling modes for the queried present mode. + /// + /// If [`SurfaceInfo::present_mode`] was not provided, the value will be empty. + pub supported_present_scaling: PresentScalingFlags, + + /// When [`SurfaceInfo::present_mode`] is provided, + /// the supported present gravity modes, horizontally and vertically, + /// for the queried present mode. + /// + /// If [`SurfaceInfo::present_mode`] was not provided, both values will be empty. + pub supported_present_gravity: [PresentGravityFlags; 2], + + /// When [`SurfaceInfo::present_mode`] is provided, + /// the smallest allowed extent for a swapchain, if it uses the queried present mode, and + /// one of the scaling modes in `supported_present_scaling`. + /// + /// This is never greater than [`SurfaceCapabilities::min_image_extent`]. + /// + /// `None` means that the surface's dimensions will depend on the dimensions of the swapchain. + /// + /// If [`SurfaceInfo::present_mode`] was not provided, this is will be equal to + /// `min_image_extent`. + pub min_scaled_image_extent: Option<[u32; 2]>, + + /// When [`SurfaceInfo::present_mode`] is provided, + /// the largest allowed extent for a swapchain, if it uses the queried present mode, and + /// one of the scaling modes in `supported_present_scaling`. + /// + /// This is never less than [`SurfaceCapabilities::max_image_extent`]. + /// + /// `None` means that the surface's dimensions will depend on the dimensions of the swapchain. + /// + /// If [`SurfaceInfo::present_mode`] was not provided, this is will be equal to + /// `max_image_extent`. + pub max_scaled_image_extent: Option<[u32; 2]>, + /// Whether creating a protected swapchain is supported. pub supports_protected: bool, @@ -2008,8 +2017,7 @@ pub struct SurfaceCapabilities { #[cfg(test)] mod tests { use crate::{ - swapchain::{Surface, SurfaceCreationError}, - Requires, RequiresAllOf, RequiresOneOf, + swapchain::Surface, Requires, RequiresAllOf, RequiresOneOf, ValidationError, VulkanError, }; use std::ptr; @@ -2017,11 +2025,11 @@ mod tests { fn khr_win32_surface_ext_missing() { let instance = instance!(); match unsafe { Surface::from_win32(instance, ptr::null::(), ptr::null::(), None) } { - Err(SurfaceCreationError::RequirementNotMet { + Err(VulkanError::ValidationError(ValidationError { requires_one_of: RequiresOneOf([RequiresAllOf([Requires::InstanceExtension("khr_win32_surface")])]), .. - }) => (), + })) => (), _ => panic!(), } } @@ -2030,11 +2038,11 @@ mod tests { fn khr_xcb_surface_ext_missing() { let instance = instance!(); match unsafe { Surface::from_xcb(instance, ptr::null::(), 0, None) } { - Err(SurfaceCreationError::RequirementNotMet { + Err(VulkanError::ValidationError(ValidationError { requires_one_of: RequiresOneOf([RequiresAllOf([Requires::InstanceExtension("khr_xcb_surface")])]), .. - }) => (), + })) => (), _ => panic!(), } } @@ -2043,11 +2051,11 @@ mod tests { fn khr_xlib_surface_ext_missing() { let instance = instance!(); match unsafe { Surface::from_xlib(instance, ptr::null::(), 0, None) } { - Err(SurfaceCreationError::RequirementNotMet { + Err(VulkanError::ValidationError(ValidationError { requires_one_of: RequiresOneOf([RequiresAllOf([Requires::InstanceExtension("khr_xlib_surface")])]), .. - }) => (), + })) => (), _ => panic!(), } } @@ -2057,11 +2065,11 @@ mod tests { let instance = instance!(); match unsafe { Surface::from_wayland(instance, ptr::null::(), ptr::null::(), None) } { - Err(SurfaceCreationError::RequirementNotMet { + Err(VulkanError::ValidationError(ValidationError { requires_one_of: RequiresOneOf([RequiresAllOf([Requires::InstanceExtension("khr_wayland_surface")])]), .. - }) => (), + })) => (), _ => panic!(), } } @@ -2070,11 +2078,11 @@ mod tests { fn khr_android_surface_ext_missing() { let instance = instance!(); match unsafe { Surface::from_android(instance, ptr::null::(), None) } { - Err(SurfaceCreationError::RequirementNotMet { + Err(VulkanError::ValidationError(ValidationError { requires_one_of: RequiresOneOf([RequiresAllOf([Requires::InstanceExtension("khr_android_surface")])]), .. - }) => (), + })) => (), _ => panic!(), } } diff --git a/vulkano/src/swapchain/swapchain.rs b/vulkano/src/swapchain/swapchain.rs deleted file mode 100644 index b2d4f8f5..00000000 --- a/vulkano/src/swapchain/swapchain.rs +++ /dev/null @@ -1,2271 +0,0 @@ -// Copyright (c) 2016 The vulkano developers -// Licensed under the Apache License, Version 2.0 -// or the MIT -// license , -// at your option. All files in the project carrying such -// notice may not be copied, modified, or distributed except -// according to those terms. - -use super::{ - ColorSpace, CompositeAlpha, CompositeAlphas, PresentMode, Surface, SurfaceTransform, - SurfaceTransforms, SwapchainPresentInfo, -}; -use crate::{ - buffer::Buffer, - device::{Device, DeviceOwned, Queue}, - format::Format, - image::{ - sys::Image, ImageFormatInfo, ImageLayout, ImageTiling, ImageType, ImageUsage, - SwapchainImage, - }, - macros::{impl_id_counter, vulkan_enum}, - swapchain::{PresentInfo, SurfaceApi, SurfaceInfo, SurfaceSwapchainLock}, - sync::{ - fence::{Fence, FenceError}, - future::{AccessCheckError, AccessError, FlushError, GpuFuture, SubmitAnyBuilder}, - semaphore::{Semaphore, SemaphoreError}, - Sharing, - }, - DeviceSize, OomError, RequirementNotMet, Requires, RequiresAllOf, RequiresOneOf, RuntimeError, - VulkanObject, -}; -use parking_lot::Mutex; -use smallvec::{smallvec, SmallVec}; -use std::{ - error::Error, - fmt::{Debug, Display, Error as FmtError, Formatter}, - mem::MaybeUninit, - num::NonZeroU64, - ops::Range, - ptr, - sync::{ - atomic::{AtomicBool, AtomicU64, Ordering}, - Arc, - }, - thread, - time::Duration, -}; - -/// Contains the swapping system and the images that can be shown on a surface. -pub struct Swapchain { - handle: ash::vk::SwapchainKHR, - device: Arc, - surface: Arc, - id: NonZeroU64, - - min_image_count: u32, - image_format: Format, - image_color_space: ColorSpace, - image_extent: [u32; 2], - image_array_layers: u32, - image_usage: ImageUsage, - image_sharing: Sharing>, - pre_transform: SurfaceTransform, - composite_alpha: CompositeAlpha, - present_mode: PresentMode, - clipped: bool, - full_screen_exclusive: FullScreenExclusive, - win32_monitor: Option, - prev_present_id: AtomicU64, - - // Whether full-screen exclusive is currently held. - full_screen_exclusive_held: AtomicBool, - - // The images of this swapchain. - images: Vec, - - // If true, that means we have tried to use this swapchain to recreate a new swapchain. The - // current swapchain can no longer be used for anything except presenting already-acquired - // images. - // - // We use a `Mutex` instead of an `AtomicBool` because we want to keep that locked while - // we acquire the image. - retired: Mutex, -} - -#[derive(Debug)] -struct ImageEntry { - handle: ash::vk::Image, - layout_initialized: AtomicBool, -} - -impl Swapchain { - /// Creates a new `Swapchain`. - /// - /// This function returns the swapchain plus a list of the images that belong to the - /// swapchain. The order in which the images are returned is important for the - /// `acquire_next_image` and `present` functions. - /// - /// # Panics - /// - /// - Panics if the device and the surface don't belong to the same instance. - /// - Panics if `create_info.usage` is empty. - /// - // TODO: isn't it unsafe to take the surface through an Arc when it comes to vulkano-win? - pub fn new( - device: Arc, - surface: Arc, - mut create_info: SwapchainCreateInfo, - ) -> Result<(Arc, Vec>), SwapchainCreationError> { - Self::validate(&device, &surface, &mut create_info)?; - - // Checking that the surface doesn't already have a swapchain. - if surface.flag().swap(true, Ordering::AcqRel) { - return Err(SwapchainCreationError::SurfaceInUse); - } - - let (handle, image_handles) = - unsafe { Self::create(&device, &surface, &create_info, None)? }; - - let SwapchainCreateInfo { - min_image_count, - image_format, - image_color_space, - image_extent, - image_array_layers, - image_usage, - image_sharing, - pre_transform, - composite_alpha, - present_mode, - clipped, - full_screen_exclusive, - win32_monitor, - _ne: _, - } = create_info; - - let swapchain = Arc::new(Swapchain { - handle, - device, - surface, - id: Self::next_id(), - min_image_count, - image_format: image_format.unwrap(), - image_color_space, - image_extent, - image_array_layers, - image_usage, - image_sharing, - pre_transform, - composite_alpha, - present_mode, - clipped, - full_screen_exclusive, - win32_monitor, - prev_present_id: Default::default(), - full_screen_exclusive_held: AtomicBool::new(false), - images: image_handles - .iter() - .map(|&handle| ImageEntry { - handle, - layout_initialized: AtomicBool::new(false), - }) - .collect(), - retired: Mutex::new(false), - }); - - let swapchain_images = image_handles - .into_iter() - .enumerate() - .map(|(image_index, handle)| unsafe { - SwapchainImage::from_handle(handle, swapchain.clone(), image_index as u32) - }) - .collect::>()?; - - Ok((swapchain, swapchain_images)) - } - - /// Creates a new swapchain from this one. - /// - /// Use this when a swapchain has become invalidated, such as due to window resizes. - /// - /// # Panics - /// - /// - Panics if `create_info.usage` is empty. - pub fn recreate( - self: &Arc, - mut create_info: SwapchainCreateInfo, - ) -> Result<(Arc, Vec>), SwapchainCreationError> { - Self::validate(&self.device, &self.surface, &mut create_info)?; - - { - let mut retired = self.retired.lock(); - - // The swapchain has already been used to create a new one. - if *retired { - return Err(SwapchainCreationError::SwapchainAlreadyRetired); - } else { - // According to the documentation of VkSwapchainCreateInfoKHR: - // - // > Upon calling vkCreateSwapchainKHR with a oldSwapchain that is not VK_NULL_HANDLE, - // > any images not acquired by the application may be freed by the implementation, - // > which may occur even if creation of the new swapchain fails. - // - // Therefore, we set retired to true and keep it to true even if the call to `vkCreateSwapchainKHR` below fails. - *retired = true; - } - } - - let (handle, image_handles) = - unsafe { Self::create(&self.device, &self.surface, &create_info, Some(self))? }; - - let full_screen_exclusive_held = - if self.full_screen_exclusive != FullScreenExclusive::ApplicationControlled { - false - } else { - self.full_screen_exclusive_held.load(Ordering::SeqCst) - }; - - let SwapchainCreateInfo { - min_image_count, - image_format, - image_color_space, - image_extent, - image_array_layers, - image_usage, - image_sharing, - pre_transform, - composite_alpha, - present_mode, - clipped, - full_screen_exclusive, - win32_monitor, - _ne: _, - } = create_info; - - let swapchain = Arc::new(Swapchain { - handle, - device: self.device.clone(), - surface: self.surface.clone(), - id: Self::next_id(), - min_image_count, - image_format: image_format.unwrap(), - image_color_space, - image_extent, - image_array_layers, - image_usage, - image_sharing, - pre_transform, - composite_alpha, - present_mode, - clipped, - full_screen_exclusive, - win32_monitor, - prev_present_id: Default::default(), - full_screen_exclusive_held: AtomicBool::new(full_screen_exclusive_held), - images: image_handles - .iter() - .map(|&handle| ImageEntry { - handle, - layout_initialized: AtomicBool::new(false), - }) - .collect(), - retired: Mutex::new(false), - }); - - let swapchain_images = image_handles - .into_iter() - .enumerate() - .map(|(image_index, handle)| unsafe { - SwapchainImage::from_handle(handle, swapchain.clone(), image_index as u32) - }) - .collect::>()?; - - Ok((swapchain, swapchain_images)) - } - - fn validate( - device: &Device, - surface: &Surface, - create_info: &mut SwapchainCreateInfo, - ) -> Result<(), SwapchainCreationError> { - let &mut SwapchainCreateInfo { - min_image_count, - ref mut image_format, - image_color_space, - ref mut image_extent, - image_array_layers, - image_usage, - ref mut image_sharing, - pre_transform, - composite_alpha, - present_mode, - clipped: _, - full_screen_exclusive, - win32_monitor, - _ne: _, - } = create_info; - - if !device.enabled_extensions().khr_swapchain { - return Err(SwapchainCreationError::RequirementNotMet { - required_for: "`Swapchain::new`", - requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension( - "khr_swapchain", - )])]), - }); - } - - assert_eq!(device.instance(), surface.instance()); - - // VUID-VkSwapchainCreateInfoKHR-imageColorSpace-parameter - image_color_space.validate_device(device)?; - - // VUID-VkSwapchainCreateInfoKHR-imageUsage-parameter - image_usage.validate_device(device)?; - - // VUID-VkSwapchainCreateInfoKHR-imageUsage-requiredbitmask - assert!(!image_usage.is_empty()); - - // VUID-VkSwapchainCreateInfoKHR-preTransform-parameter - pre_transform.validate_device(device)?; - - // VUID-VkSwapchainCreateInfoKHR-compositeAlpha-parameter - composite_alpha.validate_device(device)?; - - // VUID-VkSwapchainCreateInfoKHR-presentMode-parameter - present_mode.validate_device(device)?; - - if full_screen_exclusive != FullScreenExclusive::Default { - if !device.enabled_extensions().ext_full_screen_exclusive { - return Err(SwapchainCreationError::RequirementNotMet { - required_for: "`create_info.full_screen_exclusive` is not \ - `FullScreenExclusive::Default`", - requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension( - "ext_full_screen_exclusive", - )])]), - }); - } - - // VUID-VkSurfaceFullScreenExclusiveInfoEXT-fullScreenExclusive-parameter - full_screen_exclusive.validate_device(device)?; - } - - if surface.api() == SurfaceApi::Win32 - && full_screen_exclusive == FullScreenExclusive::ApplicationControlled - { - if win32_monitor.is_none() { - return Err(SwapchainCreationError::Win32MonitorInvalid); - } - } else { - if win32_monitor.is_some() { - return Err(SwapchainCreationError::Win32MonitorInvalid); - } - } - - // VUID-VkSwapchainCreateInfoKHR-surface-01270 - if !device - .active_queue_family_indices() - .iter() - .copied() - .any(|index| unsafe { - // Use unchecked, because all validation has been done above. - device - .physical_device() - .surface_support_unchecked(index, surface) - .unwrap_or_default() - }) - { - return Err(SwapchainCreationError::SurfaceNotSupported); - } - - *image_format = Some({ - // Use unchecked, because all validation has been done above. - let surface_formats = unsafe { - device.physical_device().surface_formats_unchecked( - surface, - SurfaceInfo { - full_screen_exclusive, - win32_monitor, - ..Default::default() - }, - )? - }; - - if let Some(format) = image_format { - // VUID-VkSwapchainCreateInfoKHR-imageFormat-parameter - format.validate_device(device)?; - - // VUID-VkSwapchainCreateInfoKHR-imageFormat-01273 - if !surface_formats - .into_iter() - .any(|(f, c)| f == *format && c == image_color_space) - { - return Err(SwapchainCreationError::FormatColorSpaceNotSupported); - } - *format - } else { - surface_formats - .into_iter() - .find_map(|(f, c)| { - (c == image_color_space - && [Format::R8G8B8A8_UNORM, Format::B8G8R8A8_UNORM].contains(&f)) - .then_some(f) - }) - .ok_or(SwapchainCreationError::FormatColorSpaceNotSupported)? - } - }); - - // Use unchecked, because all validation has been done above. - let surface_capabilities = unsafe { - device.physical_device().surface_capabilities_unchecked( - surface, - SurfaceInfo { - full_screen_exclusive, - win32_monitor, - ..Default::default() - }, - )? - }; - - // VUID-VkSwapchainCreateInfoKHR-minImageCount-01272 - // VUID-VkSwapchainCreateInfoKHR-presentMode-02839 - if min_image_count < surface_capabilities.min_image_count - || surface_capabilities - .max_image_count - .map_or(false, |c| min_image_count > c) - { - return Err(SwapchainCreationError::MinImageCountNotSupported { - provided: min_image_count, - min_supported: surface_capabilities.min_image_count, - max_supported: surface_capabilities.max_image_count, - }); - } - - if image_extent[0] == 0 || image_extent[1] == 0 { - *image_extent = surface_capabilities.current_extent.unwrap(); - } - - // VUID-VkSwapchainCreateInfoKHR-imageExtent-01274 - if image_extent[0] < surface_capabilities.min_image_extent[0] - || image_extent[1] < surface_capabilities.min_image_extent[1] - || image_extent[0] > surface_capabilities.max_image_extent[0] - || image_extent[1] > surface_capabilities.max_image_extent[1] - { - return Err(SwapchainCreationError::ImageExtentNotSupported { - provided: *image_extent, - min_supported: surface_capabilities.min_image_extent, - max_supported: surface_capabilities.max_image_extent, - }); - } - - // VUID-VkSwapchainCreateInfoKHR-imageExtent-01689 - // On some platforms, dimensions of zero-length can occur by minimizing the surface. - if image_extent.contains(&0) { - return Err(SwapchainCreationError::ImageExtentZeroLengthDimensions); - } - - // VUID-VkSwapchainCreateInfoKHR-imageArrayLayers-01275 - if image_array_layers == 0 - || image_array_layers > surface_capabilities.max_image_array_layers - { - return Err(SwapchainCreationError::ImageArrayLayersNotSupported { - provided: image_array_layers, - max_supported: surface_capabilities.max_image_array_layers, - }); - } - - // VUID-VkSwapchainCreateInfoKHR-presentMode-01427 - if (ash::vk::ImageUsageFlags::from(image_usage) - & ash::vk::ImageUsageFlags::from(surface_capabilities.supported_usage_flags)) - != ash::vk::ImageUsageFlags::from(image_usage) - { - return Err(SwapchainCreationError::ImageUsageNotSupported { - provided: image_usage, - supported: surface_capabilities.supported_usage_flags, - }); - } - - match image_sharing { - Sharing::Exclusive => (), - Sharing::Concurrent(queue_family_indices) => { - // VUID-VkSwapchainCreateInfoKHR-imageSharingMode-01278 - // VUID-VkSwapchainCreateInfoKHR-imageSharingMode-01428 - queue_family_indices.sort_unstable(); - queue_family_indices.dedup(); - assert!(queue_family_indices.len() >= 2); - - for &queue_family_index in queue_family_indices.iter() { - // VUID-VkSwapchainCreateInfoKHR-imageSharingMode-01428 - if queue_family_index - >= device.physical_device().queue_family_properties().len() as u32 - { - return Err( - SwapchainCreationError::ImageSharingQueueFamilyIndexOutOfRange { - queue_family_index, - queue_family_count: device - .physical_device() - .queue_family_properties() - .len() - as u32, - }, - ); - } - } - } - }; - - // VUID-VkSwapchainCreateInfoKHR-preTransform-01279 - if !surface_capabilities - .supported_transforms - .contains_enum(pre_transform) - { - return Err(SwapchainCreationError::PreTransformNotSupported { - provided: pre_transform, - supported: surface_capabilities.supported_transforms, - }); - } - - // VUID-VkSwapchainCreateInfoKHR-compositeAlpha-01280 - if !surface_capabilities - .supported_composite_alpha - .contains_enum(composite_alpha) - { - return Err(SwapchainCreationError::CompositeAlphaNotSupported { - provided: composite_alpha, - supported: surface_capabilities.supported_composite_alpha, - }); - } - - // VUID-VkSwapchainCreateInfoKHR-presentMode-01281 - // Use unchecked, because all validation has been done above. - if !unsafe { - device - .physical_device() - .surface_present_modes_unchecked(surface)? - } - .any(|mode| mode == present_mode) - { - return Err(SwapchainCreationError::PresentModeNotSupported); - } - - // VUID-VkSwapchainCreateInfoKHR-imageFormat-01778 - // Use unchecked, because all validation has been done above. - if unsafe { - device - .physical_device() - .image_format_properties_unchecked(ImageFormatInfo { - format: *image_format, - image_type: ImageType::Dim2d, - tiling: ImageTiling::Optimal, - usage: image_usage, - ..Default::default() - })? - } - .is_none() - { - return Err(SwapchainCreationError::ImageFormatPropertiesNotSupported); - } - - Ok(()) - } - - unsafe fn create( - device: &Device, - surface: &Surface, - create_info: &SwapchainCreateInfo, - old_swapchain: Option<&Swapchain>, - ) -> Result<(ash::vk::SwapchainKHR, Vec), SwapchainCreationError> { - let &SwapchainCreateInfo { - min_image_count, - image_format, - image_color_space, - image_extent, - image_array_layers, - image_usage, - ref image_sharing, - pre_transform, - composite_alpha, - present_mode, - clipped, - full_screen_exclusive, - win32_monitor, - _ne: _, - } = create_info; - - let (image_sharing_mode, queue_family_index_count, p_queue_family_indices) = - match image_sharing { - Sharing::Exclusive => (ash::vk::SharingMode::EXCLUSIVE, 0, ptr::null()), - Sharing::Concurrent(ref ids) => ( - ash::vk::SharingMode::CONCURRENT, - ids.len() as u32, - ids.as_ptr(), - ), - }; - - let mut info_vk = ash::vk::SwapchainCreateInfoKHR { - flags: ash::vk::SwapchainCreateFlagsKHR::empty(), - surface: surface.handle(), - min_image_count, - image_format: image_format.unwrap().into(), - image_color_space: image_color_space.into(), - image_extent: ash::vk::Extent2D { - width: image_extent[0], - height: image_extent[1], - }, - image_array_layers, - image_usage: image_usage.into(), - image_sharing_mode, - queue_family_index_count, - p_queue_family_indices, - pre_transform: pre_transform.into(), - composite_alpha: composite_alpha.into(), - present_mode: present_mode.into(), - clipped: clipped as ash::vk::Bool32, - old_swapchain: old_swapchain.map_or(ash::vk::SwapchainKHR::null(), |os| os.handle), - ..Default::default() - }; - let mut surface_full_screen_exclusive_info_vk = None; - let mut surface_full_screen_exclusive_win32_info_vk = None; - - if full_screen_exclusive != FullScreenExclusive::Default { - let next = surface_full_screen_exclusive_info_vk.insert( - ash::vk::SurfaceFullScreenExclusiveInfoEXT { - full_screen_exclusive: full_screen_exclusive.into(), - ..Default::default() - }, - ); - - next.p_next = info_vk.p_next as *mut _; - info_vk.p_next = next as *const _ as *const _; - } - - if let Some(Win32Monitor(hmonitor)) = win32_monitor { - let next = surface_full_screen_exclusive_win32_info_vk.insert( - ash::vk::SurfaceFullScreenExclusiveWin32InfoEXT { - hmonitor, - ..Default::default() - }, - ); - - next.p_next = info_vk.p_next as *mut _; - info_vk.p_next = next as *const _ as *const _; - } - - let fns = device.fns(); - - let handle = { - let mut output = MaybeUninit::uninit(); - (fns.khr_swapchain.create_swapchain_khr)( - device.handle(), - &info_vk, - ptr::null(), - output.as_mut_ptr(), - ) - .result() - .map_err(RuntimeError::from)?; - output.assume_init() - }; - - let image_handles = loop { - let mut count = 0; - (fns.khr_swapchain.get_swapchain_images_khr)( - device.handle(), - handle, - &mut count, - ptr::null_mut(), - ) - .result() - .map_err(RuntimeError::from)?; - - let mut images = Vec::with_capacity(count as usize); - let result = (fns.khr_swapchain.get_swapchain_images_khr)( - device.handle(), - handle, - &mut count, - images.as_mut_ptr(), - ); - - match result { - ash::vk::Result::SUCCESS => { - images.set_len(count as usize); - break images; - } - ash::vk::Result::INCOMPLETE => (), - err => return Err(RuntimeError::from(err).into()), - } - }; - - Ok((handle, image_handles)) - } - - /// Returns the creation parameters of the swapchain. - #[inline] - pub fn create_info(&self) -> SwapchainCreateInfo { - SwapchainCreateInfo { - min_image_count: self.min_image_count, - image_format: Some(self.image_format), - image_color_space: self.image_color_space, - image_extent: self.image_extent, - image_array_layers: self.image_array_layers, - image_usage: self.image_usage, - image_sharing: self.image_sharing.clone(), - pre_transform: self.pre_transform, - composite_alpha: self.composite_alpha, - present_mode: self.present_mode, - clipped: self.clipped, - full_screen_exclusive: self.full_screen_exclusive, - win32_monitor: self.win32_monitor, - _ne: crate::NonExhaustive(()), - } - } - - /// Returns the saved Surface, from the Swapchain creation. - #[inline] - pub fn surface(&self) -> &Arc { - &self.surface - } - - /// If `image` is one of the images of this swapchain, returns its index within the swapchain. - #[inline] - pub fn index_of_image(&self, image: &Image) -> Option { - self.images - .iter() - .position(|entry| entry.handle == image.handle()) - .map(|i| i as u32) - } - - /// Returns the number of images of the swapchain. - #[inline] - pub fn image_count(&self) -> u32 { - self.images.len() as u32 - } - - /// Returns the format of the images of the swapchain. - #[inline] - pub fn image_format(&self) -> Format { - self.image_format - } - - /// Returns the color space of the images of the swapchain. - #[inline] - pub fn image_color_space(&self) -> ColorSpace { - self.image_color_space - } - - /// Returns the extent of the images of the swapchain. - #[inline] - pub fn image_extent(&self) -> [u32; 2] { - self.image_extent - } - - /// Returns the number of array layers of the images of the swapchain. - #[inline] - pub fn image_array_layers(&self) -> u32 { - self.image_array_layers - } - - /// Returns the usage of the images of the swapchain. - #[inline] - pub fn image_usage(&self) -> ImageUsage { - self.image_usage - } - - /// Returns the sharing of the images of the swapchain. - #[inline] - pub fn image_sharing(&self) -> &Sharing> { - &self.image_sharing - } - - #[inline] - pub(crate) unsafe fn full_screen_exclusive_held(&self) -> &AtomicBool { - &self.full_screen_exclusive_held - } - - #[inline] - pub(crate) unsafe fn try_claim_present_id(&self, present_id: NonZeroU64) -> bool { - let present_id = u64::from(present_id); - self.prev_present_id.fetch_max(present_id, Ordering::SeqCst) < present_id - } - - /// Returns the pre-transform that was passed when creating the swapchain. - #[inline] - pub fn pre_transform(&self) -> SurfaceTransform { - self.pre_transform - } - - /// Returns the alpha mode that was passed when creating the swapchain. - #[inline] - pub fn composite_alpha(&self) -> CompositeAlpha { - self.composite_alpha - } - - /// Returns the present mode that was passed when creating the swapchain. - #[inline] - pub fn present_mode(&self) -> PresentMode { - self.present_mode - } - - /// Returns the value of `clipped` that was passed when creating the swapchain. - #[inline] - pub fn clipped(&self) -> bool { - self.clipped - } - - /// Returns the value of 'full_screen_exclusive` that was passed when creating the swapchain. - #[inline] - pub fn full_screen_exclusive(&self) -> FullScreenExclusive { - self.full_screen_exclusive - } - - /// Acquires full-screen exclusivity. - /// - /// The swapchain must have been created with [`FullScreenExclusive::ApplicationControlled`], - /// and must not already hold full-screen exclusivity. Full-screen exclusivity is held until - /// either the `release_full_screen_exclusive` is called, or if any of the the other `Swapchain` - /// functions return `FullScreenExclusiveLost`. - #[inline] - pub fn acquire_full_screen_exclusive(&self) -> Result<(), FullScreenExclusiveError> { - if self.full_screen_exclusive != FullScreenExclusive::ApplicationControlled { - return Err(FullScreenExclusiveError::NotApplicationControlled); - } - - if self.full_screen_exclusive_held.swap(true, Ordering::SeqCst) { - return Err(FullScreenExclusiveError::DoubleAcquire); - } - - unsafe { - let fns = self.device.fns(); - (fns.ext_full_screen_exclusive - .acquire_full_screen_exclusive_mode_ext)( - self.device.handle(), self.handle - ) - .result() - .map_err(RuntimeError::from)?; - } - - Ok(()) - } - - /// Releases full-screen exclusivity. - /// - /// The swapchain must have been created with [`FullScreenExclusive::ApplicationControlled`], - /// and must currently hold full-screen exclusivity. - #[inline] - pub fn release_full_screen_exclusive(&self) -> Result<(), FullScreenExclusiveError> { - if self.full_screen_exclusive != FullScreenExclusive::ApplicationControlled { - return Err(FullScreenExclusiveError::NotApplicationControlled); - } - - if !self - .full_screen_exclusive_held - .swap(false, Ordering::SeqCst) - { - return Err(FullScreenExclusiveError::DoubleRelease); - } - - unsafe { - let fns = self.device.fns(); - (fns.ext_full_screen_exclusive - .release_full_screen_exclusive_mode_ext)( - self.device.handle(), self.handle - ) - .result() - .map_err(RuntimeError::from)?; - } - - Ok(()) - } - - /// `FullScreenExclusive::AppControlled` is not the active full-screen exclusivity mode, - /// then this function will always return false. If true is returned the swapchain - /// is in `FullScreenExclusive::AppControlled` full-screen exclusivity mode and exclusivity - /// is currently acquired. - #[inline] - pub fn is_full_screen_exclusive(&self) -> bool { - if self.full_screen_exclusive != FullScreenExclusive::ApplicationControlled { - false - } else { - self.full_screen_exclusive_held.load(Ordering::SeqCst) - } - } - - // This method is necessary to allow `SwapchainImage`s to signal when they have been - // transitioned out of their initial `undefined` image layout. - // - // See the `ImageAccess::layout_initialized` method documentation for more details. - pub(crate) fn image_layout_initialized(&self, image_index: u32) { - let image_entry = self.images.get(image_index as usize); - if let Some(image_entry) = image_entry { - image_entry - .layout_initialized - .store(true, Ordering::Relaxed); - } - } - - pub(crate) fn is_image_layout_initialized(&self, image_index: u32) -> bool { - let image_entry = self.images.get(image_index as usize); - if let Some(image_entry) = image_entry { - image_entry.layout_initialized.load(Ordering::Relaxed) - } else { - false - } - } -} - -impl Drop for Swapchain { - #[inline] - fn drop(&mut self) { - unsafe { - let fns = self.device.fns(); - (fns.khr_swapchain.destroy_swapchain_khr)( - self.device.handle(), - self.handle, - ptr::null(), - ); - self.surface.flag().store(false, Ordering::Release); - } - } -} - -unsafe impl VulkanObject for Swapchain { - type Handle = ash::vk::SwapchainKHR; - - #[inline] - fn handle(&self) -> Self::Handle { - self.handle - } -} - -unsafe impl DeviceOwned for Swapchain { - #[inline] - fn device(&self) -> &Arc { - &self.device - } -} - -impl_id_counter!(Swapchain); - -impl Debug for Swapchain { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { - let Self { - handle, - device, - surface, - id: _, - min_image_count, - image_format, - image_color_space, - image_extent, - image_array_layers, - image_usage, - image_sharing, - pre_transform, - composite_alpha, - present_mode, - clipped, - full_screen_exclusive, - win32_monitor, - prev_present_id, - full_screen_exclusive_held, - images, - retired, - } = self; - - f.debug_struct("Swapchain") - .field("handle", &handle) - .field("device", &device.handle()) - .field("surface", &surface.handle()) - .field("min_image_count", &min_image_count) - .field("image_format", &image_format) - .field("image_color_space", &image_color_space) - .field("image_extent", &image_extent) - .field("image_array_layers", &image_array_layers) - .field("image_usage", &image_usage) - .field("image_sharing", &image_sharing) - .field("pre_transform", &pre_transform) - .field("composite_alpha", &composite_alpha) - .field("present_mode", &present_mode) - .field("clipped", &clipped) - .field("full_screen_exclusive", &full_screen_exclusive) - .field("win32_monitor", &win32_monitor) - .field("prev_present_id", &prev_present_id) - .field("full_screen_exclusive_held", &full_screen_exclusive_held) - .field("images", &images) - .field("retired", &retired) - .finish() - } -} - -/// Parameters to create a new `Swapchain`. -/// -/// Many of the values here must be supported by the physical device. -/// [`PhysicalDevice`](crate::device::physical::PhysicalDevice) has several -/// methods to query what is supported. -#[derive(Clone, Debug)] -pub struct SwapchainCreateInfo { - /// The minimum number of images that will be created. - /// - /// The implementation is allowed to create more than this number, but never less. - /// - /// The default value is `2`. - pub min_image_count: u32, - - /// The format of the created images. - /// - /// If set to `None`, [`Format::R8G8B8A8_UNORM`] or [`Format::B8G8R8A8_UNORM`] will be selected, - /// based on which is supported by the surface. - /// - /// The default value is `None`. - pub image_format: Option, - - /// The color space of the created images. - /// - /// The default value is [`ColorSpace::SrgbNonLinear`]. - pub image_color_space: ColorSpace, - - /// The extent of the created images. - /// - /// If any of the values is 0, the value of - /// [`SurfaceCapabilities::current_extent`](crate::swapchain::SurfaceCapabilities) will be used. - /// - /// The default value is `[0, 0]`. - pub image_extent: [u32; 2], - - /// The number of array layers of the created images. - /// - /// The default value is `1`. - pub image_array_layers: u32, - - /// How the created images will be used. - /// - /// The default value is [`ImageUsage::empty()`], which must be overridden. - pub image_usage: ImageUsage, - - /// Whether the created images can be shared across multiple queues, or are limited to a single - /// queue. - /// - /// The default value is [`Sharing::Exclusive`]. - pub image_sharing: Sharing>, - - /// The transform that should be applied to an image before it is presented. - /// - /// The default value is [`SurfaceTransform::Identity`]. - pub pre_transform: SurfaceTransform, - - /// How alpha values of the pixels in the image are to be treated. - /// - /// The default value is [`CompositeAlpha::Opaque`]. - pub composite_alpha: CompositeAlpha, - - /// How the swapchain should behave when multiple images are waiting in the queue to be - /// presented. - /// - /// The default is [`PresentMode::Fifo`]. - pub present_mode: PresentMode, - - /// Whether the implementation is allowed to discard rendering operations that affect regions of - /// the surface which aren't visible. This is important to take into account if your fragment - /// shader has side-effects or if you want to read back the content of the image afterwards. - /// - /// The default value is `true`. - pub clipped: bool, - - /// How full-screen exclusivity is to be handled. - /// - /// If set to anything other than [`FullScreenExclusive::Default`], then the - /// [`ext_full_screen_exclusive`](crate::device::DeviceExtensions::ext_full_screen_exclusive) - /// extension must be enabled on the device. - /// - /// The default value is [`FullScreenExclusive::Default`]. - pub full_screen_exclusive: FullScreenExclusive, - - /// For Win32 surfaces, if `full_screen_exclusive` is - /// [`FullScreenExclusive::ApplicationControlled`], this specifies the monitor on which - /// full-screen exclusivity should be used. - /// - /// For this case, the value must be `Some`, and for all others it must be `None`. - /// - /// The default value is `None`. - pub win32_monitor: Option, - - pub _ne: crate::NonExhaustive, -} - -impl Default for SwapchainCreateInfo { - #[inline] - fn default() -> Self { - Self { - min_image_count: 2, - image_format: None, - image_color_space: ColorSpace::SrgbNonLinear, - image_extent: [0, 0], - image_array_layers: 1, - image_usage: ImageUsage::empty(), - image_sharing: Sharing::Exclusive, - pre_transform: SurfaceTransform::Identity, - composite_alpha: CompositeAlpha::Opaque, - present_mode: PresentMode::Fifo, - clipped: true, - full_screen_exclusive: FullScreenExclusive::Default, - win32_monitor: None, - _ne: crate::NonExhaustive(()), - } - } -} - -/// Error that can happen when creating a `Swapchain`. -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum SwapchainCreationError { - /// Not enough memory. - OomError(OomError), - - /// The device was lost. - DeviceLost, - - /// The surface was lost. - SurfaceLost, - - /// The surface is already used by another swapchain. - SurfaceInUse, - - /// The window is already in use by another API. - NativeWindowInUse, - - RequirementNotMet { - required_for: &'static str, - requires_one_of: RequiresOneOf, - }, - - /// The provided `composite_alpha` is not supported by the surface for this device. - CompositeAlphaNotSupported { - provided: CompositeAlpha, - supported: CompositeAlphas, - }, - - /// The provided `format` and `color_space` are not supported by the surface for this device. - FormatColorSpaceNotSupported, - - /// The provided `image_array_layers` is greater than what is supported by the surface for this - /// device. - ImageArrayLayersNotSupported { provided: u32, max_supported: u32 }, - - /// The provided `image_extent` is not within the range supported by the surface for this - /// device. - ImageExtentNotSupported { - provided: [u32; 2], - min_supported: [u32; 2], - max_supported: [u32; 2], - }, - - /// The provided `image_extent` contained at least one dimension of zero length. - /// This is prohibited by [VUID-VkSwapchainCreateInfoKHR-imageExtent-01689](https://khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkSwapchainCreateInfoKHR.html#VUID-VkSwapchainCreateInfoKHR-imageExtent-01689) - /// which requires both the width and height be non-zero. - /// - /// This error is distinct from `ImageExtentNotSupported` because a surface's minimum supported - /// length may not enforce this rule. - ImageExtentZeroLengthDimensions, - - /// The provided image parameters are not supported as queried from `image_format_properties`. - ImageFormatPropertiesNotSupported, - - /// The provided `image_sharing` was set to `Concurrent`, but one of the specified queue family - /// indices was out of range. - ImageSharingQueueFamilyIndexOutOfRange { - queue_family_index: u32, - queue_family_count: u32, - }, - - /// The provided `image_usage` has fields set that are not supported by the surface for this - /// device. - ImageUsageNotSupported { - provided: ImageUsage, - supported: ImageUsage, - }, - - /// The provided `min_image_count` is not within the range supported by the surface for this - /// device. - MinImageCountNotSupported { - provided: u32, - min_supported: u32, - max_supported: Option, - }, - - /// The provided `present_mode` is not supported by the surface for this device. - PresentModeNotSupported, - - /// The provided `pre_transform` is not supported by the surface for this device. - PreTransformNotSupported { - provided: SurfaceTransform, - supported: SurfaceTransforms, - }, - - /// The provided `surface` is not supported by any of the device's queue families. - SurfaceNotSupported, - - /// The swapchain has already been used to create a new one. - SwapchainAlreadyRetired, - - /// The `win32_monitor` value was `Some` when it must be `None` or vice-versa. - Win32MonitorInvalid, -} - -impl Error for SwapchainCreationError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - match self { - Self::OomError(err) => Some(err), - _ => None, - } - } -} - -impl Display for SwapchainCreationError { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { - match self { - Self::OomError(_) => write!(f, "not enough memory available"), - Self::DeviceLost => write!(f, "the device was lost"), - Self::SurfaceLost => write!(f, "the surface was lost"), - Self::SurfaceInUse => { - write!(f, "the surface is already used by another swapchain") - } - Self::NativeWindowInUse => { - write!(f, "the window is already in use by another API") - } - Self::RequirementNotMet { - required_for, - requires_one_of, - } => write!( - f, - "a requirement was not met for: {}; requires one of: {}", - required_for, requires_one_of, - ), - Self::CompositeAlphaNotSupported { .. } => write!( - f, - "the provided `composite_alpha` is not supported by the surface for this device", - ), - Self::FormatColorSpaceNotSupported => write!( - f, - "the provided `format` and `color_space` are not supported by the surface for this \ - device", - ), - Self::ImageArrayLayersNotSupported { - provided, - max_supported, - } => write!( - f, - "the provided `image_array_layers` ({}) is greater than what is supported ({}) by \ - the surface for this device", - provided, max_supported, - ), - Self::ImageExtentNotSupported { - provided, - min_supported, - max_supported, - } => write!( - f, - "the provided `image_extent` ({:?}) is not within the range (min: {:?}, max: {:?}) \ - supported by the surface for this device", - provided, min_supported, max_supported, - ), - Self::ImageExtentZeroLengthDimensions => write!( - f, - "the provided `image_extent` contained at least one dimension of zero length", - ), - Self::ImageFormatPropertiesNotSupported => write!( - f, - "the provided image parameters are not supported as queried from \ - `image_format_properties`", - ), - Self::ImageSharingQueueFamilyIndexOutOfRange { - queue_family_index, - queue_family_count: _, - } => write!( - f, - "the provided `image_sharing` was set to `Concurrent`, but one of the specified \ - queue family indices ({}) was out of range", - queue_family_index, - ), - Self::ImageUsageNotSupported { .. } => write!( - f, - "the provided `image_usage` has fields set that are not supported by the surface \ - for this device", - ), - Self::MinImageCountNotSupported { - provided, - min_supported, - max_supported, - } => write!( - f, - "the provided `min_image_count` ({}) is not within the range (min: {}, max: {:?}) \ - supported by the surface for this device", - provided, min_supported, max_supported, - ), - Self::PresentModeNotSupported => write!( - f, - "the provided `present_mode` is not supported by the surface for this device", - ), - Self::PreTransformNotSupported { .. } => write!( - f, - "the provided `pre_transform` is not supported by the surface for this device", - ), - Self::SurfaceNotSupported => write!( - f, - "the provided `surface` is not supported by any of the device's queue families", - ), - Self::SwapchainAlreadyRetired => { - write!(f, "the swapchain has already been used to create a new one") - } - Self::Win32MonitorInvalid => write!( - f, - "the `win32_monitor` value was `Some` when it must be `None` or vice-versa", - ), - } - } -} - -impl From for SwapchainCreationError { - fn from(err: RuntimeError) -> SwapchainCreationError { - match err { - err @ RuntimeError::OutOfHostMemory => Self::OomError(OomError::from(err)), - err @ RuntimeError::OutOfDeviceMemory => Self::OomError(OomError::from(err)), - RuntimeError::DeviceLost => Self::DeviceLost, - RuntimeError::SurfaceLost => Self::SurfaceLost, - RuntimeError::NativeWindowInUse => Self::NativeWindowInUse, - _ => panic!("unexpected error: {:?}", err), - } - } -} - -impl From for SwapchainCreationError { - fn from(err: OomError) -> SwapchainCreationError { - Self::OomError(err) - } -} - -impl From for SwapchainCreationError { - fn from(err: RequirementNotMet) -> Self { - Self::RequirementNotMet { - required_for: err.required_for, - requires_one_of: err.requires_one_of, - } - } -} - -vulkan_enum! { - #[non_exhaustive] - - /// The way full-screen exclusivity is handled. - FullScreenExclusive = FullScreenExclusiveEXT(i32); - - /// Indicates that the driver should determine the appropriate full-screen method - /// by whatever means it deems appropriate. - Default = DEFAULT, - - /// Indicates that the driver may use full-screen exclusive mechanisms when available. - /// Such mechanisms may result in better performance and/or the availability of - /// different presentation capabilities, but may require a more disruptive transition - // during swapchain initialization, first presentation and/or destruction. - Allowed = ALLOWED, - - /// Indicates that the driver should avoid using full-screen mechanisms which rely - /// on disruptive transitions. - Disallowed = DISALLOWED, - - /// Indicates the application will manage full-screen exclusive mode by using the - /// [`Swapchain::acquire_full_screen_exclusive()`] and - /// [`Swapchain::release_full_screen_exclusive()`] functions. - ApplicationControlled = APPLICATION_CONTROLLED, -} - -/// A wrapper around a Win32 monitor handle. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub struct Win32Monitor(pub(crate) ash::vk::HMONITOR); - -impl Win32Monitor { - /// Wraps a Win32 monitor handle. - /// - /// # Safety - /// - /// - `hmonitor` must be a valid handle as returned by the Win32 API. - pub unsafe fn new(hmonitor: *const T) -> Self { - Self(hmonitor as _) - } -} - -// Winit's `MonitorHandle` is Send on Win32, so this seems safe. -unsafe impl Send for Win32Monitor {} -unsafe impl Sync for Win32Monitor {} - -/// Error that can happen when calling `Swapchain::acquire_full_screen_exclusive` or -/// `Swapchain::release_full_screen_exclusive`. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum FullScreenExclusiveError { - /// Not enough memory. - OomError(OomError), - - /// Operation could not be completed for driver specific reasons. - InitializationFailed, - - /// The surface is no longer accessible and must be recreated. - SurfaceLost, - - /// Full-screen exclusivity is already acquired. - DoubleAcquire, - - /// Full-screen exclusivity is not currently acquired. - DoubleRelease, - - /// The swapchain is not in full-screen exclusive application controlled mode. - NotApplicationControlled, -} - -impl Error for FullScreenExclusiveError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - match self { - FullScreenExclusiveError::OomError(err) => Some(err), - _ => None, - } - } -} - -impl Display for FullScreenExclusiveError { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { - write!( - f, - "{}", - match self { - FullScreenExclusiveError::OomError(_) => "not enough memory", - FullScreenExclusiveError::SurfaceLost => { - "the surface of this swapchain is no longer valid" - } - FullScreenExclusiveError::InitializationFailed => { - "operation could not be completed for driver specific reasons" - } - FullScreenExclusiveError::DoubleAcquire => - "full-screen exclusivity is already acquired", - FullScreenExclusiveError::DoubleRelease => - "full-screen exclusivity is not acquired", - FullScreenExclusiveError::NotApplicationControlled => { - "the swapchain is not in full-screen exclusive application controlled mode" - } - } - ) - } -} - -impl From for FullScreenExclusiveError { - fn from(err: RuntimeError) -> FullScreenExclusiveError { - match err { - err @ RuntimeError::OutOfHostMemory => { - FullScreenExclusiveError::OomError(OomError::from(err)) - } - err @ RuntimeError::OutOfDeviceMemory => { - FullScreenExclusiveError::OomError(OomError::from(err)) - } - RuntimeError::SurfaceLost => FullScreenExclusiveError::SurfaceLost, - RuntimeError::InitializationFailed => FullScreenExclusiveError::InitializationFailed, - _ => panic!("unexpected error: {:?}", err), - } - } -} - -impl From for FullScreenExclusiveError { - fn from(err: OomError) -> FullScreenExclusiveError { - FullScreenExclusiveError::OomError(err) - } -} - -/// Tries to take ownership of an image in order to draw on it. -/// -/// The function returns the index of the image in the array of images that was returned -/// when creating the swapchain, plus a future that represents the moment when the image will -/// become available from the GPU (which may not be *immediately*). -/// -/// If you try to draw on an image without acquiring it first, the execution will block. (TODO -/// behavior may change). -/// -/// The second field in the tuple in the Ok result is a bool represent if the acquisition was -/// suboptimal. In this case the acquired image is still usable, but the swapchain should be -/// recreated as the Surface's properties no longer match the swapchain. -pub fn acquire_next_image( - swapchain: Arc, - timeout: Option, -) -> Result<(u32, bool, SwapchainAcquireFuture), AcquireError> { - let semaphore = Arc::new(Semaphore::from_pool(swapchain.device.clone())?); - let fence = Fence::from_pool(swapchain.device.clone())?; - - let AcquiredImage { - image_index, - suboptimal, - } = { - // Check that this is not an old swapchain. From specs: - // > swapchain must not have been replaced by being passed as the - // > VkSwapchainCreateInfoKHR::oldSwapchain value to vkCreateSwapchainKHR - let retired = swapchain.retired.lock(); - if *retired { - return Err(AcquireError::OutOfDate); - } - - let acquire_result = - unsafe { acquire_next_image_raw(&swapchain, timeout, Some(&semaphore), Some(&fence)) }; - - if let &Err(AcquireError::FullScreenExclusiveModeLost) = &acquire_result { - swapchain - .full_screen_exclusive_held - .store(false, Ordering::SeqCst); - } - - acquire_result? - }; - - Ok(( - image_index, - suboptimal, - SwapchainAcquireFuture { - swapchain, - semaphore: Some(semaphore), - fence: Some(fence), - image_index, - finished: AtomicBool::new(false), - }, - )) -} - -/// Presents an image on the screen. -/// -/// The actual behavior depends on the present mode that you passed when creating the swapchain. -pub fn present( - before: F, - queue: Arc, - swapchain_info: SwapchainPresentInfo, -) -> PresentFuture -where - F: GpuFuture, -{ - assert!(swapchain_info.image_index < swapchain_info.swapchain.image_count()); - - // TODO: restore this check with a dummy ImageAccess implementation - /*let swapchain_image = me.images.lock().unwrap().get(index).unwrap().0.upgrade().unwrap(); // TODO: return error instead - // Normally if `check_image_access` returns false we're supposed to call the `gpu_access` - // function on the image instead. But since we know that this method on `SwapchainImage` - // always returns false anyway (by design), we don't need to do it. - assert!(before.check_image_access(&swapchain_image, ImageLayout::PresentSrc, true, &queue).is_ok()); // TODO: return error instead*/ - - PresentFuture { - previous: before, - queue, - swapchain_info, - flushed: AtomicBool::new(false), - finished: AtomicBool::new(false), - } -} - -/// Wait for an image to be presented to the user. Must be used with a `present_id` given to -/// `present_with_id`. -/// -/// Returns a bool to represent if the presentation was suboptimal. In this case the swapchain is -/// still usable, but the swapchain should be recreated as the Surface's properties no longer match -/// the swapchain. -pub fn wait_for_present( - swapchain: Arc, - present_id: u64, - timeout: Option, -) -> Result { - let retired = swapchain.retired.lock(); - - // VUID-vkWaitForPresentKHR-swapchain-04997 - if *retired { - return Err(PresentWaitError::OutOfDate); - } - - if present_id == 0 { - return Err(PresentWaitError::PresentIdZero); - } - - // VUID-vkWaitForPresentKHR-presentWait-06234 - if !swapchain.device.enabled_features().present_wait { - return Err(PresentWaitError::RequirementNotMet { - required_for: "`wait_for_present`", - requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature("present_wait")])]), - }); - } - - let timeout_ns = timeout.map(|dur| dur.as_nanos() as u64).unwrap_or(0); - - let result = unsafe { - (swapchain.device.fns().khr_present_wait.wait_for_present_khr)( - swapchain.device.handle(), - swapchain.handle, - present_id, - timeout_ns, - ) - }; - - match result { - ash::vk::Result::SUCCESS => Ok(false), - ash::vk::Result::SUBOPTIMAL_KHR => Ok(true), - ash::vk::Result::TIMEOUT => Err(PresentWaitError::Timeout), - err => { - let err = RuntimeError::from(err).into(); - - if let PresentWaitError::FullScreenExclusiveModeLost = &err { - swapchain - .full_screen_exclusive_held - .store(false, Ordering::SeqCst); - } - - Err(err) - } - } -} - -/// Represents the moment when the GPU will have access to a swapchain image. -#[must_use] -pub struct SwapchainAcquireFuture { - swapchain: Arc, - image_index: u32, - // Semaphore that is signalled when the acquire is complete. Empty if the acquire has already - // happened. - semaphore: Option>, - // Fence that is signalled when the acquire is complete. Empty if the acquire has already - // happened. - fence: Option, - finished: AtomicBool, -} - -impl SwapchainAcquireFuture { - /// Returns the index of the image in the list of images returned when creating the swapchain. - pub fn image_index(&self) -> u32 { - self.image_index - } - - /// Returns the corresponding swapchain. - pub fn swapchain(&self) -> &Arc { - &self.swapchain - } - - /// Blocks the current thread until the swapchain image has been acquired, or timeout - /// - /// If timeout is `None`, will potentially block forever - /// - /// You still need to join with this future for present to work - pub fn wait(&self, timeout: Option) -> Result<(), FenceError> { - match &self.fence { - Some(fence) => fence.wait(timeout), - None => Ok(()), - } - } -} - -unsafe impl GpuFuture for SwapchainAcquireFuture { - fn cleanup_finished(&mut self) {} - - unsafe fn build_submission(&self) -> Result { - if let Some(ref semaphore) = self.semaphore { - let sem = smallvec![semaphore.clone()]; - Ok(SubmitAnyBuilder::SemaphoresWait(sem)) - } else { - Ok(SubmitAnyBuilder::Empty) - } - } - - fn flush(&self) -> Result<(), FlushError> { - Ok(()) - } - - unsafe fn signal_finished(&self) { - self.finished.store(true, Ordering::SeqCst); - } - - fn queue_change_allowed(&self) -> bool { - true - } - - fn queue(&self) -> Option> { - None - } - - fn check_buffer_access( - &self, - _buffer: &Buffer, - _range: Range, - _exclusive: bool, - _queue: &Queue, - ) -> Result<(), AccessCheckError> { - Err(AccessCheckError::Unknown) - } - - fn check_image_access( - &self, - image: &Image, - _range: Range, - _exclusive: bool, - expected_layout: ImageLayout, - _queue: &Queue, - ) -> Result<(), AccessCheckError> { - if self.swapchain.index_of_image(image) != Some(self.image_index) { - return Err(AccessCheckError::Unknown); - } - - if !self.swapchain.images[self.image_index as usize] - .layout_initialized - .load(Ordering::Relaxed) - && expected_layout != ImageLayout::Undefined - { - return Err(AccessCheckError::Denied(AccessError::ImageNotInitialized { - requested: expected_layout, - })); - } - - if expected_layout != ImageLayout::Undefined && expected_layout != ImageLayout::PresentSrc { - return Err(AccessCheckError::Denied( - AccessError::UnexpectedImageLayout { - allowed: ImageLayout::PresentSrc, - requested: expected_layout, - }, - )); - } - - Ok(()) - } - - #[inline] - fn check_swapchain_image_acquired( - &self, - swapchain: &Swapchain, - image_index: u32, - before: bool, - ) -> Result<(), AccessCheckError> { - if before { - Ok(()) - } else { - if swapchain == self.swapchain.as_ref() && image_index == self.image_index { - Ok(()) - } else { - Err(AccessCheckError::Unknown) - } - } - } -} - -impl Drop for SwapchainAcquireFuture { - fn drop(&mut self) { - if thread::panicking() { - return; - } - - if let Some(fence) = &self.fence { - fence.wait(None).unwrap(); // TODO: handle error? - self.semaphore = None; - } - - // TODO: if this future is destroyed without being presented, then eventually acquiring - // a new image will block forever ; difficulty: hard - } -} - -unsafe impl DeviceOwned for SwapchainAcquireFuture { - fn device(&self) -> &Arc { - &self.swapchain.device - } -} - -/// Error that can happen when calling `acquire_next_image`. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[repr(u32)] -pub enum AcquireError { - /// Not enough memory. - OomError(OomError), - - /// The connection to the device has been lost. - DeviceLost, - - /// The timeout of the function has been reached before an image was available. - Timeout, - - /// The surface is no longer accessible and must be recreated. - SurfaceLost, - - /// The swapchain has lost or doesn't have full-screen exclusivity possibly for - /// implementation-specific reasons outside of the application’s control. - FullScreenExclusiveModeLost, - - /// The surface has changed in a way that makes the swapchain unusable. You must query the - /// surface's new properties and recreate a new swapchain if you want to continue drawing. - OutOfDate, - - /// Error during fence creation. - FenceError(FenceError), - - /// Error during semaphore creation. - SemaphoreError(SemaphoreError), -} - -impl Error for AcquireError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - match self { - AcquireError::OomError(err) => Some(err), - _ => None, - } - } -} - -impl Display for AcquireError { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { - write!( - f, - "{}", - match self { - AcquireError::OomError(_) => "not enough memory", - AcquireError::DeviceLost => "the connection to the device has been lost", - AcquireError::Timeout => "no image is available for acquiring yet", - AcquireError::SurfaceLost => "the surface of this swapchain is no longer valid", - AcquireError::OutOfDate => "the swapchain needs to be recreated", - AcquireError::FullScreenExclusiveModeLost => { - "the swapchain no longer has full-screen exclusivity" - } - AcquireError::FenceError(_) => "error creating fence", - AcquireError::SemaphoreError(_) => "error creating semaphore", - } - ) - } -} - -impl From for AcquireError { - fn from(err: FenceError) -> Self { - AcquireError::FenceError(err) - } -} - -impl From for AcquireError { - fn from(err: SemaphoreError) -> Self { - AcquireError::SemaphoreError(err) - } -} - -impl From for AcquireError { - fn from(err: OomError) -> AcquireError { - AcquireError::OomError(err) - } -} - -impl From for AcquireError { - fn from(err: RuntimeError) -> AcquireError { - match err { - err @ RuntimeError::OutOfHostMemory => AcquireError::OomError(OomError::from(err)), - err @ RuntimeError::OutOfDeviceMemory => AcquireError::OomError(OomError::from(err)), - RuntimeError::DeviceLost => AcquireError::DeviceLost, - RuntimeError::SurfaceLost => AcquireError::SurfaceLost, - RuntimeError::OutOfDate => AcquireError::OutOfDate, - RuntimeError::FullScreenExclusiveModeLost => AcquireError::FullScreenExclusiveModeLost, - _ => panic!("unexpected error: {:?}", err), - } - } -} - -/// Error that can happen when calling `acquire_next_image`. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[repr(u32)] -pub enum PresentWaitError { - /// Not enough memory. - OomError(OomError), - - /// The connection to the device has been lost. - DeviceLost, - - /// The surface has changed in a way that makes the swapchain unusable. You must query the - /// surface's new properties and recreate a new swapchain if you want to continue drawing. - OutOfDate, - - /// The surface is no longer accessible and must be recreated. - SurfaceLost, - - /// The swapchain has lost or doesn't have full-screen exclusivity possibly for - /// implementation-specific reasons outside of the application’s control. - FullScreenExclusiveModeLost, - - /// The timeout of the function has been reached before the present occured. - Timeout, - - RequirementNotMet { - required_for: &'static str, - requires_one_of: RequiresOneOf, - }, - - /// Present id of zero is invalid. - PresentIdZero, -} - -impl Error for PresentWaitError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - match self { - Self::OomError(err) => Some(err), - _ => None, - } - } -} - -impl Display for PresentWaitError { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { - match self { - Self::OomError(e) => write!(f, "{}", e), - Self::DeviceLost => write!(f, "the connection to the device has been lost"), - Self::Timeout => write!(f, "no image is available for acquiring yet"), - Self::SurfaceLost => write!(f, "the surface of this swapchain is no longer valid"), - Self::OutOfDate => write!(f, "the swapchain needs to be recreated"), - Self::FullScreenExclusiveModeLost => { - write!(f, "the swapchain no longer has full-screen exclusivity") - } - Self::RequirementNotMet { - required_for, - requires_one_of, - } => write!( - f, - "a requirement was not met for: {}; requires one of: {}", - required_for, requires_one_of, - ), - Self::PresentIdZero => write!(f, "present id of zero is invalid"), - } - } -} - -impl From for PresentWaitError { - fn from(err: OomError) -> PresentWaitError { - Self::OomError(err) - } -} - -impl From for PresentWaitError { - fn from(err: RequirementNotMet) -> Self { - Self::RequirementNotMet { - required_for: err.required_for, - requires_one_of: err.requires_one_of, - } - } -} - -impl From for PresentWaitError { - fn from(err: RuntimeError) -> PresentWaitError { - match err { - err @ RuntimeError::OutOfHostMemory => Self::OomError(OomError::from(err)), - err @ RuntimeError::OutOfDeviceMemory => Self::OomError(OomError::from(err)), - RuntimeError::DeviceLost => Self::DeviceLost, - RuntimeError::SurfaceLost => Self::SurfaceLost, - RuntimeError::OutOfDate => Self::OutOfDate, - RuntimeError::FullScreenExclusiveModeLost => Self::FullScreenExclusiveModeLost, - _ => panic!("unexpected error: {:?}", err), - } - } -} - -/// Represents a swapchain image being presented on the screen. -#[must_use = "Dropping this object will immediately block the thread until the GPU has finished processing the submission"] -pub struct PresentFuture

-where - P: GpuFuture, -{ - previous: P, - queue: Arc, - swapchain_info: SwapchainPresentInfo, - // True if `flush()` has been called on the future, which means that the present command has - // been submitted. - flushed: AtomicBool, - // True if `signal_finished()` has been called on the future, which means that the future has - // been submitted and has already been processed by the GPU. - finished: AtomicBool, -} - -impl

PresentFuture

-where - P: GpuFuture, -{ - /// Returns the index of the image in the list of images returned when creating the swapchain. - pub fn image_id(&self) -> u32 { - self.swapchain_info.image_index - } - - /// Returns the corresponding swapchain. - pub fn swapchain(&self) -> &Arc { - &self.swapchain_info.swapchain - } -} - -unsafe impl

GpuFuture for PresentFuture

-where - P: GpuFuture, -{ - fn cleanup_finished(&mut self) { - self.previous.cleanup_finished(); - } - - unsafe fn build_submission(&self) -> Result { - if self.flushed.load(Ordering::SeqCst) { - return Ok(SubmitAnyBuilder::Empty); - } - - let mut swapchain_info = self.swapchain_info.clone(); - debug_assert!(swapchain_info.image_index < swapchain_info.swapchain.image_count()); - let device = swapchain_info.swapchain.device(); - - if !device.enabled_features().present_id { - swapchain_info.present_id = None; - } - - if device.enabled_extensions().khr_incremental_present { - for rectangle in &swapchain_info.present_regions { - assert!(rectangle.is_compatible_with(swapchain_info.swapchain.as_ref())); - } - } else { - swapchain_info.present_regions = Default::default(); - } - - let _queue = self.previous.queue(); - - // TODO: if the swapchain image layout is not PRESENT, should add a transition command - // buffer - - Ok(match self.previous.build_submission()? { - SubmitAnyBuilder::Empty => SubmitAnyBuilder::QueuePresent(PresentInfo { - swapchain_infos: vec![self.swapchain_info.clone()], - ..Default::default() - }), - SubmitAnyBuilder::SemaphoresWait(semaphores) => { - SubmitAnyBuilder::QueuePresent(PresentInfo { - wait_semaphores: semaphores.into_iter().collect(), - swapchain_infos: vec![self.swapchain_info.clone()], - ..Default::default() - }) - } - SubmitAnyBuilder::CommandBuffer(_, _) => { - // submit the command buffer by flushing previous. - // Since the implementation should remember being flushed it's safe to call build_submission multiple times - self.previous.flush()?; - - SubmitAnyBuilder::QueuePresent(PresentInfo { - swapchain_infos: vec![self.swapchain_info.clone()], - ..Default::default() - }) - } - SubmitAnyBuilder::BindSparse(_, _) => { - // submit the command buffer by flushing previous. - // Since the implementation should remember being flushed it's safe to call build_submission multiple times - self.previous.flush()?; - - SubmitAnyBuilder::QueuePresent(PresentInfo { - swapchain_infos: vec![self.swapchain_info.clone()], - ..Default::default() - }) - } - SubmitAnyBuilder::QueuePresent(mut present_info) => { - present_info - .swapchain_infos - .push(self.swapchain_info.clone()); - - SubmitAnyBuilder::QueuePresent(present_info) - } - }) - } - - fn flush(&self) -> Result<(), FlushError> { - unsafe { - // If `flushed` already contains `true`, then `build_submission` will return `Empty`. - - let build_submission_result = self.build_submission(); - self.flushed.store(true, Ordering::SeqCst); - - match build_submission_result? { - SubmitAnyBuilder::Empty => Ok(()), - SubmitAnyBuilder::QueuePresent(present_info) => { - // VUID-VkPresentIdKHR-presentIds-04999 - for swapchain_info in &present_info.swapchain_infos { - if swapchain_info.present_id.map_or(false, |present_id| { - !swapchain_info.swapchain.try_claim_present_id(present_id) - }) { - return Err(FlushError::PresentIdLessThanOrEqual); - } - } - - match self.previous.check_swapchain_image_acquired( - &self.swapchain_info.swapchain, - self.swapchain_info.image_index, - true, - ) { - Ok(_) => (), - Err(AccessCheckError::Unknown) => { - return Err(AccessError::SwapchainImageNotAcquired.into()) - } - Err(AccessCheckError::Denied(e)) => return Err(e.into()), - } - - Ok(self - .queue - .with(|mut q| q.present_unchecked(present_info))? - .map(|r| r.map(|_| ())) - .fold(Ok(()), Result::and)?) - } - _ => unreachable!(), - } - } - } - - unsafe fn signal_finished(&self) { - self.flushed.store(true, Ordering::SeqCst); - self.finished.store(true, Ordering::SeqCst); - self.previous.signal_finished(); - } - - fn queue_change_allowed(&self) -> bool { - false - } - - fn queue(&self) -> Option> { - debug_assert!(match self.previous.queue() { - None => true, - Some(q) => q == self.queue, - }); - - Some(self.queue.clone()) - } - - fn check_buffer_access( - &self, - buffer: &Buffer, - range: Range, - exclusive: bool, - queue: &Queue, - ) -> Result<(), AccessCheckError> { - self.previous - .check_buffer_access(buffer, range, exclusive, queue) - } - - fn check_image_access( - &self, - image: &Image, - range: Range, - exclusive: bool, - expected_layout: ImageLayout, - queue: &Queue, - ) -> Result<(), AccessCheckError> { - if self.swapchain_info.swapchain.index_of_image(image) - == Some(self.swapchain_info.image_index) - { - // This future presents the swapchain image, which "unlocks" it. Therefore any attempt - // to use this swapchain image afterwards shouldn't get granted automatic access. - // Instead any attempt to access the image afterwards should get an authorization from - // a later swapchain acquire future. Hence why we return `Unknown` here. - Err(AccessCheckError::Unknown) - } else { - self.previous - .check_image_access(image, range, exclusive, expected_layout, queue) - } - } - - #[inline] - fn check_swapchain_image_acquired( - &self, - swapchain: &Swapchain, - image_index: u32, - before: bool, - ) -> Result<(), AccessCheckError> { - if before { - self.previous - .check_swapchain_image_acquired(swapchain, image_index, false) - } else if swapchain == self.swapchain_info.swapchain.as_ref() - && image_index == self.swapchain_info.image_index - { - Err(AccessError::SwapchainImageNotAcquired.into()) - } else { - self.previous - .check_swapchain_image_acquired(swapchain, image_index, false) - } - } -} - -unsafe impl

DeviceOwned for PresentFuture

-where - P: GpuFuture, -{ - fn device(&self) -> &Arc { - self.queue.device() - } -} - -impl

Drop for PresentFuture

-where - P: GpuFuture, -{ - fn drop(&mut self) { - if thread::panicking() { - return; - } - - unsafe { - if !*self.flushed.get_mut() { - // Flushing may fail, that's okay. We will still wait for the queue later, so any - // previous futures that were flushed correctly will still be waited upon. - self.flush().ok(); - } - - if !*self.finished.get_mut() { - // Block until the queue finished. - self.queue().unwrap().with(|mut q| q.wait_idle()).unwrap(); - self.previous.signal_finished(); - } - } - } -} - -pub struct AcquiredImage { - pub image_index: u32, - pub suboptimal: bool, -} - -/// Unsafe variant of `acquire_next_image`. -/// -/// # Safety -/// -/// - The semaphore and/or the fence must be kept alive until it is signaled. -/// - The swapchain must not have been replaced by being passed as the old swapchain when creating -/// a new one. -pub unsafe fn acquire_next_image_raw( - swapchain: &Swapchain, - timeout: Option, - semaphore: Option<&Semaphore>, - fence: Option<&Fence>, -) -> Result { - let fns = swapchain.device.fns(); - - let timeout_ns = if let Some(timeout) = timeout { - timeout - .as_secs() - .saturating_mul(1_000_000_000) - .saturating_add(timeout.subsec_nanos() as u64) - } else { - u64::MAX - }; - - let mut out = MaybeUninit::uninit(); - let result = (fns.khr_swapchain.acquire_next_image_khr)( - swapchain.device.handle(), - swapchain.handle, - timeout_ns, - semaphore - .map(|s| s.handle()) - .unwrap_or(ash::vk::Semaphore::null()), - fence.map(|f| f.handle()).unwrap_or(ash::vk::Fence::null()), - out.as_mut_ptr(), - ); - - let suboptimal = match result { - ash::vk::Result::SUCCESS => false, - ash::vk::Result::SUBOPTIMAL_KHR => true, - ash::vk::Result::NOT_READY => return Err(AcquireError::Timeout), - ash::vk::Result::TIMEOUT => return Err(AcquireError::Timeout), - err => return Err(RuntimeError::from(err).into()), - }; - - if let Some(semaphore) = semaphore { - let mut state = semaphore.state(); - state.swapchain_acquire(); - } - - if let Some(fence) = fence { - let mut state = fence.state(); - state.import_swapchain_acquire(); - } - - Ok(AcquiredImage { - image_index: out.assume_init(), - suboptimal, - }) -} diff --git a/vulkano/src/sync/future/fence_signal.rs b/vulkano/src/sync/future/fence_signal.rs index 7f71d884..5bc52188 100644 --- a/vulkano/src/sync/future/fence_signal.rs +++ b/vulkano/src/sync/future/fence_signal.rs @@ -310,8 +310,12 @@ where .map_err(|err| OutcomeErr::Full(err.into())) } SubmitAnyBuilder::QueuePresent(present_info) => { - let intermediary_result = if partially_flushed { - Ok(()) + if partially_flushed { + queue + .with(|mut q| { + q.submit_unchecked([Default::default()], Some(new_fence.clone())) + }) + .map_err(|err| OutcomeErr::Partial(err.into())) } else { // VUID-VkPresentIdKHR-presentIds-04999 for swapchain_info in &present_info.swapchain_infos { @@ -334,19 +338,22 @@ where } } - queue + let intermediary_result = queue .with(|mut q| q.present_unchecked(present_info))? .map(|r| r.map(|_| ())) - .fold(Ok(()), Result::and) - }; + .fold(Ok(()), Result::and); - match intermediary_result { - Ok(()) => queue - .with(|mut q| { - q.submit_unchecked([Default::default()], Some(new_fence.clone())) - }) - .map_err(|err| OutcomeErr::Partial(err.into())), - Err(err) => Err(OutcomeErr::Full(err.into())), + match intermediary_result { + Ok(()) => queue + .with(|mut q| { + q.submit_unchecked( + [Default::default()], + Some(new_fence.clone()), + ) + }) + .map_err(|err| OutcomeErr::Partial(err.into())), + Err(err) => Err(OutcomeErr::Full(err.into())), + } } } }; diff --git a/vulkano/src/sync/future/mod.rs b/vulkano/src/sync/future/mod.rs index 64353dd2..e6b6feb4 100644 --- a/vulkano/src/sync/future/mod.rs +++ b/vulkano/src/sync/future/mod.rs @@ -597,6 +597,10 @@ pub enum FlushError { /// for the same swapchain. PresentIdLessThanOrEqual, + /// A new present mode was provided, but this mode was not one of the valid present modes + /// that the swapchain was created with. + PresentModeNotValid, + /// Access to a resource has been denied. ResourceAccessError { error: AccessError, @@ -615,9 +619,9 @@ pub enum FlushError { impl Error for FlushError { fn source(&self) -> Option<&(dyn Error + 'static)> { match self { - FlushError::AccessError(err) => Some(err), - FlushError::OomError(err) => Some(err), - FlushError::ResourceAccessError { error, .. } => Some(error), + Self::AccessError(err) => Some(err), + Self::OomError(err) => Some(err), + Self::ResourceAccessError { error, .. } => Some(error), _ => None, } } @@ -629,27 +633,31 @@ impl Display for FlushError { f, "{}", match self { - FlushError::AccessError(_) => "access to a resource has been denied", - FlushError::OomError(_) => "not enough memory", - FlushError::DeviceLost => "the connection to the device has been lost", - FlushError::SurfaceLost => "the surface of this swapchain is no longer valid", - FlushError::OutOfDate => "the swapchain needs to be recreated", - FlushError::FullScreenExclusiveModeLost => { + Self::AccessError(_) => "access to a resource has been denied", + Self::OomError(_) => "not enough memory", + Self::DeviceLost => "the connection to the device has been lost", + Self::SurfaceLost => "the surface of this swapchain is no longer valid", + Self::OutOfDate => "the swapchain needs to be recreated", + Self::FullScreenExclusiveModeLost => { "the swapchain no longer has full screen exclusivity" } - FlushError::Timeout => { + Self::Timeout => { "the flush operation needed to block, but the timeout has elapsed" } - FlushError::PresentIdLessThanOrEqual => { + Self::PresentIdLessThanOrEqual => { "present id is less than or equal to previous" } - FlushError::ResourceAccessError { .. } => "access to a resource has been denied", - FlushError::OneTimeSubmitAlreadySubmitted => { + Self::PresentModeNotValid => { + "a new present mode was provided, but this mode was not one of the valid \ + present modes that the swapchain was created with" + } + Self::ResourceAccessError { .. } => "access to a resource has been denied", + Self::OneTimeSubmitAlreadySubmitted => { "the command buffer or one of the secondary command buffers it executes was \ created with the \"one time submit\" flag, but has already been submitted in \ the past" } - FlushError::ExclusiveAlreadyInUse => { + Self::ExclusiveAlreadyInUse => { "the command buffer or one of the secondary command buffers it executes is \ already in use was not created with the \"concurrent\" flag" } @@ -660,7 +668,7 @@ impl Display for FlushError { impl From for FlushError { fn from(err: AccessError) -> FlushError { - FlushError::AccessError(err) + Self::AccessError(err) } } @@ -682,9 +690,9 @@ impl From for FlushError { impl From for FlushError { fn from(err: FenceError) -> FlushError { match err { - FenceError::OomError(err) => FlushError::OomError(err), - FenceError::Timeout => FlushError::Timeout, - FenceError::DeviceLost => FlushError::DeviceLost, + FenceError::OomError(err) => Self::OomError(err), + FenceError::Timeout => Self::Timeout, + FenceError::DeviceLost => Self::DeviceLost, _ => unreachable!(), } }