From 645faa45301e5b83d7a04a66cea227622a523763 Mon Sep 17 00:00:00 2001 From: Rua Date: Sun, 20 Feb 2022 10:55:34 +0100 Subject: [PATCH] Add `SwapchainCreateInfo`, various related changes (#1832) --- examples/src/bin/buffer-pool.rs | 55 +- examples/src/bin/clear_attachments.rs | 70 +- examples/src/bin/deferred/main.rs | 60 +- examples/src/bin/gl-interop.rs | 53 +- examples/src/bin/image-self-copy-blit/main.rs | 60 +- examples/src/bin/image/main.rs | 62 +- examples/src/bin/immutable-sampler/main.rs | 62 +- examples/src/bin/indirect.rs | 55 +- examples/src/bin/instancing.rs | 55 +- examples/src/bin/interactive_fractal/app.rs | 16 +- .../src/bin/interactive_fractal/renderer.rs | 111 +- examples/src/bin/multi-window.rs | 102 +- .../vulkano_context.rs | 57 +- .../vulkano_window.rs | 47 +- examples/src/bin/occlusion-query.rs | 63 +- examples/src/bin/push-descriptors/main.rs | 62 +- examples/src/bin/runtime-shader/main.rs | 62 +- examples/src/bin/runtime_array/main.rs | 62 +- examples/src/bin/teapot/main.rs | 59 +- examples/src/bin/tessellation.rs | 62 +- examples/src/bin/texture_array/main.rs | 62 +- examples/src/bin/triangle.rs | 90 +- vulkano-win/src/raw_window_handle.rs | 18 +- vulkano-win/src/winit.rs | 79 +- .../command_buffer/submit/queue_present.rs | 12 +- vulkano/src/device/physical.rs | 516 ++++ vulkano/src/image/swapchain.rs | 4 +- vulkano/src/lib.rs | 4 +- vulkano/src/swapchain/capabilities.rs | 585 ---- vulkano/src/swapchain/display.rs | 2 +- vulkano/src/swapchain/mod.rs | 128 +- vulkano/src/swapchain/present_region.rs | 6 +- vulkano/src/swapchain/surface.rs | 1350 ++++++---- vulkano/src/swapchain/swapchain.rs | 2342 +++++++++-------- vulkano/src/sync/future/mod.rs | 10 +- 35 files changed, 3696 insertions(+), 2747 deletions(-) delete mode 100644 vulkano/src/swapchain/capabilities.rs diff --git a/examples/src/bin/buffer-pool.rs b/examples/src/bin/buffer-pool.rs index b0f6da3a..190b18e2 100644 --- a/examples/src/bin/buffer-pool.rs +++ b/examples/src/bin/buffer-pool.rs @@ -33,7 +33,9 @@ use vulkano::pipeline::graphics::vertex_input::BuffersDefinition; use vulkano::pipeline::graphics::viewport::{Viewport, ViewportState}; use vulkano::pipeline::GraphicsPipeline; use vulkano::render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}; -use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError}; +use vulkano::swapchain::{ + self, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError, +}; use vulkano::sync::{self, FlushError, GpuFuture}; use vulkano_win::VkSurfaceBuild; use winit::event::{Event, WindowEvent}; @@ -68,7 +70,7 @@ fn main() { .filter(|&p| p.supported_extensions().is_superset_of(&device_extensions)) .filter_map(|p| { p.queue_families() - .find(|&q| q.supports_graphics() && surface.is_supported(q).unwrap_or(false)) + .find(|&q| q.supports_graphics() && q.supports_surface(&surface).unwrap_or(false)) .map(|q| (p, q)) }) .min_by_key(|(p, _)| match p.properties().device_type { @@ -101,20 +103,33 @@ fn main() { let queue = queues.next().unwrap(); let (mut swapchain, images) = { - let caps = surface.capabilities(physical_device).unwrap(); - let composite_alpha = caps.supported_composite_alpha.iter().next().unwrap(); - let format = caps.supported_formats[0].0; - let dimensions: [u32; 2] = surface.window().inner_size().into(); + let surface_capabilities = physical_device + .surface_capabilities(&surface, Default::default()) + .unwrap(); + let image_format = Some( + physical_device + .surface_formats(&surface, Default::default()) + .unwrap()[0] + .0, + ); - Swapchain::start(device.clone(), surface.clone()) - .num_images(caps.min_image_count) - .format(format) - .dimensions(dimensions) - .usage(ImageUsage::color_attachment()) - .sharing_mode(&queue) - .composite_alpha(composite_alpha) - .build() - .unwrap() + Swapchain::new( + device.clone(), + surface.clone(), + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count, + image_format, + image_extent: surface.window().inner_size().into(), + image_usage: ImageUsage::color_attachment(), + composite_alpha: surface_capabilities + .supported_composite_alpha + .iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap() }; // Vertex Buffer Pool @@ -159,7 +174,7 @@ fn main() { color: { load: Clear, store: Store, - format: swapchain.format(), + format: swapchain.image_format(), samples: 1, } }, @@ -207,11 +222,13 @@ fn main() { previous_frame_end.as_mut().unwrap().cleanup_finished(); if recreate_swapchain { - let dimensions: [u32; 2] = surface.window().inner_size().into(); let (new_swapchain, new_images) = - match swapchain.recreate().dimensions(dimensions).build() { + match swapchain.recreate(SwapchainCreateInfo { + image_extent: surface.window().inner_size().into(), + ..swapchain.create_info() + }) { Ok(r) => r, - Err(SwapchainCreationError::UnsupportedDimensions) => return, + Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return, Err(e) => panic!("Failed to recreate swapchain: {:?}", e), }; diff --git a/examples/src/bin/clear_attachments.rs b/examples/src/bin/clear_attachments.rs index 9d2d8bec..c8d3335b 100644 --- a/examples/src/bin/clear_attachments.rs +++ b/examples/src/bin/clear_attachments.rs @@ -18,7 +18,9 @@ use vulkano::instance::{Instance, InstanceCreateInfo}; use vulkano::pipeline::graphics::viewport::ViewportState; use vulkano::pipeline::GraphicsPipeline; use vulkano::render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}; -use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError}; +use vulkano::swapchain::{ + self, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError, +}; use vulkano::sync::{self, FlushError, GpuFuture}; use vulkano_win::VkSurfaceBuild; use winit::event::{Event, WindowEvent}; @@ -49,7 +51,7 @@ fn main() { .filter(|&p| p.supported_extensions().is_superset_of(&device_extensions)) .filter_map(|p| { p.queue_families() - .find(|&q| q.supports_graphics() && surface.is_supported(q).unwrap_or(false)) + .find(|&q| q.supports_graphics() && q.supports_surface(&surface).unwrap_or(false)) .map(|q| (p, q)) }) .min_by_key(|(p, _)| match p.properties().device_type { @@ -81,20 +83,33 @@ fn main() { let queue = queues.next().unwrap(); let (mut swapchain, images) = { - let caps = surface.capabilities(physical_device).unwrap(); - let composite_alpha = caps.supported_composite_alpha.iter().next().unwrap(); - let format = caps.supported_formats[0].0; - let dimensions: [u32; 2] = surface.window().inner_size().into(); + let surface_capabilities = physical_device + .surface_capabilities(&surface, Default::default()) + .unwrap(); + let image_format = Some( + physical_device + .surface_formats(&surface, Default::default()) + .unwrap()[0] + .0, + ); - Swapchain::start(device.clone(), surface.clone()) - .num_images(caps.min_image_count) - .format(format) - .dimensions(dimensions) - .usage(ImageUsage::color_attachment()) - .sharing_mode(&queue) - .composite_alpha(composite_alpha) - .build() - .unwrap() + Swapchain::new( + device.clone(), + surface.clone(), + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count, + image_format, + image_extent: surface.window().inner_size().into(), + image_usage: ImageUsage::color_attachment(), + composite_alpha: surface_capabilities + .supported_composite_alpha + .iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap() }; mod vs { @@ -133,7 +148,7 @@ fn main() { color: { load: Clear, store: Store, - format: swapchain.format(), + format: swapchain.image_format(), samples: 1, } }, @@ -153,8 +168,8 @@ fn main() { .build(device.clone()) .unwrap(); - let mut width = swapchain.dimensions()[0]; - let mut height = swapchain.dimensions()[1]; + let mut width = swapchain.image_extent()[0]; + let mut height = swapchain.image_extent()[1]; let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone()); let mut recreate_swapchain = false; @@ -177,17 +192,18 @@ fn main() { previous_frame_end.as_mut().unwrap().cleanup_finished(); if recreate_swapchain { - let dimensions: [u32; 2] = surface.window().inner_size().into(); - width = dimensions[0]; - height = dimensions[1]; - let (new_swapchain, new_images) = - match swapchain.recreate().dimensions(dimensions).build() { - Ok(r) => r, - Err(SwapchainCreationError::UnsupportedDimensions) => return, - Err(e) => panic!("Failed to recreate swapchain: {:?}", e), - }; + let (new_swapchain, new_images) = match swapchain.recreate(SwapchainCreateInfo { + image_extent: surface.window().inner_size().into(), + ..swapchain.create_info() + }) { + Ok(r) => r, + Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return, + Err(e) => panic!("Failed to recreate swapchain: {:?}", e), + }; swapchain = new_swapchain; + width = swapchain.image_extent()[0]; + height = swapchain.image_extent()[1]; framebuffers = window_size_dependent_setup(&new_images, render_pass.clone()); recreate_swapchain = false; } diff --git a/examples/src/bin/deferred/main.rs b/examples/src/bin/deferred/main.rs index 57b7cc21..48e7c84e 100644 --- a/examples/src/bin/deferred/main.rs +++ b/examples/src/bin/deferred/main.rs @@ -38,7 +38,7 @@ use vulkano::image::view::ImageView; use vulkano::image::ImageUsage; use vulkano::instance::{Instance, InstanceCreateInfo}; use vulkano::swapchain; -use vulkano::swapchain::{AcquireError, Swapchain, SwapchainCreationError}; +use vulkano::swapchain::{AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError}; use vulkano::sync; use vulkano::sync::{FlushError, GpuFuture}; use vulkano_win::VkSurfaceBuild; @@ -72,7 +72,7 @@ fn main() { .filter(|&p| p.supported_extensions().is_superset_of(&device_extensions)) .filter_map(|p| { p.queue_families() - .find(|&q| q.supports_graphics() && surface.is_supported(q).unwrap_or(false)) + .find(|&q| q.supports_graphics() && q.supports_surface(&surface).unwrap_or(false)) .map(|q| (p, q)) }) .min_by_key(|(p, _)| match p.properties().device_type { @@ -104,20 +104,33 @@ fn main() { let queue = queues.next().unwrap(); let (mut swapchain, mut images) = { - let caps = surface.capabilities(physical_device).unwrap(); - let composite_alpha = caps.supported_composite_alpha.iter().next().unwrap(); - let format = caps.supported_formats[0].0; - let dimensions: [u32; 2] = surface.window().inner_size().into(); - - let (swapchain, images) = Swapchain::start(device.clone(), surface.clone()) - .num_images(caps.min_image_count) - .format(format) - .dimensions(dimensions) - .usage(ImageUsage::color_attachment()) - .sharing_mode(&queue) - .composite_alpha(composite_alpha) - .build() + let surface_capabilities = physical_device + .surface_capabilities(&surface, Default::default()) .unwrap(); + let image_format = Some( + physical_device + .surface_formats(&surface, Default::default()) + .unwrap()[0] + .0, + ); + + let (swapchain, images) = Swapchain::new( + device.clone(), + surface.clone(), + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count, + image_format, + image_extent: surface.window().inner_size().into(), + image_usage: ImageUsage::color_attachment(), + composite_alpha: surface_capabilities + .supported_composite_alpha + .iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap(); let images = images .into_iter() .map(|image| ImageView::new(image.clone()).unwrap()) @@ -126,7 +139,7 @@ fn main() { }; // Here is the basic initialization for the deferred system. - let mut frame_system = FrameSystem::new(queue.clone(), swapchain.format()); + let mut frame_system = FrameSystem::new(queue.clone(), swapchain.image_format()); let triangle_draw_system = TriangleDrawSystem::new(queue.clone(), frame_system.deferred_subpass()); @@ -150,13 +163,14 @@ fn main() { previous_frame_end.as_mut().unwrap().cleanup_finished(); if recreate_swapchain { - let dimensions: [u32; 2] = surface.window().inner_size().into(); - let (new_swapchain, new_images) = - match swapchain.recreate().dimensions(dimensions).build() { - Ok(r) => r, - Err(SwapchainCreationError::UnsupportedDimensions) => return, - Err(e) => panic!("Failed to recreate swapchain: {:?}", e), - }; + let (new_swapchain, new_images) = match swapchain.recreate(SwapchainCreateInfo { + image_extent: surface.window().inner_size().into(), + ..swapchain.create_info() + }) { + Ok(r) => r, + Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return, + Err(e) => panic!("Failed to recreate swapchain: {:?}", e), + }; let new_images = new_images .into_iter() .map(|image| ImageView::new(image.clone()).unwrap()) diff --git a/examples/src/bin/gl-interop.rs b/examples/src/bin/gl-interop.rs index 2ad46f6b..e1e2dc6f 100644 --- a/examples/src/bin/gl-interop.rs +++ b/examples/src/bin/gl-interop.rs @@ -35,7 +35,7 @@ use vulkano::{ }, render_pass::{Framebuffer, RenderPass, Subpass}, sampler::{Filter, Sampler, SamplerAddressMode}, - swapchain::{AcquireError, Swapchain, SwapchainCreationError}, + swapchain::{AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError}, sync::{now, FlushError, GpuFuture, PipelineStages, Semaphore}, }; #[cfg(target_os = "linux")] @@ -233,11 +233,13 @@ fn main() { previous_frame_end.as_mut().unwrap().cleanup_finished(); if recreate_swapchain { - let dimensions: [u32; 2] = surface.window().inner_size().into(); let (new_swapchain, new_images) = - match swapchain.recreate().dimensions(dimensions).build() { + match swapchain.recreate(SwapchainCreateInfo { + image_extent: surface.window().inner_size().into(), + ..swapchain.create_info() + }) { Ok(r) => r, - Err(SwapchainCreationError::UnsupportedDimensions) => return, + Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return, Err(e) => panic!("Failed to recreate swapchain: {:?}", e), }; @@ -395,7 +397,7 @@ fn vk_setup( .filter(|&p| p.supported_extensions().is_superset_of(&device_extensions)) .filter_map(|p| { p.queue_families() - .find(|&q| q.supports_graphics() && surface.is_supported(q).unwrap_or(false)) + .find(|&q| q.supports_graphics() && q.supports_surface(&surface).unwrap_or(false)) .map(|q| (p, q)) }) .filter(|(p, _)| p.properties().driver_uuid.unwrap() == display.driver_uuid().unwrap()) @@ -433,20 +435,33 @@ fn vk_setup( let queue = queues.next().unwrap(); let (swapchain, images) = { - let caps = surface.capabilities(physical_device).unwrap(); - let composite_alpha = caps.supported_composite_alpha.iter().next().unwrap(); - let format = caps.supported_formats[0].0; - let dimensions: [u32; 2] = surface.window().inner_size().into(); + let surface_capabilities = physical_device + .surface_capabilities(&surface, Default::default()) + .unwrap(); + let image_format = Some( + physical_device + .surface_formats(&surface, Default::default()) + .unwrap()[0] + .0, + ); - Swapchain::start(device.clone(), surface.clone()) - .num_images(caps.min_image_count) - .format(format) - .dimensions(dimensions) - .usage(ImageUsage::color_attachment()) - .sharing_mode(&queue) - .composite_alpha(composite_alpha) - .build() - .unwrap() + Swapchain::new( + device.clone(), + surface.clone(), + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count, + image_format, + image_extent: surface.window().inner_size().into(), + image_usage: ImageUsage::color_attachment(), + composite_alpha: surface_capabilities + .supported_composite_alpha + .iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap() }; let vertex_buffer = CpuAccessibleBuffer::<[Vertex]>::from_iter( @@ -480,7 +495,7 @@ fn vk_setup( color: { load: Clear, store: Store, - format: swapchain.format(), + format: swapchain.image_format(), samples: 1, } }, diff --git a/examples/src/bin/image-self-copy-blit/main.rs b/examples/src/bin/image-self-copy-blit/main.rs index d43dcdc0..823eac15 100644 --- a/examples/src/bin/image-self-copy-blit/main.rs +++ b/examples/src/bin/image-self-copy-blit/main.rs @@ -27,7 +27,9 @@ use vulkano::pipeline::graphics::viewport::{Viewport, ViewportState}; use vulkano::pipeline::{GraphicsPipeline, Pipeline, PipelineBindPoint}; use vulkano::render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}; use vulkano::sampler::{Filter, Sampler, SamplerAddressMode}; -use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError}; +use vulkano::swapchain::{ + self, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError, +}; use vulkano::sync::{self, FlushError, GpuFuture}; use vulkano_win::VkSurfaceBuild; use winit::event::{Event, WindowEvent}; @@ -58,7 +60,7 @@ fn main() { .filter(|&p| p.supported_extensions().is_superset_of(&device_extensions)) .filter_map(|p| { p.queue_families() - .find(|&q| q.supports_graphics() && surface.is_supported(q).unwrap_or(false)) + .find(|&q| q.supports_graphics() && q.supports_surface(&surface).unwrap_or(false)) .map(|q| (p, q)) }) .min_by_key(|(p, _)| match p.properties().device_type { @@ -90,20 +92,31 @@ fn main() { let queue = queues.next().unwrap(); let (mut swapchain, images) = { - let caps = surface.capabilities(physical_device).unwrap(); - let composite_alpha = caps.supported_composite_alpha.iter().next().unwrap(); - let format = caps.supported_formats[0].0; - let dimensions: [u32; 2] = surface.window().inner_size().into(); + let surface_capabilities = physical_device.surface_capabilities(&surface, Default::default()).unwrap(); + let image_format = Some( + physical_device + .surface_formats(&surface, Default::default()) + .unwrap()[0] + .0, + ); - Swapchain::start(device.clone(), surface.clone()) - .num_images(caps.min_image_count) - .format(format) - .dimensions(dimensions) - .usage(ImageUsage::color_attachment()) - .sharing_mode(&queue) - .composite_alpha(composite_alpha) - .build() - .unwrap() + Swapchain::new( + device.clone(), + surface.clone(), + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count, + image_format, + image_extent: surface.window().inner_size().into(), + image_usage: ImageUsage::color_attachment(), + composite_alpha: surface_capabilities + .supported_composite_alpha + .iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap() }; #[repr(C)] @@ -144,7 +157,7 @@ fn main() { color: { load: Clear, store: Store, - format: swapchain.format(), + format: swapchain.image_format(), samples: 1, } }, @@ -304,13 +317,14 @@ fn main() { previous_frame_end.as_mut().unwrap().cleanup_finished(); if recreate_swapchain { - let dimensions: [u32; 2] = surface.window().inner_size().into(); - let (new_swapchain, new_images) = - match swapchain.recreate().dimensions(dimensions).build() { - Ok(r) => r, - Err(SwapchainCreationError::UnsupportedDimensions) => return, - Err(e) => panic!("Failed to recreate swapchain: {:?}", e), - }; + let (new_swapchain, new_images) = match swapchain.recreate(SwapchainCreateInfo { + image_extent: surface.window().inner_size().into(), + ..swapchain.create_info() + }) { + Ok(r) => r, + Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return, + Err(e) => panic!("Failed to recreate swapchain: {:?}", e), + }; swapchain = new_swapchain; framebuffers = diff --git a/examples/src/bin/image/main.rs b/examples/src/bin/image/main.rs index ca566559..7312439f 100644 --- a/examples/src/bin/image/main.rs +++ b/examples/src/bin/image/main.rs @@ -28,7 +28,9 @@ use vulkano::pipeline::graphics::viewport::{Viewport, ViewportState}; use vulkano::pipeline::{GraphicsPipeline, Pipeline, PipelineBindPoint}; use vulkano::render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}; use vulkano::sampler::{Filter, Sampler, SamplerAddressMode}; -use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError}; +use vulkano::swapchain::{ + self, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError, +}; use vulkano::sync::{self, FlushError, GpuFuture}; use vulkano_win::VkSurfaceBuild; use winit::event::{Event, WindowEvent}; @@ -59,7 +61,7 @@ fn main() { .filter(|&p| p.supported_extensions().is_superset_of(&device_extensions)) .filter_map(|p| { p.queue_families() - .find(|&q| q.supports_graphics() && surface.is_supported(q).unwrap_or(false)) + .find(|&q| q.supports_graphics() && q.supports_surface(&surface).unwrap_or(false)) .map(|q| (p, q)) }) .min_by_key(|(p, _)| match p.properties().device_type { @@ -91,20 +93,33 @@ fn main() { let queue = queues.next().unwrap(); let (mut swapchain, images) = { - let caps = surface.capabilities(physical_device).unwrap(); - let composite_alpha = caps.supported_composite_alpha.iter().next().unwrap(); - let format = caps.supported_formats[0].0; - let dimensions: [u32; 2] = surface.window().inner_size().into(); + let surface_capabilities = physical_device + .surface_capabilities(&surface, Default::default()) + .unwrap(); + let image_format = Some( + physical_device + .surface_formats(&surface, Default::default()) + .unwrap()[0] + .0, + ); - Swapchain::start(device.clone(), surface.clone()) - .num_images(caps.min_image_count) - .format(format) - .dimensions(dimensions) - .usage(ImageUsage::color_attachment()) - .sharing_mode(&queue) - .composite_alpha(composite_alpha) - .build() - .unwrap() + Swapchain::new( + device.clone(), + surface.clone(), + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count, + image_format, + image_extent: surface.window().inner_size().into(), + image_usage: ImageUsage::color_attachment(), + composite_alpha: surface_capabilities + .supported_composite_alpha + .iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap() }; #[repr(C)] @@ -145,7 +160,7 @@ fn main() { color: { load: Clear, store: Store, - format: swapchain.format(), + format: swapchain.image_format(), samples: 1, } }, @@ -238,13 +253,14 @@ fn main() { previous_frame_end.as_mut().unwrap().cleanup_finished(); if recreate_swapchain { - let dimensions: [u32; 2] = surface.window().inner_size().into(); - let (new_swapchain, new_images) = - match swapchain.recreate().dimensions(dimensions).build() { - Ok(r) => r, - Err(SwapchainCreationError::UnsupportedDimensions) => return, - Err(e) => panic!("Failed to recreate swapchain: {:?}", e), - }; + let (new_swapchain, new_images) = match swapchain.recreate(SwapchainCreateInfo { + image_extent: surface.window().inner_size().into(), + ..swapchain.create_info() + }) { + Ok(r) => r, + Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return, + Err(e) => panic!("Failed to recreate swapchain: {:?}", e), + }; swapchain = new_swapchain; framebuffers = diff --git a/examples/src/bin/immutable-sampler/main.rs b/examples/src/bin/immutable-sampler/main.rs index da9e1678..d53eee06 100644 --- a/examples/src/bin/immutable-sampler/main.rs +++ b/examples/src/bin/immutable-sampler/main.rs @@ -37,7 +37,9 @@ use vulkano::pipeline::graphics::viewport::{Viewport, ViewportState}; use vulkano::pipeline::{GraphicsPipeline, Pipeline, PipelineBindPoint}; use vulkano::render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}; use vulkano::sampler::{Filter, Sampler, SamplerAddressMode}; -use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError}; +use vulkano::swapchain::{ + self, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError, +}; use vulkano::sync::{self, FlushError, GpuFuture}; use vulkano_win::VkSurfaceBuild; use winit::event::{Event, WindowEvent}; @@ -65,7 +67,7 @@ fn main() { .filter(|&p| p.supported_extensions().is_superset_of(&device_extensions)) .filter_map(|p| { p.queue_families() - .find(|&q| q.supports_graphics() && surface.is_supported(q).unwrap_or(false)) + .find(|&q| q.supports_graphics() && q.supports_surface(&surface).unwrap_or(false)) .map(|q| (p, q)) }) .min_by_key(|(p, _)| match p.properties().device_type { @@ -97,20 +99,33 @@ fn main() { let queue = queues.next().unwrap(); let (mut swapchain, images) = { - let caps = surface.capabilities(physical_device).unwrap(); - let composite_alpha = caps.supported_composite_alpha.iter().next().unwrap(); - let format = caps.supported_formats[0].0; - let dimensions: [u32; 2] = surface.window().inner_size().into(); + let surface_capabilities = physical_device + .surface_capabilities(&surface, Default::default()) + .unwrap(); + let image_format = Some( + physical_device + .surface_formats(&surface, Default::default()) + .unwrap()[0] + .0, + ); - Swapchain::start(device.clone(), surface.clone()) - .num_images(caps.min_image_count) - .format(format) - .dimensions(dimensions) - .usage(ImageUsage::color_attachment()) - .sharing_mode(&queue) - .composite_alpha(composite_alpha) - .build() - .unwrap() + Swapchain::new( + device.clone(), + surface.clone(), + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count, + image_format, + image_extent: surface.window().inner_size().into(), + image_usage: ImageUsage::color_attachment(), + composite_alpha: surface_capabilities + .supported_composite_alpha + .iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap() }; #[repr(C)] @@ -151,7 +166,7 @@ fn main() { color: { load: Clear, store: Store, - format: swapchain.format(), + format: swapchain.image_format(), samples: 1, } }, @@ -246,13 +261,14 @@ fn main() { previous_frame_end.as_mut().unwrap().cleanup_finished(); if recreate_swapchain { - let dimensions: [u32; 2] = surface.window().inner_size().into(); - let (new_swapchain, new_images) = - match swapchain.recreate().dimensions(dimensions).build() { - Ok(r) => r, - Err(SwapchainCreationError::UnsupportedDimensions) => return, - Err(e) => panic!("Failed to recreate swapchain: {:?}", e), - }; + let (new_swapchain, new_images) = match swapchain.recreate(SwapchainCreateInfo { + image_extent: surface.window().inner_size().into(), + ..swapchain.create_info() + }) { + Ok(r) => r, + Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return, + Err(e) => panic!("Failed to recreate swapchain: {:?}", e), + }; swapchain = new_swapchain; framebuffers = diff --git a/examples/src/bin/indirect.rs b/examples/src/bin/indirect.rs index 7bcb7e8c..1bc8790a 100644 --- a/examples/src/bin/indirect.rs +++ b/examples/src/bin/indirect.rs @@ -47,7 +47,9 @@ use vulkano::pipeline::graphics::vertex_input::BuffersDefinition; use vulkano::pipeline::graphics::viewport::{Viewport, ViewportState}; use vulkano::pipeline::{ComputePipeline, GraphicsPipeline, Pipeline, PipelineBindPoint}; use vulkano::render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}; -use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError}; +use vulkano::swapchain::{ + self, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError, +}; use vulkano::sync::{self, FlushError, GpuFuture}; use vulkano_win::VkSurfaceBuild; use winit::event::{Event, WindowEvent}; @@ -85,7 +87,7 @@ fn main() { .filter(|&p| p.supported_extensions().is_superset_of(&device_extensions)) .filter_map(|p| { p.queue_families() - .find(|&q| q.supports_graphics() && surface.is_supported(q).unwrap_or(false)) + .find(|&q| q.supports_graphics() && q.supports_surface(&surface).unwrap_or(false)) .map(|q| (p, q)) }) .min_by_key(|(p, _)| match p.properties().device_type { @@ -118,20 +120,33 @@ fn main() { let queue = queues.next().unwrap(); let (mut swapchain, images) = { - let caps = surface.capabilities(physical_device).unwrap(); - let composite_alpha = caps.supported_composite_alpha.iter().next().unwrap(); - let format = caps.supported_formats[0].0; - let dimensions: [u32; 2] = surface.window().inner_size().into(); + let surface_capabilities = physical_device + .surface_capabilities(&surface, Default::default()) + .unwrap(); + let image_format = Some( + physical_device + .surface_formats(&surface, Default::default()) + .unwrap()[0] + .0, + ); - Swapchain::start(device.clone(), surface.clone()) - .num_images(caps.min_image_count) - .format(format) - .dimensions(dimensions) - .usage(ImageUsage::color_attachment()) - .sharing_mode(&queue) - .composite_alpha(composite_alpha) - .build() - .unwrap() + Swapchain::new( + device.clone(), + surface.clone(), + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count, + image_format, + image_extent: surface.window().inner_size().into(), + image_usage: ImageUsage::color_attachment(), + composite_alpha: surface_capabilities + .supported_composite_alpha + .iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap() }; mod vs { @@ -231,7 +246,7 @@ fn main() { color: { load: Clear, store: Store, - format: swapchain.format(), + format: swapchain.image_format(), samples: 1, } }, @@ -279,11 +294,13 @@ fn main() { previous_frame_end.as_mut().unwrap().cleanup_finished(); if recreate_swapchain { - let dimensions: [u32; 2] = surface.window().inner_size().into(); let (new_swapchain, new_images) = - match swapchain.recreate().dimensions(dimensions).build() { + match swapchain.recreate(SwapchainCreateInfo { + image_extent: surface.window().inner_size().into(), + ..swapchain.create_info() + }) { Ok(r) => r, - Err(SwapchainCreationError::UnsupportedDimensions) => return, + Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return, Err(e) => panic!("Failed to recreate swapchain: {:?}", e), }; diff --git a/examples/src/bin/instancing.rs b/examples/src/bin/instancing.rs index 8213acf0..f8c1edd3 100644 --- a/examples/src/bin/instancing.rs +++ b/examples/src/bin/instancing.rs @@ -31,7 +31,9 @@ use vulkano::pipeline::graphics::vertex_input::BuffersDefinition; use vulkano::pipeline::graphics::viewport::{Viewport, ViewportState}; use vulkano::pipeline::GraphicsPipeline; use vulkano::render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}; -use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError}; +use vulkano::swapchain::{ + self, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError, +}; use vulkano::sync::{self, FlushError, GpuFuture}; use vulkano_win::VkSurfaceBuild; use winit::event::{Event, WindowEvent}; @@ -81,7 +83,7 @@ fn main() { .filter(|&p| p.supported_extensions().is_superset_of(&device_extensions)) .filter_map(|p| { p.queue_families() - .find(|&q| q.supports_graphics() && surface.is_supported(q).unwrap_or(false)) + .find(|&q| q.supports_graphics() && q.supports_surface(&surface).unwrap_or(false)) .map(|q| (p, q)) }) .min_by_key(|(p, _)| match p.properties().device_type { @@ -114,20 +116,33 @@ fn main() { let queue = queues.next().unwrap(); let (mut swapchain, images) = { - let caps = surface.capabilities(physical_device).unwrap(); - let composite_alpha = caps.supported_composite_alpha.iter().next().unwrap(); - let format = caps.supported_formats[0].0; - let dimensions: [u32; 2] = surface.window().inner_size().into(); + let surface_capabilities = physical_device + .surface_capabilities(&surface, Default::default()) + .unwrap(); + let image_format = Some( + physical_device + .surface_formats(&surface, Default::default()) + .unwrap()[0] + .0, + ); - Swapchain::start(device.clone(), surface.clone()) - .num_images(caps.min_image_count) - .format(format) - .dimensions(dimensions) - .usage(ImageUsage::color_attachment()) - .sharing_mode(&queue) - .composite_alpha(composite_alpha) - .build() - .unwrap() + Swapchain::new( + device.clone(), + surface.clone(), + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count, + image_format, + image_extent: surface.window().inner_size().into(), + image_usage: ImageUsage::color_attachment(), + composite_alpha: surface_capabilities + .supported_composite_alpha + .iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap() }; // We now create a buffer that will store the shape of our triangle. @@ -229,7 +244,7 @@ fn main() { color: { load: Clear, store: Store, - format: swapchain.format(), + format: swapchain.image_format(), samples: 1, } }, @@ -283,11 +298,13 @@ fn main() { previous_frame_end.as_mut().unwrap().cleanup_finished(); if recreate_swapchain { - let dimensions: [u32; 2] = surface.window().inner_size().into(); let (new_swapchain, new_images) = - match swapchain.recreate().dimensions(dimensions).build() { + match swapchain.recreate(SwapchainCreateInfo { + image_extent: surface.window().inner_size().into(), + ..swapchain.create_info() + }) { Ok(r) => r, - Err(SwapchainCreationError::UnsupportedDimensions) => return, + Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return, Err(e) => panic!("Failed to recreate swapchain: {:?}", e), }; diff --git a/examples/src/bin/interactive_fractal/app.rs b/examples/src/bin/interactive_fractal/app.rs index da746a4d..d4faa6c8 100644 --- a/examples/src/bin/interactive_fractal/app.rs +++ b/examples/src/bin/interactive_fractal/app.rs @@ -74,7 +74,7 @@ Usage: Space: Toggle between Mandelbrot and Julia Enter: Randomize color palette Equals/Minus: Increase/Decrease max iterations - F: Toggle fullscreen + F: Toggle full-screen Right mouse: Stop movement in Julia (mouse position determines c) Esc: Quit\ " @@ -175,9 +175,9 @@ Usage: if self.input_state.randomize_palette { self.fractal_pipeline.randomize_palette(); } - // Toggle fullscreen - if self.input_state.toggle_fullscreen { - renderer.toggle_fullscreen() + // Toggle full-screen + if self.input_state.toggle_full_screen { + renderer.toggle_full_screen() } } @@ -211,7 +211,7 @@ struct InputState { pub increase_iterations: bool, pub decrease_iterations: bool, pub randomize_palette: bool, - pub toggle_fullscreen: bool, + pub toggle_full_screen: bool, pub toggle_julia: bool, pub toggle_c: bool, pub should_quit: bool, @@ -230,7 +230,7 @@ impl InputState { increase_iterations: false, decrease_iterations: false, randomize_palette: false, - toggle_fullscreen: false, + toggle_full_screen: false, toggle_julia: false, toggle_c: false, should_quit: false, @@ -250,7 +250,7 @@ impl InputState { fn reset(&mut self) { *self = InputState { scroll_delta: 0.0, - toggle_fullscreen: false, + toggle_full_screen: false, toggle_julia: false, toggle_c: false, randomize_palette: false, @@ -284,7 +284,7 @@ impl InputState { VirtualKeyCode::A => self.pan_left = state_is_pressed(input.state), VirtualKeyCode::S => self.pan_down = state_is_pressed(input.state), VirtualKeyCode::D => self.pan_right = state_is_pressed(input.state), - VirtualKeyCode::F => self.toggle_fullscreen = state_is_pressed(input.state), + VirtualKeyCode::F => self.toggle_full_screen = state_is_pressed(input.state), VirtualKeyCode::Return => self.randomize_palette = state_is_pressed(input.state), VirtualKeyCode::Equals => self.increase_iterations = state_is_pressed(input.state), VirtualKeyCode::Minus => self.decrease_iterations = state_is_pressed(input.state), diff --git a/examples/src/bin/interactive_fractal/renderer.rs b/examples/src/bin/interactive_fractal/renderer.rs index cd201659..55c9748c 100644 --- a/examples/src/bin/interactive_fractal/renderer.rs +++ b/examples/src/bin/interactive_fractal/renderer.rs @@ -25,8 +25,7 @@ use vulkano::image::{ use vulkano::instance::InstanceExtensions; use vulkano::instance::{Instance, InstanceCreateInfo}; use vulkano::swapchain::{ - AcquireError, ColorSpace, FullscreenExclusive, PresentMode, Surface, SurfaceTransform, - Swapchain, SwapchainCreationError, + AcquireError, PresentMode, Surface, Swapchain, SwapchainCreateInfo, SwapchainCreationError, }; use vulkano::sync::{FlushError, GpuFuture}; use vulkano::{swapchain, sync}; @@ -67,7 +66,7 @@ pub struct Renderer { device: Arc, surface: Arc>, queue: Arc, - swap_chain: Arc>, + swapchain: Arc>, image_index: usize, final_views: Vec, /// Image view that is to be rendered with our pipeline. @@ -76,7 +75,7 @@ pub struct Renderer { recreate_swapchain: bool, previous_frame_end: Option>, render_passes: RenderPasses, - is_fullscreen: bool, + is_full_screen: bool, } impl Renderer { @@ -120,11 +119,10 @@ impl Renderer { // Create device let (device, queue) = Self::create_device(physical_device, surface.clone()); // Create swap chain & frame(s) to which we'll render - let (swap_chain, final_images) = Self::create_swap_chain( + let (swapchain, final_images) = Self::create_swapchain( surface.clone(), physical_device, device.clone(), - queue.clone(), if opts.v_sync { PresentMode::Fifo } else { @@ -132,7 +130,7 @@ impl Renderer { }, ); let previous_frame_end = Some(sync::now(device.clone()).boxed()); - let is_fullscreen = swap_chain.surface().window().fullscreen().is_some(); + let is_full_screen = swapchain.surface().window().fullscreen().is_some(); let image_format = final_images.first().unwrap().format(); let render_passes = RenderPasses { place_over_frame: RenderPassPlaceOverFrame::new(queue.clone(), image_format), @@ -143,14 +141,14 @@ impl Renderer { device, surface, queue, - swap_chain, + swapchain, image_index: 0, final_views: final_images, interim_image_views: HashMap::new(), previous_frame_end, recreate_swapchain: false, render_passes, - is_fullscreen, + is_full_screen, } } @@ -161,7 +159,7 @@ impl Renderer { ) -> (Arc, Arc) { let queue_family = physical_device .queue_families() - .find(|&q| q.supports_graphics() && surface.is_supported(q).unwrap_or(false)) + .find(|&q| q.supports_graphics() && q.supports_surface(&surface).unwrap_or(false)) .unwrap(); // Add device extensions based on needs, @@ -191,37 +189,46 @@ impl Renderer { } /// Creates swapchain and swapchain images - fn create_swap_chain( + fn create_swapchain( surface: Arc>, physical: PhysicalDevice, device: Arc, - queue: Arc, present_mode: PresentMode, ) -> (Arc>, Vec) { - let caps = surface.capabilities(physical).unwrap(); - let alpha = caps.supported_composite_alpha.iter().next().unwrap(); - let format = caps.supported_formats[0].0; - let dimensions: [u32; 2] = surface.window().inner_size().into(); - let (swap_chain, images) = Swapchain::start(device, surface) - .num_images(caps.min_image_count) - .format(format) - .dimensions(dimensions) - .usage(ImageUsage::color_attachment()) - .sharing_mode(&queue) - .composite_alpha(alpha) - .transform(SurfaceTransform::Identity) - .present_mode(present_mode) - .fullscreen_exclusive(FullscreenExclusive::Default) - .clipped(true) - .color_space(ColorSpace::SrgbNonLinear) - .layers(1) - .build() + let surface_capabilities = physical + .surface_capabilities(&surface, Default::default()) .unwrap(); + let image_format = Some( + physical + .surface_formats(&surface, Default::default()) + .unwrap()[0] + .0, + ); + let image_extent = surface.window().inner_size().into(); + + let (swapchain, images) = Swapchain::new( + device, + surface, + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count, + image_format, + image_extent, + image_usage: ImageUsage::color_attachment(), + composite_alpha: surface_capabilities + .supported_composite_alpha + .iter() + .next() + .unwrap(), + present_mode, + ..Default::default() + }, + ) + .unwrap(); let images = images .into_iter() .map(|image| ImageView::new(image).unwrap()) .collect::>(); - (swap_chain, images) + (swapchain, images) } /// Return default image format for images (swapchain format may differ) @@ -334,10 +341,10 @@ impl Renderer { self.interim_image_views.remove(&key); } - /// Toggles fullscreen view - pub fn toggle_fullscreen(&mut self) { - self.is_fullscreen = !self.is_fullscreen; - self.window().set_fullscreen(if self.is_fullscreen { + /// Toggles full-screen view + pub fn toggle_full_screen(&mut self) { + self.is_full_screen = !self.is_full_screen; + self.window().set_fullscreen(if self.is_full_screen { Some(Fullscreen::Borderless(self.window().current_monitor())) } else { None @@ -367,7 +374,7 @@ impl Renderer { // Acquire next image in the swapchain let (image_num, suboptimal, acquire_future) = - match swapchain::acquire_next_image(self.swap_chain.clone(), None) { + match swapchain::acquire_next_image(self.swapchain.clone(), None) { Ok(r) => r, Err(AcquireError::OutOfDate) => { self.recreate_swapchain = true; @@ -388,11 +395,7 @@ impl Renderer { /// Finishes render by presenting the swapchain pub(crate) fn finish_frame(&mut self, after_future: Box) { let future = after_future - .then_swapchain_present( - self.queue.clone(), - self.swap_chain.clone(), - self.image_index, - ) + .then_swapchain_present(self.queue.clone(), self.swapchain.clone(), self.image_index) .then_signal_fence_and_flush(); match future { Ok(future) => { @@ -418,21 +421,19 @@ impl Renderer { /// Swapchain is recreated when resized. Interim image views that should follow swapchain /// are also recreated fn recreate_swapchain_and_views(&mut self) { - let dimensions: [u32; 2] = self.window().inner_size().into(); - let (new_swapchain, new_images) = - match self.swap_chain.recreate().dimensions(dimensions).build() { - Ok(r) => r, - Err(SwapchainCreationError::UnsupportedDimensions) => { - println!( - "{}", - SwapchainCreationError::UnsupportedDimensions.to_string() - ); - return; - } - Err(e) => panic!("Failed to recreate swapchain: {:?}", e), - }; + let (new_swapchain, new_images) = match self.swapchain.recreate(SwapchainCreateInfo { + image_extent: self.window().inner_size().into(), + ..self.swapchain.create_info() + }) { + Ok(r) => r, + Err(e @ SwapchainCreationError::ImageExtentNotSupported { .. }) => { + println!("{}", e); + return; + } + Err(e) => panic!("Failed to recreate swapchain: {:?}", e), + }; - self.swap_chain = new_swapchain; + self.swapchain = new_swapchain; let new_images = new_images .into_iter() .map(|image| ImageView::new(image).unwrap()) diff --git a/examples/src/bin/multi-window.rs b/examples/src/bin/multi-window.rs index abe8aca8..39d25b13 100644 --- a/examples/src/bin/multi-window.rs +++ b/examples/src/bin/multi-window.rs @@ -30,7 +30,9 @@ use vulkano::pipeline::graphics::vertex_input::BuffersDefinition; use vulkano::pipeline::graphics::viewport::{Viewport, ViewportState}; use vulkano::pipeline::GraphicsPipeline; use vulkano::render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}; -use vulkano::swapchain::{self, AcquireError, Surface, Swapchain, SwapchainCreationError}; +use vulkano::swapchain::{ + self, AcquireError, Surface, Swapchain, SwapchainCreateInfo, SwapchainCreationError, +}; use vulkano::sync::{self, FlushError, GpuFuture}; use vulkano_win::VkSurfaceBuild; use winit::event::ElementState; @@ -67,7 +69,7 @@ fn main() { let window_id = surface.window().id(); // Find the device and a queue. - // TODO: it is assumed the device, queue, and surface caps are the same for all windows + // TODO: it is assumed the device, queue, and surface surface_capabilities are the same for all windows let (device, queue, surface_caps) = { let device_extensions = DeviceExtensions { @@ -78,7 +80,9 @@ fn main() { .filter(|&p| p.supported_extensions().is_superset_of(&device_extensions)) .filter_map(|p| { p.queue_families() - .find(|&q| q.supports_graphics() && surface.is_supported(q).unwrap_or(false)) + .find(|&q| { + q.supports_graphics() && q.supports_surface(&surface).unwrap_or(false) + }) .map(|q| (p, q)) }) .min_by_key(|(p, _)| match p.properties().device_type { @@ -111,30 +115,40 @@ fn main() { ( device, queues.next().unwrap(), - surface.capabilities(physical_device).unwrap(), + physical_device + .surface_capabilities(&surface, Default::default()) + .unwrap(), ) }; // The swapchain and framebuffer images for this perticular window let (swapchain, images) = { - let composite_alpha = surface_caps - .supported_composite_alpha - .iter() - .next() - .unwrap(); - let format = surface_caps.supported_formats[0].0; - let dimensions: [u32; 2] = surface.window().inner_size().into(); + let image_format = Some( + device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap()[0] + .0, + ); - Swapchain::start(device.clone(), surface.clone()) - .num_images(surface_caps.min_image_count) - .format(format) - .dimensions(dimensions) - .usage(ImageUsage::color_attachment()) - .sharing_mode(&queue) - .composite_alpha(composite_alpha) - .build() - .unwrap() + Swapchain::new( + device.clone(), + surface.clone(), + SwapchainCreateInfo { + min_image_count: surface_caps.min_image_count, + image_format, + image_extent: surface.window().inner_size().into(), + image_usage: ImageUsage::color_attachment(), + composite_alpha: surface_caps + .supported_composite_alpha + .iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap() }; #[repr(C)] @@ -203,7 +217,7 @@ fn main() { color: { load: Clear, store: Store, - format: swapchain.format(), + format: swapchain.image_format(), samples: 1, } }, @@ -280,18 +294,27 @@ fn main() { .iter() .next() .unwrap(); - let format = surface_caps.supported_formats[0].0; - let dimensions: [u32; 2] = surface.window().inner_size().into(); + let image_format = Some( + device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap()[0] + .0, + ); - Swapchain::start(device.clone(), surface.clone()) - .num_images(surface_caps.min_image_count) - .format(format) - .dimensions(dimensions) - .usage(ImageUsage::color_attachment()) - .sharing_mode(&queue) - .composite_alpha(composite_alpha) - .build() - .unwrap() + Swapchain::new( + device.clone(), + surface.clone(), + SwapchainCreateInfo { + min_image_count: surface_caps.min_image_count, + image_format, + image_extent: surface.window().inner_size().into(), + image_usage: ImageUsage::color_attachment(), + composite_alpha, + ..Default::default() + }, + ) + .unwrap() }; window_surfaces.insert( @@ -326,13 +349,14 @@ fn main() { previous_frame_end.as_mut().unwrap().cleanup_finished(); if *recreate_swapchain { - let dimensions: [u32; 2] = surface.window().inner_size().into(); - let (new_swapchain, new_images) = - match swapchain.recreate().dimensions(dimensions).build() { - Ok(r) => r, - Err(SwapchainCreationError::UnsupportedDimensions) => return, - Err(e) => panic!("Failed to recreate swapchain: {:?}", e), - }; + let (new_swapchain, new_images) = match swapchain.recreate(SwapchainCreateInfo { + image_extent: surface.window().inner_size().into(), + ..swapchain.create_info() + }) { + Ok(r) => r, + Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return, + Err(e) => panic!("Failed to recreate swapchain: {:?}", e), + }; *swapchain = new_swapchain; *framebuffers = diff --git a/examples/src/bin/multi_window_game_of_life/vulkano_context.rs b/examples/src/bin/multi_window_game_of_life/vulkano_context.rs index 198ad46d..432911c8 100644 --- a/examples/src/bin/multi_window_game_of_life/vulkano_context.rs +++ b/examples/src/bin/multi_window_game_of_life/vulkano_context.rs @@ -24,7 +24,8 @@ use vulkano::{ Instance, InstanceCreateInfo, InstanceExtensions, }, swapchain::{ - ColorSpace, FullscreenExclusive, PresentMode, Surface, SurfaceTransform, Swapchain, + ColorSpace, FullScreenExclusive, PresentMode, Surface, SurfaceTransform, Swapchain, + SwapchainCreateInfo, }, Version, }; @@ -165,36 +166,48 @@ impl VulkanoContext { } /// Creates swapchain and swapchain images - pub(crate) fn create_swap_chain( + pub(crate) fn create_swapchain( &self, surface: Arc>, - queue: Arc, present_mode: PresentMode, ) -> (Arc>, Vec) { - let caps = surface.capabilities(self.device.physical_device()).unwrap(); - let alpha = caps.supported_composite_alpha.iter().next().unwrap(); - let format = caps.supported_formats[0].0; - let dimensions: [u32; 2] = surface.window().inner_size().into(); - let (swap_chain, images) = Swapchain::start(self.device.clone(), surface) - .num_images(caps.min_image_count) - .format(format) - .dimensions(dimensions) - .usage(ImageUsage::color_attachment()) - .sharing_mode(&queue) - .composite_alpha(alpha) - .transform(SurfaceTransform::Identity) - .present_mode(present_mode) - .fullscreen_exclusive(FullscreenExclusive::Default) - .clipped(true) - .color_space(ColorSpace::SrgbNonLinear) - .layers(1) - .build() + let surface_capabilities = self + .device + .physical_device() + .surface_capabilities(&surface, Default::default()) .unwrap(); + let image_format = Some( + self.device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap()[0] + .0, + ); + let image_extent = surface.window().inner_size().into(); + + let (swapchain, images) = Swapchain::new( + self.device.clone(), + surface, + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count, + image_format, + image_extent, + image_usage: ImageUsage::color_attachment(), + composite_alpha: surface_capabilities + .supported_composite_alpha + .iter() + .next() + .unwrap(), + present_mode, + ..Default::default() + }, + ) + .unwrap(); let images = images .into_iter() .map(|image| ImageView::new(image).unwrap()) .collect::>(); - (swap_chain, images) + (swapchain, images) } pub fn device_name(&self) -> &str { diff --git a/examples/src/bin/multi_window_game_of_life/vulkano_window.rs b/examples/src/bin/multi_window_game_of_life/vulkano_window.rs index cb6e5525..0c213347 100644 --- a/examples/src/bin/multi_window_game_of_life/vulkano_window.rs +++ b/examples/src/bin/multi_window_game_of_life/vulkano_window.rs @@ -9,15 +9,17 @@ use std::sync::Arc; use vulkano::{ - device::Queue, + device::{DeviceOwned, Queue}, format::Format, image::{view::ImageView, ImageAccess, ImageViewAbstract}, swapchain, - swapchain::{AcquireError, PresentMode, Surface, Swapchain, SwapchainCreationError}, + swapchain::{ + AcquireError, PresentMode, Surface, Swapchain, SwapchainCreateInfo, SwapchainCreationError, + }, sync, sync::{FlushError, GpuFuture}, }; -use vulkano_win::create_vk_surface_from_handle; +use vulkano_win::create_surface_from_handle; use winit::window::Window; use crate::vulkano_context::{DeviceImageView, FinalImageView, VulkanoContext}; @@ -28,7 +30,7 @@ use vulkano::image::{ImageCreateFlags, ImageDimensions, ImageUsage, StorageImage pub struct VulkanoWindow { surface: Arc>, graphics_queue: Arc, - swap_chain: Arc>, + swapchain: Arc>, final_views: Vec, /// Image view that is to be rendered with our pipeline. /// (bool refers to whether it should get resized with swapchain resize) @@ -51,11 +53,10 @@ impl VulkanoWindow { vsync: bool, ) -> VulkanoWindow { // Create rendering surface from window - let surface = create_vk_surface_from_handle(window, vulkano_context.instance()).unwrap(); + let surface = create_surface_from_handle(window, vulkano_context.instance()).unwrap(); // Create swap chain & frame(s) to which we'll render - let (swap_chain, final_views) = vulkano_context.create_swap_chain( + let (swapchain, final_views) = vulkano_context.create_swapchain( surface.clone(), - vulkano_context.graphics_queue(), if vsync { PresentMode::Fifo } else { @@ -68,7 +69,7 @@ impl VulkanoWindow { VulkanoWindow { surface, graphics_queue: vulkano_context.graphics_queue(), - swap_chain, + swapchain, final_views, image_views: HashMap::default(), recreate_swapchain: false, @@ -186,7 +187,7 @@ impl VulkanoWindow { // Acquire next image in the swapchain let (image_num, suboptimal, acquire_future) = - match swapchain::acquire_next_image(self.swap_chain.clone(), None) { + match swapchain::acquire_next_image(self.swapchain.clone(), None) { Ok(r) => r, Err(AcquireError::OutOfDate) => { self.recreate_swapchain = true; @@ -210,7 +211,7 @@ impl VulkanoWindow { let future = after_future .then_swapchain_present( self.graphics_queue.clone(), - self.swap_chain.clone(), + self.swapchain.clone(), self.image_index, ) .then_signal_fence_and_flush(); @@ -239,21 +240,19 @@ impl VulkanoWindow { /// Recreates swapchain images and image views that should follow swap chain image size fn recreate_swapchain_and_views(&mut self) { - let dimensions: [u32; 2] = self.window().inner_size().into(); - let (new_swapchain, new_images) = - match self.swap_chain.recreate().dimensions(dimensions).build() { - Ok(r) => r, - Err(SwapchainCreationError::UnsupportedDimensions) => { - println!( - "{}", - SwapchainCreationError::UnsupportedDimensions.to_string() - ); - return; - } - Err(e) => panic!("Failed to recreate swapchain: {:?}", e), - }; + let (new_swapchain, new_images) = match self.swapchain.recreate(SwapchainCreateInfo { + image_extent: self.window().inner_size().into(), + ..self.swapchain.create_info() + }) { + Ok(r) => r, + Err(e @ SwapchainCreationError::ImageExtentNotSupported { .. }) => { + println!("{}", e); + return; + } + Err(e) => panic!("Failed to recreate swapchain: {:?}", e), + }; - self.swap_chain = new_swapchain; + self.swapchain = new_swapchain; let new_images = new_images .into_iter() .map(|image| ImageView::new(image).unwrap()) diff --git a/examples/src/bin/occlusion-query.rs b/examples/src/bin/occlusion-query.rs index acdbd5d7..affa2c3e 100644 --- a/examples/src/bin/occlusion-query.rs +++ b/examples/src/bin/occlusion-query.rs @@ -27,7 +27,9 @@ use vulkano::pipeline::graphics::viewport::{Viewport, ViewportState}; use vulkano::pipeline::GraphicsPipeline; use vulkano::query::{QueryControlFlags, QueryPool, QueryResultFlags, QueryType}; use vulkano::render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}; -use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError}; +use vulkano::swapchain::{ + self, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError, +}; use vulkano::sync::{self, FlushError, GpuFuture}; use vulkano_win::VkSurfaceBuild; use winit::event::{Event, WindowEvent}; @@ -55,7 +57,7 @@ fn main() { .filter(|&p| p.supported_extensions().is_superset_of(&device_extensions)) .filter_map(|p| { p.queue_families() - .find(|&q| q.supports_graphics() && surface.is_supported(q).unwrap_or(false)) + .find(|&q| q.supports_graphics() && q.supports_surface(&surface).unwrap_or(false)) .map(|q| (p, q)) }) .min_by_key(|(p, _)| match p.properties().device_type { @@ -87,19 +89,33 @@ fn main() { let queue = queues.next().unwrap(); let (mut swapchain, images) = { - let caps = surface.capabilities(physical_device).unwrap(); - let composite_alpha = caps.supported_composite_alpha.iter().next().unwrap(); - let format = caps.supported_formats[0].0; - let dimensions: [u32; 2] = surface.window().inner_size().into(); - Swapchain::start(device.clone(), surface.clone()) - .num_images(caps.min_image_count) - .format(format) - .dimensions(dimensions) - .usage(ImageUsage::color_attachment()) - .sharing_mode(&queue) - .composite_alpha(composite_alpha) - .build() - .unwrap() + let surface_capabilities = physical_device + .surface_capabilities(&surface, Default::default()) + .unwrap(); + let image_format = Some( + physical_device + .surface_formats(&surface, Default::default()) + .unwrap()[0] + .0, + ); + + Swapchain::new( + device.clone(), + surface.clone(), + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count, + image_format, + image_extent: surface.window().inner_size().into(), + image_usage: ImageUsage::color_attachment(), + composite_alpha: surface_capabilities + .supported_composite_alpha + .iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap() }; #[repr(C)] @@ -225,7 +241,7 @@ fn main() { color: { load: Clear, store: Store, - format: swapchain.format(), + format: swapchain.image_format(), samples: 1, }, depth: { @@ -284,13 +300,14 @@ fn main() { previous_frame_end.as_mut().unwrap().cleanup_finished(); if recreate_swapchain { - let dimensions: [u32; 2] = surface.window().inner_size().into(); - let (new_swapchain, new_images) = - match swapchain.recreate().dimensions(dimensions).build() { - Ok(r) => r, - Err(SwapchainCreationError::UnsupportedDimensions) => return, - Err(e) => panic!("Failed to recreate swapchain: {:?}", e), - }; + let (new_swapchain, new_images) = match swapchain.recreate(SwapchainCreateInfo { + image_extent: surface.window().inner_size().into(), + ..swapchain.create_info() + }) { + Ok(r) => r, + Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return, + Err(e) => panic!("Failed to recreate swapchain: {:?}", e), + }; swapchain = new_swapchain; framebuffers = diff --git a/examples/src/bin/push-descriptors/main.rs b/examples/src/bin/push-descriptors/main.rs index f2c5c408..70932fe5 100644 --- a/examples/src/bin/push-descriptors/main.rs +++ b/examples/src/bin/push-descriptors/main.rs @@ -28,7 +28,9 @@ use vulkano::pipeline::graphics::viewport::{Viewport, ViewportState}; use vulkano::pipeline::{GraphicsPipeline, Pipeline, PipelineBindPoint}; use vulkano::render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}; use vulkano::sampler::{Filter, Sampler, SamplerAddressMode}; -use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError}; +use vulkano::swapchain::{ + self, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError, +}; use vulkano::sync::{self, FlushError, GpuFuture}; use vulkano_win::VkSurfaceBuild; use winit::event::{Event, WindowEvent}; @@ -57,7 +59,7 @@ fn main() { .filter(|&p| p.supported_extensions().is_superset_of(&device_extensions)) .filter_map(|p| { p.queue_families() - .find(|&q| q.supports_graphics() && surface.is_supported(q).unwrap_or(false)) + .find(|&q| q.supports_graphics() && q.supports_surface(&surface).unwrap_or(false)) .map(|q| (p, q)) }) .min_by_key(|(p, _)| match p.properties().device_type { @@ -89,20 +91,33 @@ fn main() { let queue = queues.next().unwrap(); let (mut swapchain, images) = { - let caps = surface.capabilities(physical_device).unwrap(); - let composite_alpha = caps.supported_composite_alpha.iter().next().unwrap(); - let format = caps.supported_formats[0].0; - let dimensions: [u32; 2] = surface.window().inner_size().into(); + let surface_capabilities = physical_device + .surface_capabilities(&surface, Default::default()) + .unwrap(); + let image_format = Some( + physical_device + .surface_formats(&surface, Default::default()) + .unwrap()[0] + .0, + ); - Swapchain::start(device.clone(), surface.clone()) - .num_images(caps.min_image_count) - .format(format) - .dimensions(dimensions) - .usage(ImageUsage::color_attachment()) - .sharing_mode(&queue) - .composite_alpha(composite_alpha) - .build() - .unwrap() + Swapchain::new( + device.clone(), + surface.clone(), + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count, + image_format, + image_extent: surface.window().inner_size().into(), + image_usage: ImageUsage::color_attachment(), + composite_alpha: surface_capabilities + .supported_composite_alpha + .iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap() }; #[repr(C)] @@ -143,7 +158,7 @@ fn main() { color: { load: Clear, store: Store, - format: swapchain.format(), + format: swapchain.image_format(), samples: 1, } }, @@ -228,13 +243,14 @@ fn main() { previous_frame_end.as_mut().unwrap().cleanup_finished(); if recreate_swapchain { - let dimensions: [u32; 2] = surface.window().inner_size().into(); - let (new_swapchain, new_images) = - match swapchain.recreate().dimensions(dimensions).build() { - Ok(r) => r, - Err(SwapchainCreationError::UnsupportedDimensions) => return, - Err(e) => panic!("Failed to recreate swapchain: {:?}", e), - }; + let (new_swapchain, new_images) = match swapchain.recreate(SwapchainCreateInfo { + image_extent: surface.window().inner_size().into(), + ..swapchain.create_info() + }) { + Ok(r) => r, + Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return, + Err(e) => panic!("Failed to recreate swapchain: {:?}", e), + }; swapchain = new_swapchain; framebuffers = diff --git a/examples/src/bin/runtime-shader/main.rs b/examples/src/bin/runtime-shader/main.rs index cfe068c3..86a88472 100644 --- a/examples/src/bin/runtime-shader/main.rs +++ b/examples/src/bin/runtime-shader/main.rs @@ -37,7 +37,9 @@ use vulkano::pipeline::graphics::viewport::{Viewport, ViewportState}; use vulkano::pipeline::GraphicsPipeline; use vulkano::render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}; use vulkano::shader::ShaderModule; -use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError}; +use vulkano::swapchain::{ + self, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError, +}; use vulkano::sync::{self, FlushError, GpuFuture}; use vulkano_win::VkSurfaceBuild; use winit::event::{Event, WindowEvent}; @@ -74,7 +76,7 @@ fn main() { .filter(|&p| p.supported_extensions().is_superset_of(&device_extensions)) .filter_map(|p| { p.queue_families() - .find(|&q| q.supports_graphics() && surface.is_supported(q).unwrap_or(false)) + .find(|&q| q.supports_graphics() && q.supports_surface(&surface).unwrap_or(false)) .map(|q| (p, q)) }) .min_by_key(|(p, _)| match p.properties().device_type { @@ -106,20 +108,33 @@ fn main() { let queue = queues.next().unwrap(); let (mut swapchain, images) = { - let caps = surface.capabilities(physical_device).unwrap(); - let composite_alpha = caps.supported_composite_alpha.iter().next().unwrap(); - let format = caps.supported_formats[0].0; - let dimensions: [u32; 2] = surface.window().inner_size().into(); + let surface_capabilities = physical_device + .surface_capabilities(&surface, Default::default()) + .unwrap(); + let image_format = Some( + physical_device + .surface_formats(&surface, Default::default()) + .unwrap()[0] + .0, + ); - Swapchain::start(device.clone(), surface.clone()) - .num_images(caps.min_image_count) - .format(format) - .dimensions(dimensions) - .usage(ImageUsage::color_attachment()) - .sharing_mode(&queue) - .composite_alpha(composite_alpha) - .build() - .unwrap() + Swapchain::new( + device.clone(), + surface.clone(), + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count, + image_format, + image_extent: surface.window().inner_size().into(), + image_usage: ImageUsage::color_attachment(), + composite_alpha: surface_capabilities + .supported_composite_alpha + .iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap() }; let render_pass = vulkano::single_pass_renderpass!( @@ -128,7 +143,7 @@ fn main() { color: { load: Clear, store: Store, - format: swapchain.format(), + format: swapchain.image_format(), samples: 1, } }, @@ -226,13 +241,14 @@ fn main() { previous_frame_end.as_mut().unwrap().cleanup_finished(); if recreate_swapchain { - let dimensions: [u32; 2] = surface.window().inner_size().into(); - let (new_swapchain, new_images) = - match swapchain.recreate().dimensions(dimensions).build() { - Ok(r) => r, - Err(SwapchainCreationError::UnsupportedDimensions) => return, - Err(e) => panic!("Failed to recreate swapchain: {:?}", e), - }; + let (new_swapchain, new_images) = match swapchain.recreate(SwapchainCreateInfo { + image_extent: surface.window().inner_size().into(), + ..swapchain.create_info() + }) { + Ok(r) => r, + Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return, + Err(e) => panic!("Failed to recreate swapchain: {:?}", e), + }; swapchain = new_swapchain; framebuffers = diff --git a/examples/src/bin/runtime_array/main.rs b/examples/src/bin/runtime_array/main.rs index 741f3133..9b8ae01e 100644 --- a/examples/src/bin/runtime_array/main.rs +++ b/examples/src/bin/runtime_array/main.rs @@ -31,7 +31,9 @@ use vulkano::pipeline::layout::PipelineLayout; use vulkano::pipeline::{GraphicsPipeline, Pipeline, PipelineBindPoint}; use vulkano::render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}; use vulkano::sampler::{Filter, Sampler, SamplerAddressMode}; -use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError}; +use vulkano::swapchain::{ + self, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError, +}; use vulkano::sync::{self, FlushError, GpuFuture}; use vulkano_win::VkSurfaceBuild; use winit::event::{Event, WindowEvent}; @@ -62,7 +64,7 @@ fn main() { .filter(|&p| p.supported_extensions().is_superset_of(&device_extensions)) .filter_map(|p| { p.queue_families() - .find(|&q| q.supports_graphics() && surface.is_supported(q).unwrap_or(false)) + .find(|&q| q.supports_graphics() && q.supports_surface(&surface).unwrap_or(false)) .map(|q| (p, q)) }) .min_by_key(|(p, _)| match p.properties().device_type { @@ -101,20 +103,33 @@ fn main() { let queue = queues.next().unwrap(); let (mut swapchain, images) = { - let caps = surface.capabilities(physical_device).unwrap(); - let composite_alpha = caps.supported_composite_alpha.iter().next().unwrap(); - let format = caps.supported_formats[0].0; - let dimensions: [u32; 2] = surface.window().inner_size().into(); + let surface_capabilities = physical_device + .surface_capabilities(&surface, Default::default()) + .unwrap(); + let image_format = Some( + physical_device + .surface_formats(&surface, Default::default()) + .unwrap()[0] + .0, + ); - Swapchain::start(device.clone(), surface.clone()) - .num_images(caps.min_image_count) - .format(format) - .dimensions(dimensions) - .usage(ImageUsage::color_attachment()) - .sharing_mode(&queue) - .composite_alpha(composite_alpha) - .build() - .unwrap() + Swapchain::new( + device.clone(), + surface.clone(), + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count, + image_format, + image_extent: surface.window().inner_size().into(), + image_usage: ImageUsage::color_attachment(), + composite_alpha: surface_capabilities + .supported_composite_alpha + .iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap() }; #[repr(C)] @@ -205,7 +220,7 @@ fn main() { color: { load: Clear, store: Store, - format: swapchain.format(), + format: swapchain.image_format(), samples: 1, } }, @@ -357,13 +372,14 @@ fn main() { previous_frame_end.as_mut().unwrap().cleanup_finished(); if recreate_swapchain { - let dimensions: [u32; 2] = surface.window().inner_size().into(); - let (new_swapchain, new_images) = - match swapchain.recreate().dimensions(dimensions).build() { - Ok(r) => r, - Err(SwapchainCreationError::UnsupportedDimensions) => return, - Err(e) => panic!("Failed to recreate swapchain: {:?}", e), - }; + let (new_swapchain, new_images) = match swapchain.recreate(SwapchainCreateInfo { + image_extent: surface.window().inner_size().into(), + ..swapchain.create_info() + }) { + Ok(r) => r, + Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return, + Err(e) => panic!("Failed to recreate swapchain: {:?}", e), + }; swapchain = new_swapchain; framebuffers = diff --git a/examples/src/bin/teapot/main.rs b/examples/src/bin/teapot/main.rs index 8c9c075b..7cf9f8b2 100644 --- a/examples/src/bin/teapot/main.rs +++ b/examples/src/bin/teapot/main.rs @@ -29,7 +29,9 @@ use vulkano::pipeline::graphics::viewport::{Viewport, ViewportState}; use vulkano::pipeline::{GraphicsPipeline, Pipeline, PipelineBindPoint}; use vulkano::render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}; use vulkano::shader::ShaderModule; -use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError}; +use vulkano::swapchain::{ + self, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError, +}; use vulkano::sync::{self, FlushError, GpuFuture}; use vulkano_win::VkSurfaceBuild; use winit::event::{Event, WindowEvent}; @@ -60,7 +62,7 @@ fn main() { .filter(|&p| p.supported_extensions().is_superset_of(&device_extensions)) .filter_map(|p| { p.queue_families() - .find(|&q| q.supports_graphics() && surface.is_supported(q).unwrap_or(false)) + .find(|&q| q.supports_graphics() && q.supports_surface(&surface).unwrap_or(false)) .map(|q| (p, q)) }) .min_by_key(|(p, _)| match p.properties().device_type { @@ -91,23 +93,35 @@ fn main() { .unwrap(); let queue = queues.next().unwrap(); - let dimensions: [u32; 2] = surface.window().inner_size().into(); let (mut swapchain, images) = { - let caps = surface.capabilities(physical_device).unwrap(); - let format = caps.supported_formats[0].0; - let composite_alpha = caps.supported_composite_alpha.iter().next().unwrap(); - let dimensions: [u32; 2] = surface.window().inner_size().into(); + let surface_capabilities = physical_device + .surface_capabilities(&surface, Default::default()) + .unwrap(); + let image_format = Some( + physical_device + .surface_formats(&surface, Default::default()) + .unwrap()[0] + .0, + ); - Swapchain::start(device.clone(), surface.clone()) - .num_images(caps.min_image_count) - .format(format) - .dimensions(dimensions) - .usage(ImageUsage::color_attachment()) - .sharing_mode(&queue) - .composite_alpha(composite_alpha) - .build() - .unwrap() + Swapchain::new( + device.clone(), + surface.clone(), + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count, + image_format, + image_extent: surface.window().inner_size().into(), + image_usage: ImageUsage::color_attachment(), + composite_alpha: surface_capabilities + .supported_composite_alpha + .iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap() }; let vertices = VERTICES.iter().cloned(); @@ -133,7 +147,7 @@ fn main() { color: { load: Clear, store: Store, - format: swapchain.format(), + format: swapchain.image_format(), samples: 1, }, depth: { @@ -175,11 +189,13 @@ fn main() { previous_frame_end.as_mut().unwrap().cleanup_finished(); if recreate_swapchain { - let dimensions: [u32; 2] = surface.window().inner_size().into(); let (new_swapchain, new_images) = - match swapchain.recreate().dimensions(dimensions).build() { + match swapchain.recreate(SwapchainCreateInfo { + image_extent: surface.window().inner_size().into(), + ..swapchain.create_info() + }) { Ok(r) => r, - Err(SwapchainCreationError::UnsupportedDimensions) => return, + Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return, Err(e) => panic!("Failed to recreate swapchain: {:?}", e), }; @@ -204,7 +220,8 @@ fn main() { // note: this teapot was meant for OpenGL where the origin is at the lower left // instead the origin is at the upper left in Vulkan, so we reverse the Y axis - let aspect_ratio = dimensions[0] as f32 / dimensions[1] as f32; + let aspect_ratio = + swapchain.image_extent()[0] as f32 / swapchain.image_extent()[1] as f32; let proj = cgmath::perspective( Rad(std::f32::consts::FRAC_PI_2), aspect_ratio, diff --git a/examples/src/bin/tessellation.rs b/examples/src/bin/tessellation.rs index 670cf323..caaf0ee1 100644 --- a/examples/src/bin/tessellation.rs +++ b/examples/src/bin/tessellation.rs @@ -33,7 +33,9 @@ use vulkano::pipeline::graphics::vertex_input::BuffersDefinition; use vulkano::pipeline::graphics::viewport::{Viewport, ViewportState}; use vulkano::pipeline::GraphicsPipeline; use vulkano::render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}; -use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError}; +use vulkano::swapchain::{ + self, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError, +}; use vulkano::sync::{self, FlushError, GpuFuture}; use vulkano_win::VkSurfaceBuild; use winit::event::{Event, WindowEvent}; @@ -162,7 +164,7 @@ fn main() { .filter(|&p| p.supported_features().is_superset_of(&features)) .filter_map(|p| { p.queue_families() - .find(|&q| q.supports_graphics() && surface.is_supported(q).unwrap_or(false)) + .find(|&q| q.supports_graphics() && q.supports_surface(&surface).unwrap_or(false)) .map(|q| (p, q)) }) .min_by_key(|(p, _)| match p.properties().device_type { @@ -195,20 +197,33 @@ fn main() { let queue = queues.next().unwrap(); let (mut swapchain, images) = { - let caps = surface.capabilities(physical_device).unwrap(); - let composite_alpha = caps.supported_composite_alpha.iter().next().unwrap(); - let format = caps.supported_formats[0].0; - let dimensions: [u32; 2] = surface.window().inner_size().into(); + let surface_capabilities = physical_device + .surface_capabilities(&surface, Default::default()) + .unwrap(); + let image_format = Some( + physical_device + .surface_formats(&surface, Default::default()) + .unwrap()[0] + .0, + ); - Swapchain::start(device.clone(), surface.clone()) - .num_images(caps.min_image_count) - .format(format) - .dimensions(dimensions) - .usage(ImageUsage::color_attachment()) - .sharing_mode(&queue) - .composite_alpha(composite_alpha) - .build() - .unwrap() + Swapchain::new( + device.clone(), + surface.clone(), + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count, + image_format, + image_extent: surface.window().inner_size().into(), + image_usage: ImageUsage::color_attachment(), + composite_alpha: surface_capabilities + .supported_composite_alpha + .iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap() }; #[derive(Default, Debug, Clone)] @@ -267,7 +282,7 @@ fn main() { color: { load: Clear, store: Store, - format: swapchain.format(), + format: swapchain.image_format(), samples: 1, } }, @@ -329,13 +344,14 @@ fn main() { previous_frame_end.as_mut().unwrap().cleanup_finished(); if recreate_swapchain { - let dimensions: [u32; 2] = surface.window().inner_size().into(); - let (new_swapchain, new_images) = - match swapchain.recreate().dimensions(dimensions).build() { - Ok(r) => r, - Err(SwapchainCreationError::UnsupportedDimensions) => return, - Err(e) => panic!("Failed to recreate swapchain: {:?}", e), - }; + let (new_swapchain, new_images) = match swapchain.recreate(SwapchainCreateInfo { + image_extent: surface.window().inner_size().into(), + ..swapchain.create_info() + }) { + Ok(r) => r, + Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return, + Err(e) => panic!("Failed to recreate swapchain: {:?}", e), + }; swapchain = new_swapchain; framebuffers = diff --git a/examples/src/bin/texture_array/main.rs b/examples/src/bin/texture_array/main.rs index 49ce6908..6cf442a5 100644 --- a/examples/src/bin/texture_array/main.rs +++ b/examples/src/bin/texture_array/main.rs @@ -33,7 +33,9 @@ use vulkano::pipeline::graphics::viewport::{Viewport, ViewportState}; use vulkano::pipeline::{GraphicsPipeline, Pipeline, PipelineBindPoint}; use vulkano::render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}; use vulkano::sampler::Sampler; -use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError}; +use vulkano::swapchain::{ + self, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError, +}; use vulkano::sync::{self, FlushError, GpuFuture}; use vulkano_win::VkSurfaceBuild; @@ -63,7 +65,7 @@ fn main() { .filter(|&p| p.supported_extensions().is_superset_of(&device_extensions)) .filter_map(|p| { p.queue_families() - .find(|&q| q.supports_graphics() && surface.is_supported(q).unwrap_or(false)) + .find(|&q| q.supports_graphics() && q.supports_surface(&surface).unwrap_or(false)) .map(|q| (p, q)) }) .min_by_key(|(p, _)| match p.properties().device_type { @@ -95,20 +97,33 @@ fn main() { let queue = queues.next().unwrap(); let (mut swapchain, images) = { - let caps = surface.capabilities(physical_device).unwrap(); - let composite_alpha = caps.supported_composite_alpha.iter().next().unwrap(); - let format = caps.supported_formats[0].0; - let dimensions: [u32; 2] = surface.window().inner_size().into(); + let surface_capabilities = physical_device + .surface_capabilities(&surface, Default::default()) + .unwrap(); + let image_format = Some( + physical_device + .surface_formats(&surface, Default::default()) + .unwrap()[0] + .0, + ); - Swapchain::start(device.clone(), surface.clone()) - .num_images(caps.min_image_count) - .format(format) - .dimensions(dimensions) - .usage(ImageUsage::color_attachment()) - .sharing_mode(&queue) - .composite_alpha(composite_alpha) - .build() - .unwrap() + Swapchain::new( + device.clone(), + surface.clone(), + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count, + image_format, + image_extent: surface.window().inner_size().into(), + image_usage: ImageUsage::color_attachment(), + composite_alpha: surface_capabilities + .supported_composite_alpha + .iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap() }; #[repr(C)] @@ -149,7 +164,7 @@ fn main() { color: { load: Clear, store: Store, - format: swapchain.format(), + format: swapchain.image_format(), samples: 1, } }, @@ -246,13 +261,14 @@ fn main() { previous_frame_end.as_mut().unwrap().cleanup_finished(); if recreate_swapchain { - let dimensions: [u32; 2] = surface.window().inner_size().into(); - let (new_swapchain, new_images) = - match swapchain.recreate().dimensions(dimensions).build() { - Ok(r) => r, - Err(SwapchainCreationError::UnsupportedDimensions) => return, - Err(e) => panic!("Failed to recreate swapchain: {:?}", e), - }; + let (new_swapchain, new_images) = match swapchain.recreate(SwapchainCreateInfo { + image_extent: surface.window().inner_size().into(), + ..swapchain.create_info() + }) { + Ok(r) => r, + Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return, + Err(e) => panic!("Failed to recreate swapchain: {:?}", e), + }; swapchain = new_swapchain; framebuffers = diff --git a/examples/src/bin/triangle.rs b/examples/src/bin/triangle.rs index 8134edf2..1890ad5e 100644 --- a/examples/src/bin/triangle.rs +++ b/examples/src/bin/triangle.rs @@ -29,7 +29,9 @@ use vulkano::pipeline::graphics::vertex_input::BuffersDefinition; use vulkano::pipeline::graphics::viewport::{Viewport, ViewportState}; use vulkano::pipeline::GraphicsPipeline; use vulkano::render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}; -use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError}; +use vulkano::swapchain::{ + self, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError, +}; use vulkano::sync::{self, FlushError, GpuFuture}; use vulkano_win::VkSurfaceBuild; use winit::event::{Event, WindowEvent}; @@ -103,7 +105,7 @@ fn main() { // We select a queue family that supports graphics operations. When drawing to // a window surface, as we do in this example, we also need to check that queues // in this queue family are capable of presenting images to the surface. - q.supports_graphics() && surface.is_supported(q).unwrap_or(false) + q.supports_graphics() && q.supports_surface(&surface).unwrap_or(false) }) // The code here searches for the first queue family that is suitable. If none is // found, `None` is returned to `filter_map`, which disqualifies this physical @@ -175,37 +177,56 @@ fn main() { let (mut swapchain, images) = { // Querying the capabilities of the surface. When we create the swapchain we can only // pass values that are allowed by the capabilities. - let caps = surface.capabilities(physical_device).unwrap(); - - // The alpha mode indicates how the alpha value of the final image will behave. For example, - // you can choose whether the window will be opaque or transparent. - let composite_alpha = caps.supported_composite_alpha.iter().next().unwrap(); + let surface_capabilities = physical_device + .surface_capabilities(&surface, Default::default()) + .unwrap(); // Choosing the internal format that the images will have. - let format = caps.supported_formats[0].0; - - // The dimensions of the window, only used to initially setup the swapchain. - // NOTE: - // On some drivers the swapchain dimensions are specified by `caps.current_extent` and the - // swapchain size must use these dimensions. - // These dimensions are always the same as the window dimensions. - // - // However, other drivers don't specify a value, i.e. `caps.current_extent` is `None` - // These drivers will allow anything, but the only sensible value is the window dimensions. - // - // Both of these cases need the swapchain to use the window dimensions, so we just use that. - let dimensions: [u32; 2] = surface.window().inner_size().into(); + let image_format = Some( + physical_device + .surface_formats(&surface, Default::default()) + .unwrap()[0] + .0, + ); // Please take a look at the docs for the meaning of the parameters we didn't mention. - Swapchain::start(device.clone(), surface.clone()) - .num_images(caps.min_image_count) - .format(format) - .dimensions(dimensions) - .usage(ImageUsage::color_attachment()) - .sharing_mode(&queue) - .composite_alpha(composite_alpha) - .build() - .unwrap() + Swapchain::new( + device.clone(), + surface.clone(), + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count, + + image_format, + // The dimensions of the window, only used to initially setup the swapchain. + // NOTE: + // On some drivers the swapchain dimensions are specified by + // `surface_capabilities.current_extent` and the swapchain size must use these + // dimensions. + // These dimensions are always the same as the window dimensions. + // + // However, other drivers don't specify a value, i.e. + // `surface_capabilities.current_extent` is `None`. These drivers will allow + // anything, but the only sensible value is the window + // dimensions. + // + // Both of these cases need the swapchain to use the window dimensions, so we just + // use that. + image_extent: surface.window().inner_size().into(), + + image_usage: ImageUsage::color_attachment(), + + // The alpha mode indicates how the alpha value of the final image will behave. For + // example, you can choose whether the window will be opaque or transparent. + composite_alpha: surface_capabilities + .supported_composite_alpha + .iter() + .next() + .unwrap(), + + ..Default::default() + }, + ) + .unwrap() }; // We now create a buffer that will store the shape of our triangle. @@ -301,7 +322,7 @@ fn main() { // be one of the types of the `vulkano::format` module (or alternatively one // of your structs that implements the `FormatDesc` trait). Here we use the // same format as the swapchain. - format: swapchain.format(), + format: swapchain.image_format(), // TODO: samples: 1, } @@ -397,13 +418,16 @@ fn main() { // In this example that includes the swapchain, the framebuffers and the dynamic state viewport. if recreate_swapchain { // Get the new dimensions of the window. - let dimensions: [u32; 2] = surface.window().inner_size().into(); + let (new_swapchain, new_images) = - match swapchain.recreate().dimensions(dimensions).build() { + match swapchain.recreate(SwapchainCreateInfo { + image_extent: surface.window().inner_size().into(), + ..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::UnsupportedDimensions) => return, + Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return, Err(e) => panic!("Failed to recreate swapchain: {:?}", e), }; diff --git a/vulkano-win/src/raw_window_handle.rs b/vulkano-win/src/raw_window_handle.rs index 99e2d60d..015fdd34 100644 --- a/vulkano-win/src/raw_window_handle.rs +++ b/vulkano-win/src/raw_window_handle.rs @@ -6,7 +6,7 @@ use vulkano::swapchain::SurfaceCreationError; /// Creates a vulkan surface from a generic window /// which implements HasRawWindowHandle and thus can reveal the os-dependent handle -pub fn create_vk_surface_from_handle( +pub fn create_surface_from_handle( window: W, instance: Arc, ) -> Result>, SurfaceCreationError> @@ -15,17 +15,17 @@ where { unsafe { match window.raw_window_handle() { - RawWindowHandle::UiKit(h) => Surface::from_ios_moltenvk(instance, h.ui_view, window), - RawWindowHandle::AppKit(h) => Surface::from_macos_moltenvk(instance, h.ns_view, window), - RawWindowHandle::Xlib(h) => Surface::from_xlib(instance, h.display, h.window, window), - RawWindowHandle::Xcb(h) => Surface::from_xcb(instance, h.connection, h.window, window), + RawWindowHandle::AndroidNdk(h) => { + Surface::from_android(instance, h.a_native_window, window) + } + RawWindowHandle::UiKit(h) => Surface::from_ios(instance, h.ui_view, window), + RawWindowHandle::AppKit(h) => Surface::from_mac_os(instance, h.ns_view, window), RawWindowHandle::Wayland(h) => { Surface::from_wayland(instance, h.display, h.surface, window) } - RawWindowHandle::AndroidNdk(h) => { - Surface::from_anativewindow(instance, h.a_native_window, window) - } - RawWindowHandle::Win32(h) => Surface::from_hwnd(instance, h.hinstance, h.hwnd, window), + RawWindowHandle::Win32(h) => Surface::from_win32(instance, h.hinstance, h.hwnd, window), + RawWindowHandle::Xcb(h) => Surface::from_xcb(instance, h.connection, h.window, window), + RawWindowHandle::Xlib(h) => Surface::from_xlib(instance, h.display, h.window, window), RawWindowHandle::Web(_) => unimplemented!(), _ => unimplemented!(), } diff --git a/vulkano-win/src/winit.rs b/vulkano-win/src/winit.rs index e108381f..61380d6a 100644 --- a/vulkano-win/src/winit.rs +++ b/vulkano-win/src/winit.rs @@ -8,22 +8,11 @@ use vulkano::instance::Instance; use vulkano::instance::InstanceExtensions; use vulkano::swapchain::Surface; use vulkano::swapchain::SurfaceCreationError; -use winit::error::OsError as WindowCreationError; -use winit::event_loop::EventLoopWindowTarget; -use winit::window::Window; -use winit::window::WindowBuilder; - -#[cfg(target_os = "macos")] -use cocoa::appkit::{NSView, NSWindow}; -#[cfg(target_os = "macos")] -use cocoa::base::id as cocoa_id; -#[cfg(target_os = "macos")] -use metal::CoreAnimationLayer; -#[cfg(target_os = "macos")] -use objc::runtime::YES; - -#[cfg(target_os = "macos")] -use std::mem; +use winit::{ + error::OsError as WindowCreationError, + event_loop::EventLoopWindowTarget, + window::{Window, WindowBuilder}, +}; pub fn required_extensions() -> InstanceExtensions { let ideal = InstanceExtensions { @@ -46,9 +35,10 @@ pub fn required_extensions() -> InstanceExtensions { } } -/// Create a surface from the window type `W`. The surface borrows the window -/// to prevent it from being dropped before the surface. -pub fn create_vk_surface( +/// Create a surface from a Winit window or a reference to it. The surface takes `W` to prevent it +/// from being dropped before the surface. +#[inline] +pub fn create_surface_from_winit( window: W, instance: Arc, ) -> Result>, SurfaceCreationError> @@ -67,13 +57,14 @@ pub trait VkSurfaceBuild { } impl VkSurfaceBuild for WindowBuilder { + #[inline] fn build_vk_surface( self, event_loop: &EventLoopWindowTarget, instance: Arc, ) -> Result>, CreationError> { let window = self.build(event_loop)?; - Ok(create_vk_surface(window, instance)?) + Ok(create_surface_from_winit(window, instance)?) } } @@ -125,13 +116,14 @@ impl From for CreationError { } #[cfg(target_os = "android")] +#[inline] unsafe fn winit_to_surface>( instance: Arc, win: W, ) -> Result>, SurfaceCreationError> { use winit::platform::android::WindowExtAndroid; - Surface::from_anativewindow(instance, win.borrow().native_window(), win) + Surface::from_android(instance, win.borrow().native_window(), win) } #[cfg(all(unix, not(target_os = "android"), not(target_os = "macos")))] @@ -168,15 +160,17 @@ unsafe fn winit_to_surface>( } } -#[cfg(target_os = "windows")] -unsafe fn winit_to_surface>( - instance: Arc, - win: W, -) -> Result>, SurfaceCreationError> { - use winit::platform::windows::WindowExtWindows; - - Surface::from_hwnd(instance, win.borrow().hinstance(), win.borrow().hwnd(), win) -} +#[cfg(target_os = "macos")] +use cocoa::{ + appkit::{NSView, NSWindow}, + base::id as cocoa_id, +}; +#[cfg(target_os = "macos")] +use metal::CoreAnimationLayer; +#[cfg(target_os = "macos")] +use objc::runtime::YES; +#[cfg(target_os = "macos")] +use std::mem; #[cfg(target_os = "macos")] unsafe fn winit_to_surface>( @@ -198,7 +192,30 @@ unsafe fn winit_to_surface>( view.setLayer(mem::transmute(layer.as_ref())); // Bombs here with out of memory view.setWantsLayer(YES); - Surface::from_macos_moltenvk(instance, win.borrow().ns_view() as *const (), win) + Surface::from_mac_os(instance, win.borrow().ns_view() as *const (), win) +} + +#[cfg(target_os = "windows")] +#[inline] +unsafe fn winit_to_surface>( + instance: Arc, + win: W, +) -> Result>, SurfaceCreationError> { + use winit::platform::windows::WindowExtWindows; + + Surface::from_win32(instance, win.borrow().hinstance(), win.borrow().hwnd(), win) +} + +#[cfg(target_os = "windows")] +use vulkano::swapchain::Win32Monitor; +#[cfg(target_os = "windows")] +use winit::{monitor::MonitorHandle, platform::windows::MonitorHandleExtWindows}; + +#[cfg(target_os = "windows")] +/// Creates a `Win32Monitor` from a Winit monitor handle. +#[inline] +pub fn create_win32_monitor_from_winit(monitor_handle: &MonitorHandle) -> Win32Monitor { + unsafe { Win32Monitor::new(monitor_handle.hmonitor()) } } /// An alternative to `Borrow` with the requirement that all calls to diff --git a/vulkano/src/command_buffer/submit/queue_present.rs b/vulkano/src/command_buffer/submit/queue_present.rs index 28f4229a..27013d29 100644 --- a/vulkano/src/command_buffer/submit/queue_present.rs +++ b/vulkano/src/command_buffer/submit/queue_present.rs @@ -94,7 +94,7 @@ impl<'a> SubmitPresentBuilder<'a> { image_num: u32, present_region: Option<&'a PresentRegion>, ) { - debug_assert!(image_num < swapchain.num_images()); + debug_assert!(image_num < swapchain.image_count()); if swapchain .device() @@ -211,9 +211,9 @@ pub enum SubmitPresentError { /// The surface is no longer accessible and must be recreated. SurfaceLost, - /// The swapchain has lost or doesn't have fullscreen exclusivity possibly for + /// The swapchain has lost or doesn't have full-screen exclusivity possibly for /// implementation-specific reasons outside of the application’s control. - FullscreenExclusiveLost, + FullScreenExclusiveLost, /// 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. @@ -242,8 +242,8 @@ impl fmt::Display for SubmitPresentError { SubmitPresentError::SurfaceLost => "the surface of this swapchain is no longer valid", SubmitPresentError::OutOfDate => "the swapchain needs to be recreated", - SubmitPresentError::FullscreenExclusiveLost => { - "the swapchain no longer has fullscreen exclusivity" + SubmitPresentError::FullScreenExclusiveLost => { + "the swapchain no longer has full-screen exclusivity" } } ) @@ -259,7 +259,7 @@ impl From for SubmitPresentError { Error::DeviceLost => SubmitPresentError::DeviceLost, Error::SurfaceLost => SubmitPresentError::SurfaceLost, Error::OutOfDate => SubmitPresentError::OutOfDate, - Error::FullscreenExclusiveLost => SubmitPresentError::FullscreenExclusiveLost, + Error::FullScreenExclusiveLost => SubmitPresentError::FullScreenExclusiveLost, _ => panic!("unexpected error: {:?}", err), } } diff --git a/vulkano/src/device/physical.rs b/vulkano/src/device/physical.rs index c1df7d19..d576f56c 100644 --- a/vulkano/src/device/physical.rs +++ b/vulkano/src/device/physical.rs @@ -13,11 +13,16 @@ use crate::image::view::ImageViewType; use crate::image::{ImageCreateFlags, ImageTiling, ImageType, ImageUsage, SampleCounts}; use crate::instance::{Instance, InstanceCreationError}; use crate::memory::ExternalMemoryHandleType; +use crate::swapchain::{ + ColorSpace, FullScreenExclusive, PresentMode, SupportedSurfaceTransforms, Surface, SurfaceApi, + SurfaceCapabilities, Win32Monitor, +}; use crate::sync::PipelineStage; use crate::Version; use crate::VulkanObject; use crate::{check_errors, OomError}; use crate::{DeviceSize, Error}; +use std::error; use std::ffi::CStr; use std::fmt; use std::hash::Hash; @@ -706,6 +711,420 @@ impl<'a> PhysicalDevice<'a> { None } } + + /// Returns the capabilities that are supported by the physical device for the given surface. + /// + /// # Panic + /// + /// - Panics if the physical device and the surface don't belong to the same instance. + pub fn surface_capabilities( + &self, + surface: &Surface, + surface_info: SurfaceInfo, + ) -> Result { + assert_eq!( + self.instance.internal_object(), + surface.instance().internal_object(), + ); + + /* Input */ + + let SurfaceInfo { + full_screen_exclusive, + win32_monitor, + _ne: _, + } = surface_info; + + let mut surface_full_screen_exclusive_info = + if self.supported_extensions().ext_full_screen_exclusive { + Some(ash::vk::SurfaceFullScreenExclusiveInfoEXT { + full_screen_exclusive: full_screen_exclusive.into(), + ..Default::default() + }) + } else { + if full_screen_exclusive != FullScreenExclusive::Default { + return Err(SurfacePropertiesError::NotSupported); + } + + None + }; + + let mut surface_full_screen_exclusive_win32_info = if surface.api() == SurfaceApi::Win32 + && full_screen_exclusive == FullScreenExclusive::ApplicationControlled + { + if let Some(win32_monitor) = win32_monitor { + Some(ash::vk::SurfaceFullScreenExclusiveWin32InfoEXT { + hmonitor: win32_monitor.0, + ..Default::default() + }) + } else { + return Err(SurfacePropertiesError::NotSupported); + } + } else { + if win32_monitor.is_some() { + return Err(SurfacePropertiesError::NotSupported); + } else { + None + } + }; + + let mut surface_info2 = ash::vk::PhysicalDeviceSurfaceInfo2KHR { + surface: surface.internal_object(), + ..Default::default() + }; + + 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(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 _; + } + + /* Output */ + + let mut surface_capabilities2 = ash::vk::SurfaceCapabilities2KHR::default(); + + let mut surface_capabilities_full_screen_exclusive = + if surface_full_screen_exclusive_info.is_some() { + Some(ash::vk::SurfaceCapabilitiesFullScreenExclusiveEXT::default()) + } else { + None + }; + + if let Some(surface_capabilities_full_screen_exclusive) = + surface_capabilities_full_screen_exclusive.as_mut() + { + surface_capabilities_full_screen_exclusive.p_next = + surface_capabilities2.p_next as *mut _; + surface_capabilities2.p_next = + surface_capabilities_full_screen_exclusive as *mut _ as *mut _; + } + + unsafe { + let fns = self.instance.fns(); + + if self + .instance + .enabled_extensions() + .khr_get_surface_capabilities2 + { + check_errors( + fns.khr_get_surface_capabilities2 + .get_physical_device_surface_capabilities2_khr( + self.internal_object(), + &surface_info2, + &mut surface_capabilities2, + ), + )?; + } else { + check_errors( + fns.khr_surface + .get_physical_device_surface_capabilities_khr( + self.internal_object(), + surface_info2.surface, + &mut surface_capabilities2.surface_capabilities, + ), + )?; + }; + } + + Ok(SurfaceCapabilities { + min_image_count: surface_capabilities2.surface_capabilities.min_image_count, + max_image_count: if surface_capabilities2.surface_capabilities.max_image_count == 0 { + None + } else { + Some(surface_capabilities2.surface_capabilities.max_image_count) + }, + current_extent: if surface_capabilities2 + .surface_capabilities + .current_extent + .width + == 0xffffffff + && surface_capabilities2 + .surface_capabilities + .current_extent + .height + == 0xffffffff + { + None + } else { + Some([ + surface_capabilities2 + .surface_capabilities + .current_extent + .width, + surface_capabilities2 + .surface_capabilities + .current_extent + .height, + ]) + }, + min_image_extent: [ + surface_capabilities2 + .surface_capabilities + .min_image_extent + .width, + surface_capabilities2 + .surface_capabilities + .min_image_extent + .height, + ], + max_image_extent: [ + surface_capabilities2 + .surface_capabilities + .max_image_extent + .width, + surface_capabilities2 + .surface_capabilities + .max_image_extent + .height, + ], + max_image_array_layers: surface_capabilities2 + .surface_capabilities + .max_image_array_layers, + supported_transforms: surface_capabilities2 + .surface_capabilities + .supported_transforms + .into(), + + current_transform: SupportedSurfaceTransforms::from( + surface_capabilities2.surface_capabilities.current_transform, + ) + .iter() + .next() + .unwrap(), // TODO: + supported_composite_alpha: surface_capabilities2 + .surface_capabilities + .supported_composite_alpha + .into(), + supported_usage_flags: { + let usage = ImageUsage::from( + surface_capabilities2 + .surface_capabilities + .supported_usage_flags, + ); + debug_assert!(usage.color_attachment); // specs say that this must be true + usage + }, + + full_screen_exclusive_supported: surface_capabilities_full_screen_exclusive + .map_or(false, |c| c.full_screen_exclusive_supported != 0), + }) + } + + /// Returns the combinations of format and color space that are supported by the physical device + /// for the given surface. + /// + /// # Panic + /// + /// - Panics if the physical device and the surface don't belong to the same instance. + pub fn surface_formats( + &self, + surface: &Surface, + surface_info: SurfaceInfo, + ) -> Result, SurfacePropertiesError> { + assert_eq!( + self.instance.internal_object(), + surface.instance().internal_object(), + ); + + if self + .instance + .enabled_extensions() + .khr_get_surface_capabilities2 + { + let SurfaceInfo { + full_screen_exclusive, + win32_monitor, + _ne: _, + } = surface_info; + + let mut surface_full_screen_exclusive_info = + if full_screen_exclusive != FullScreenExclusive::Default { + if !self.supported_extensions().ext_full_screen_exclusive { + return Err(SurfacePropertiesError::NotSupported); + } + + Some(ash::vk::SurfaceFullScreenExclusiveInfoEXT { + full_screen_exclusive: full_screen_exclusive.into(), + ..Default::default() + }) + } else { + None + }; + + let mut surface_full_screen_exclusive_win32_info = if surface.api() == SurfaceApi::Win32 + && full_screen_exclusive == FullScreenExclusive::ApplicationControlled + { + if let Some(win32_monitor) = win32_monitor { + Some(ash::vk::SurfaceFullScreenExclusiveWin32InfoEXT { + hmonitor: win32_monitor.0, + ..Default::default() + }) + } else { + return Err(SurfacePropertiesError::NotSupported); + } + } else { + if win32_monitor.is_some() { + return Err(SurfacePropertiesError::NotSupported); + } else { + None + } + }; + + let mut surface_info2 = ash::vk::PhysicalDeviceSurfaceInfo2KHR { + surface: surface.internal_object(), + ..Default::default() + }; + + 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(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 _; + } + + let mut surface_format2s; + + unsafe { + let fns = self.instance.fns(); + + let mut num = 0; + check_errors( + fns.khr_get_surface_capabilities2 + .get_physical_device_surface_formats2_khr( + self.internal_object(), + &surface_info2, + &mut num, + ptr::null_mut(), + ), + )?; + + surface_format2s = Vec::with_capacity(num as usize); + check_errors( + fns.khr_get_surface_capabilities2 + .get_physical_device_surface_formats2_khr( + self.internal_object(), + &surface_info2, + &mut num, + surface_format2s.as_mut_ptr(), + ), + )?; + surface_format2s.set_len(num as usize); + } + + Ok(surface_format2s + .into_iter() + .filter_map(|surface_format2| { + (surface_format2.surface_format.format.try_into().ok()) + .zip(surface_format2.surface_format.color_space.try_into().ok()) + }) + .collect()) + } else { + if surface_info != SurfaceInfo::default() { + return Ok(Vec::new()); + } + + let mut surface_formats; + + unsafe { + let fns = self.instance.fns(); + + let mut num = 0; + check_errors(fns.khr_surface.get_physical_device_surface_formats_khr( + self.internal_object(), + surface.internal_object(), + &mut num, + ptr::null_mut(), + ))?; + + surface_formats = Vec::with_capacity(num as usize); + check_errors(fns.khr_surface.get_physical_device_surface_formats_khr( + self.internal_object(), + surface.internal_object(), + &mut num, + surface_formats.as_mut_ptr(), + ))?; + surface_formats.set_len(num as usize); + } + + Ok(surface_formats + .into_iter() + .filter_map(|surface_format| { + (surface_format.format.try_into().ok()) + .zip(surface_format.color_space.try_into().ok()) + }) + .collect()) + } + } + + /// Returns the present modes that are supported by the physical device for the given surface. + /// + /// # Panic + /// + /// - Panics if the physical device and the surface don't belong to the same instance. + pub fn surface_present_modes( + &self, + surface: &Surface, + ) -> Result, SurfacePropertiesError> { + assert_eq!( + self.instance.internal_object(), + surface.instance().internal_object(), + ); + + let modes = unsafe { + let fns = self.instance.fns(); + + let mut num = 0; + check_errors( + fns.khr_surface + .get_physical_device_surface_present_modes_khr( + self.internal_object(), + surface.internal_object(), + &mut num, + ptr::null_mut(), + ), + )?; + + let mut modes = Vec::with_capacity(num as usize); + check_errors( + fns.khr_surface + .get_physical_device_surface_present_modes_khr( + self.internal_object(), + surface.internal_object(), + &mut num, + modes.as_mut_ptr(), + ), + )?; + modes.set_len(num as usize); + modes + }; + + debug_assert!(modes.len() > 0); + debug_assert!(modes + .iter() + .find(|&&m| m == ash::vk::PresentModeKHR::FIFO) + .is_some()); + + Ok(modes + .into_iter() + .filter_map(|mode_vk| mode_vk.try_into().ok())) + } } unsafe impl<'a> VulkanObject for PhysicalDevice<'a> { @@ -965,6 +1384,25 @@ impl<'a> QueueFamily<'a> { .queue_flags .contains(stage.required_queue_flags()) } + + /// Returns whether queues of this family can draw on the given surface. + pub fn supports_surface( + &self, + surface: &Surface, + ) -> Result { + unsafe { + let fns = self.physical_device.instance.fns(); + + let mut output = MaybeUninit::uninit(); + check_errors(fns.khr_surface.get_physical_device_surface_support_khr( + self.physical_device.internal_object(), + self.id, + surface.internal_object(), + output.as_mut_ptr(), + ))?; + Ok(output.assume_init() != 0) + } + } } impl<'a> PartialEq for QueueFamily<'a> { @@ -1485,3 +1923,81 @@ impl From for ImageFormatProperties { } } } + +/// Parameters for [`PhysicalDevice::surface_capabilities`] and +/// [`PhysicalDevice::surface_formats`]. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct SurfaceInfo { + pub full_screen_exclusive: FullScreenExclusive, + pub win32_monitor: Option, + pub _ne: crate::NonExhaustive, +} + +impl Default for SurfaceInfo { + #[inline] + fn default() -> Self { + Self { + full_screen_exclusive: FullScreenExclusive::Default, + win32_monitor: None, + _ne: crate::NonExhaustive(()), + } + } +} + +/// Error that can happen when retrieving properties of a surface. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[repr(u32)] +pub enum SurfacePropertiesError { + /// Not enough memory. + OomError(OomError), + + /// The surface is no longer accessible and must be recreated. + SurfaceLost, + + // The given `SurfaceInfo` values are not supported for the surface by the physical device. + NotSupported, +} + +impl error::Error for SurfacePropertiesError { + #[inline] + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + match *self { + Self::OomError(ref err) => Some(err), + _ => None, + } + } +} + +impl fmt::Display for SurfacePropertiesError { + #[inline] + fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!( + fmt, + "{}", + match *self { + Self::OomError(_) => "not enough memory", + Self::SurfaceLost => "the surface is no longer valid", + Self::NotSupported => "the given `SurfaceInfo` values are not supported for the surface by the physical device", + } + ) + } +} + +impl From for SurfacePropertiesError { + #[inline] + fn from(err: OomError) -> SurfacePropertiesError { + Self::OomError(err) + } +} + +impl From for SurfacePropertiesError { + #[inline] + fn from(err: Error) -> SurfacePropertiesError { + match err { + err @ Error::OutOfHostMemory => Self::OomError(OomError::from(err)), + err @ Error::OutOfDeviceMemory => Self::OomError(OomError::from(err)), + Error::SurfaceLost => Self::SurfaceLost, + _ => panic!("unexpected error: {:?}", err), + } + } +} diff --git a/vulkano/src/image/swapchain.rs b/vulkano/src/image/swapchain.rs index 5462e680..db92c9e4 100644 --- a/vulkano/src/image/swapchain.rs +++ b/vulkano/src/image/swapchain.rs @@ -115,7 +115,7 @@ where #[inline] fn try_gpu_lock(&self, _: bool, _: bool, _: ImageLayout) -> Result<(), AccessError> { - if self.swapchain.is_fullscreen_exclusive() { + if self.swapchain.is_full_screen_exclusive() { Ok(()) } else { // Swapchain image are only accessible after being acquired. @@ -158,7 +158,7 @@ where { #[inline] fn decode(&self, value: ClearValue) -> Option { - Some(self.swapchain.format().decode_clear_value(value)) + Some(self.swapchain.image_format().decode_clear_value(value)) } } diff --git a/vulkano/src/lib.rs b/vulkano/src/lib.rs index fc0aba8b..f10193b2 100644 --- a/vulkano/src/lib.rs +++ b/vulkano/src/lib.rs @@ -198,7 +198,7 @@ pub(crate) enum Error { IncompatibleDisplay = ash::vk::Result::ERROR_INCOMPATIBLE_DISPLAY_KHR.as_raw(), ValidationFailed = ash::vk::Result::ERROR_VALIDATION_FAILED_EXT.as_raw(), OutOfPoolMemory = ash::vk::Result::ERROR_OUT_OF_POOL_MEMORY_KHR.as_raw(), - FullscreenExclusiveLost = ash::vk::Result::ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT.as_raw(), + FullScreenExclusiveLost = ash::vk::Result::ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT.as_raw(), } /// Checks whether the result returned correctly. @@ -229,7 +229,7 @@ fn check_errors(result: ash::vk::Result) -> Result { ash::vk::Result::ERROR_VALIDATION_FAILED_EXT => Err(Error::ValidationFailed), ash::vk::Result::ERROR_OUT_OF_POOL_MEMORY_KHR => Err(Error::OutOfPoolMemory), ash::vk::Result::ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT => { - Err(Error::FullscreenExclusiveLost) + Err(Error::FullScreenExclusiveLost) } ash::vk::Result::ERROR_INVALID_SHADER_NV => panic!( "Vulkan function returned \ diff --git a/vulkano/src/swapchain/capabilities.rs b/vulkano/src/swapchain/capabilities.rs deleted file mode 100644 index 95c606f1..00000000 --- a/vulkano/src/swapchain/capabilities.rs +++ /dev/null @@ -1,585 +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 crate::format::Format; -use crate::image::ImageUsage; - -/// The capabilities of a surface when used by a physical device. -/// -/// You have to match these capabilities when you create a swapchain. -#[derive(Clone, Debug)] -pub struct Capabilities { - /// Minimum number of images that must be present in the swapchain. - pub min_image_count: u32, - - /// Maximum number of images that must be present in the swapchain, or `None` if there is no - /// maximum value. Note that "no maximum" doesn't mean that you can set a very high value, as - /// 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. - pub current_extent: Option<[u32; 2]>, - - /// Minimum width and height of a swapchain that uses this surface. - pub min_image_extent: [u32; 2], - - /// Maximum width and height of a swapchain that uses this surface. - pub max_image_extent: [u32; 2], - - /// Maximum number of image layers if you create an image array. The minimum is 1. - pub max_image_array_layers: u32, - - /// List of transforms supported for the swapchain. - pub supported_transforms: SupportedSurfaceTransforms, - - /// Current transform used by the surface. - pub current_transform: SurfaceTransform, - - /// List of composite alpha modes supports for the swapchain. - pub supported_composite_alpha: SupportedCompositeAlpha, - - /// List of image usages that are supported for images of the swapchain. Only - /// the `color_attachment` usage is guaranteed to be supported. - pub supported_usage_flags: ImageUsage, - - /// List of formats supported for the swapchain. - pub supported_formats: Vec<(Format, ColorSpace)>, // TODO: https://github.com/KhronosGroup/Vulkan-Docs/issues/207 - - /// List of present modes that are supported. `Fifo` is always guaranteed to be supported. - pub present_modes: SupportedPresentModes, -} - -/// The way presenting a swapchain is accomplished. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[repr(i32)] -pub enum PresentMode { - /// Immediately shows the image to the user. May result in visible tearing. - Immediate = ash::vk::PresentModeKHR::IMMEDIATE.as_raw(), - - /// The action of presenting an image puts it in wait. When the next vertical blanking period - /// happens, the waiting image is effectively shown to the user. If an image is presented while - /// another one is waiting, it is replaced. - Mailbox = ash::vk::PresentModeKHR::MAILBOX.as_raw(), - - /// The action of presenting an image adds it to a queue of images. At each vertical blanking - /// period, the queue is popped and an image is presented. - /// - /// Guaranteed to be always supported. - /// - /// This is the equivalent of OpenGL's `SwapInterval` with a value of 1. - Fifo = ash::vk::PresentModeKHR::FIFO.as_raw(), - - /// Same as `Fifo`, except that if the queue was empty during the previous vertical blanking - /// period then it is equivalent to `Immediate`. - /// - /// This is the equivalent of OpenGL's `SwapInterval` with a value of -1. - Relaxed = ash::vk::PresentModeKHR::FIFO_RELAXED.as_raw(), - // TODO: These can't be enabled yet because they have to be used with shared present surfaces - // which vulkano doesnt support yet. - //SharedDemand = ash::vk::PresentModeKHR::SHARED_DEMAND_REFRESH, - //SharedContinuous = ash::vk::PresentModeKHR::SHARED_CONTINUOUS_REFRESH, -} - -impl From for ash::vk::PresentModeKHR { - #[inline] - fn from(val: PresentMode) -> Self { - Self::from_raw(val as i32) - } -} - -/// List of `PresentMode`s that are supported. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct SupportedPresentModes { - pub immediate: bool, - pub mailbox: bool, - pub fifo: bool, - pub relaxed: bool, - pub shared_demand: bool, - pub shared_continuous: bool, -} - -impl FromIterator for SupportedPresentModes { - fn from_iter(iter: T) -> Self - where - T: IntoIterator, - { - let mut result = SupportedPresentModes::none(); - for e in iter { - match e { - ash::vk::PresentModeKHR::IMMEDIATE => result.immediate = true, - ash::vk::PresentModeKHR::MAILBOX => result.mailbox = true, - ash::vk::PresentModeKHR::FIFO => result.fifo = true, - ash::vk::PresentModeKHR::FIFO_RELAXED => result.relaxed = true, - ash::vk::PresentModeKHR::SHARED_DEMAND_REFRESH => result.shared_demand = true, - ash::vk::PresentModeKHR::SHARED_CONTINUOUS_REFRESH => { - result.shared_continuous = true - } - _ => {} - } - } - result - } -} - -impl SupportedPresentModes { - /// Builds a `SupportedPresentModes` with all fields set to false. - #[inline] - pub fn none() -> SupportedPresentModes { - SupportedPresentModes { - immediate: false, - mailbox: false, - fifo: false, - relaxed: false, - shared_demand: false, - shared_continuous: false, - } - } - - /// Returns true if the given present mode is in this list of supported modes. - #[inline] - pub fn supports(&self, mode: PresentMode) -> bool { - match mode { - PresentMode::Immediate => self.immediate, - PresentMode::Mailbox => self.mailbox, - PresentMode::Fifo => self.fifo, - PresentMode::Relaxed => self.relaxed, - } - } - - /// Returns an iterator to the list of supported present modes. - #[inline] - pub fn iter(&self) -> impl Iterator { - let moved = *self; - [ - PresentMode::Immediate, - PresentMode::Mailbox, - PresentMode::Fifo, - PresentMode::Relaxed, - ] - .into_iter() - .filter(move |&mode| moved.supports(mode)) - } -} - -/// A transformation to apply to the image before showing it on the screen. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[repr(u32)] -pub enum SurfaceTransform { - /// Don't transform the image. - Identity = ash::vk::SurfaceTransformFlagsKHR::IDENTITY.as_raw(), - /// Rotate 90 degrees. - Rotate90 = ash::vk::SurfaceTransformFlagsKHR::ROTATE_90.as_raw(), - /// Rotate 180 degrees. - Rotate180 = ash::vk::SurfaceTransformFlagsKHR::ROTATE_180.as_raw(), - /// Rotate 270 degrees. - Rotate270 = ash::vk::SurfaceTransformFlagsKHR::ROTATE_270.as_raw(), - /// Mirror the image horizontally. - HorizontalMirror = ash::vk::SurfaceTransformFlagsKHR::HORIZONTAL_MIRROR.as_raw(), - /// Mirror the image horizontally and rotate 90 degrees. - HorizontalMirrorRotate90 = - ash::vk::SurfaceTransformFlagsKHR::HORIZONTAL_MIRROR_ROTATE_90.as_raw(), - /// Mirror the image horizontally and rotate 180 degrees. - HorizontalMirrorRotate180 = - ash::vk::SurfaceTransformFlagsKHR::HORIZONTAL_MIRROR_ROTATE_180.as_raw(), - /// Mirror the image horizontally and rotate 270 degrees. - HorizontalMirrorRotate270 = - ash::vk::SurfaceTransformFlagsKHR::HORIZONTAL_MIRROR_ROTATE_270.as_raw(), - /// Let the operating system or driver implementation choose. - Inherit = ash::vk::SurfaceTransformFlagsKHR::INHERIT.as_raw(), -} - -impl From for ash::vk::SurfaceTransformFlagsKHR { - #[inline] - fn from(val: SurfaceTransform) -> Self { - Self::from_raw(val as u32) - } -} - -/// How the alpha values of the pixels of the window are treated. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[repr(u32)] -pub enum CompositeAlpha { - /// The alpha channel of the image is ignored. All the pixels are considered as if they have a - /// value of 1.0. - Opaque = ash::vk::CompositeAlphaFlagsKHR::OPAQUE.as_raw(), - - /// The alpha channel of the image is respected. The color channels are expected to have - /// already been multiplied by the alpha value. - PreMultiplied = ash::vk::CompositeAlphaFlagsKHR::PRE_MULTIPLIED.as_raw(), - - /// The alpha channel of the image is respected. The color channels will be multiplied by the - /// alpha value by the compositor before being added to what is behind. - PostMultiplied = ash::vk::CompositeAlphaFlagsKHR::POST_MULTIPLIED.as_raw(), - - /// Let the operating system or driver implementation choose. - Inherit = ash::vk::CompositeAlphaFlagsKHR::INHERIT.as_raw(), -} - -impl From for ash::vk::CompositeAlphaFlagsKHR { - #[inline] - fn from(val: CompositeAlpha) -> Self { - Self::from_raw(val as u32) - } -} - -/// List of supported composite alpha modes. -/// -/// See the docs of `CompositeAlpha`. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[allow(missing_docs)] -pub struct SupportedCompositeAlpha { - pub opaque: bool, - pub pre_multiplied: bool, - pub post_multiplied: bool, - pub inherit: bool, -} - -impl From for SupportedCompositeAlpha { - #[inline] - fn from(val: ash::vk::CompositeAlphaFlagsKHR) -> SupportedCompositeAlpha { - let mut result = SupportedCompositeAlpha::none(); - if !(val & ash::vk::CompositeAlphaFlagsKHR::OPAQUE).is_empty() { - result.opaque = true; - } - if !(val & ash::vk::CompositeAlphaFlagsKHR::PRE_MULTIPLIED).is_empty() { - result.pre_multiplied = true; - } - if !(val & ash::vk::CompositeAlphaFlagsKHR::POST_MULTIPLIED).is_empty() { - result.post_multiplied = true; - } - if !(val & ash::vk::CompositeAlphaFlagsKHR::INHERIT).is_empty() { - result.inherit = true; - } - result - } -} - -impl SupportedCompositeAlpha { - /// Builds a `SupportedCompositeAlpha` with all fields set to false. - #[inline] - pub fn none() -> SupportedCompositeAlpha { - SupportedCompositeAlpha { - opaque: false, - pre_multiplied: false, - post_multiplied: false, - inherit: false, - } - } - - /// Returns true if the given `CompositeAlpha` is in this list. - #[inline] - pub fn supports(&self, value: CompositeAlpha) -> bool { - match value { - CompositeAlpha::Opaque => self.opaque, - CompositeAlpha::PreMultiplied => self.pre_multiplied, - CompositeAlpha::PostMultiplied => self.post_multiplied, - CompositeAlpha::Inherit => self.inherit, - } - } - - /// Returns an iterator to the list of supported composite alpha. - #[inline] - pub fn iter(&self) -> impl Iterator { - let moved = *self; - [ - CompositeAlpha::Opaque, - CompositeAlpha::PreMultiplied, - CompositeAlpha::PostMultiplied, - CompositeAlpha::Inherit, - ] - .into_iter() - .filter(move |&mode| moved.supports(mode)) - } -} - -/// List of supported composite alpha modes. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct SupportedSurfaceTransforms { - pub identity: bool, - pub rotate90: bool, - pub rotate180: bool, - pub rotate270: bool, - pub horizontal_mirror: bool, - pub horizontal_mirror_rotate90: bool, - pub horizontal_mirror_rotate180: bool, - pub horizontal_mirror_rotate270: bool, - pub inherit: bool, -} - -impl From for SupportedSurfaceTransforms { - fn from(val: ash::vk::SurfaceTransformFlagsKHR) -> Self { - macro_rules! v { - ($val:expr, $out:ident, $e:expr, $f:ident) => { - if !($val & $e).is_empty() { - $out.$f = true; - } - }; - } - - let mut result = SupportedSurfaceTransforms::none(); - v!( - val, - result, - ash::vk::SurfaceTransformFlagsKHR::IDENTITY, - identity - ); - v!( - val, - result, - ash::vk::SurfaceTransformFlagsKHR::ROTATE_90, - rotate90 - ); - v!( - val, - result, - ash::vk::SurfaceTransformFlagsKHR::ROTATE_180, - rotate180 - ); - v!( - val, - result, - ash::vk::SurfaceTransformFlagsKHR::ROTATE_270, - rotate270 - ); - v!( - val, - result, - ash::vk::SurfaceTransformFlagsKHR::HORIZONTAL_MIRROR, - horizontal_mirror - ); - v!( - val, - result, - ash::vk::SurfaceTransformFlagsKHR::HORIZONTAL_MIRROR_ROTATE_90, - horizontal_mirror_rotate90 - ); - v!( - val, - result, - ash::vk::SurfaceTransformFlagsKHR::HORIZONTAL_MIRROR_ROTATE_180, - horizontal_mirror_rotate180 - ); - v!( - val, - result, - ash::vk::SurfaceTransformFlagsKHR::HORIZONTAL_MIRROR_ROTATE_270, - horizontal_mirror_rotate270 - ); - v!( - val, - result, - ash::vk::SurfaceTransformFlagsKHR::INHERIT, - inherit - ); - result - } -} - -impl SupportedSurfaceTransforms { - /// Builds a `SupportedSurfaceTransforms` with all fields set to false. - #[inline] - pub fn none() -> SupportedSurfaceTransforms { - SupportedSurfaceTransforms { - identity: false, - rotate90: false, - rotate180: false, - rotate270: false, - horizontal_mirror: false, - horizontal_mirror_rotate90: false, - horizontal_mirror_rotate180: false, - horizontal_mirror_rotate270: false, - inherit: false, - } - } - - /// Returns true if the given `SurfaceTransform` is in this list. - #[inline] - pub fn supports(&self, value: SurfaceTransform) -> bool { - match value { - SurfaceTransform::Identity => self.identity, - SurfaceTransform::Rotate90 => self.rotate90, - SurfaceTransform::Rotate180 => self.rotate180, - SurfaceTransform::Rotate270 => self.rotate270, - SurfaceTransform::HorizontalMirror => self.horizontal_mirror, - SurfaceTransform::HorizontalMirrorRotate90 => self.horizontal_mirror_rotate90, - SurfaceTransform::HorizontalMirrorRotate180 => self.horizontal_mirror_rotate180, - SurfaceTransform::HorizontalMirrorRotate270 => self.horizontal_mirror_rotate270, - SurfaceTransform::Inherit => self.inherit, - } - } - - /// Returns an iterator to the list of supported composite alpha. - #[inline] - pub fn iter(&self) -> impl Iterator { - let moved = *self; - [ - SurfaceTransform::Identity, - SurfaceTransform::Rotate90, - SurfaceTransform::Rotate180, - SurfaceTransform::Rotate270, - SurfaceTransform::HorizontalMirror, - SurfaceTransform::HorizontalMirrorRotate90, - SurfaceTransform::HorizontalMirrorRotate180, - SurfaceTransform::HorizontalMirrorRotate270, - SurfaceTransform::Inherit, - ] - .into_iter() - .filter(move |&mode| moved.supports(mode)) - } -} - -impl Default for SurfaceTransform { - #[inline] - fn default() -> SurfaceTransform { - SurfaceTransform::Identity - } -} - -/// How the presentation engine should interpret the data. -/// -/// # A quick lesson about color spaces -/// -/// ## What is a color space? -/// -/// Each pixel of a monitor is made of three components: one red, one green, and one blue. In the -/// past, computers would simply send to the monitor the intensity of each of the three components. -/// -/// This proved to be problematic, because depending on the brand of the monitor the colors would -/// not exactly be the same. For example on some monitors, a value of `[1.0, 0.0, 0.0]` would be a -/// bit more orange than on others. -/// -/// In order to standardize this, there exist what are called *color spaces*: sRGB, AdobeRGB, -/// DCI-P3, scRGB, etc. When you manipulate RGB values in a specific color space, these values have -/// a precise absolute meaning in terms of color, that is the same across all systems and monitors. -/// -/// > **Note**: Color spaces are orthogonal to concept of RGB. *RGB* only indicates what is the -/// > representation of the data, but not how it is interpreted. You can think of this a bit like -/// > text encoding. An *RGB* value is a like a byte, in other words it is the medium by which -/// > values are communicated, and a *color space* is like a text encoding (eg. UTF-8), in other -/// > words it is the way the value should be interpreted. -/// -/// The most commonly used color space today is sRGB. Most monitors today use this color space, -/// and most images files are encoded in this color space. -/// -/// ## Pixel formats and linear vs non-linear -/// -/// In Vulkan all images have a specific format in which the data is stored. The data of an image -/// consists of pixels in RGB but contains no information about the color space (or lack thereof) -/// of these pixels. You are free to store them in whatever color space you want. -/// -/// But one big practical problem with color spaces is that they are sometimes not linear, and in -/// particular the popular sRGB color space is not linear. In a non-linear color space, a value of -/// `[0.6, 0.6, 0.6]` for example is **not** twice as bright as a value of `[0.3, 0.3, 0.3]`. This -/// is problematic, because operations such as taking the average of two colors or calculating the -/// lighting of a texture with a dot product are mathematically incorrect and will produce -/// incorrect colors. -/// -/// > **Note**: If the texture format has an alpha component, it is not affected by the color space -/// > and always behaves linearly. -/// -/// In order to solve this Vulkan also provides image formats with the `Srgb` suffix, which are -/// expected to contain RGB data in the sRGB color space. When you sample an image with such a -/// format from a shader, the implementation will automatically turn the pixel values into a linear -/// color space that is suitable for linear operations (such as additions or multiplications). -/// When you write to a framebuffer attachment with such a format, the implementation will -/// automatically perform the opposite conversion. These conversions are most of the time performed -/// by the hardware and incur no additional cost. -/// -/// ## Color space of the swapchain -/// -/// The color space that you specify when you create a swapchain is how the implementation will -/// interpret the raw data inside of the image. -/// -/// > **Note**: The implementation can choose to send the data in the swapchain image directly to -/// > the monitor, but it can also choose to write it in an intermediary buffer that is then read -/// > by the operating system or windowing system. Therefore the color space that the -/// > implementation supports is not necessarily the same as the one supported by the monitor. -/// -/// It is *your* job to ensure that the data in the swapchain image is in the color space -/// that is specified here, otherwise colors will be incorrect. -/// The implementation will never perform any additional automatic conversion after the colors have -/// been written to the swapchain image. -/// -/// # How do I handle this correctly? -/// -/// The easiest way to handle color spaces in a cross-platform program is: -/// -/// - Always request the `SrgbNonLinear` color space when creating the swapchain. -/// - Make sure that all your image files use the sRGB color space, and load them in images whose -/// format has the `Srgb` suffix. Only use non-sRGB image formats for intermediary computations -/// or to store non-color data. -/// - Swapchain images should have a format with the `Srgb` suffix. -/// -/// > **Note**: It is unclear whether the `SrgbNonLinear` color space is always supported by the -/// > the implementation or not. See . -/// -/// > **Note**: Lots of developers are confused by color spaces. You can sometimes find articles -/// > talking about gamma correction and suggestion to put your colors to the power 2.2 for -/// > example. These are all hacks and you should use the sRGB pixel formats instead. -/// -/// If you follow these three rules, then everything should render the same way on all platforms. -/// -/// Additionally you can try detect whether the implementation supports any additional color space -/// and perform a manual conversion to that color space from inside your shader. -/// -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[repr(i32)] -pub enum ColorSpace { - SrgbNonLinear = ash::vk::ColorSpaceKHR::SRGB_NONLINEAR.as_raw(), - DisplayP3NonLinear = ash::vk::ColorSpaceKHR::DISPLAY_P3_NONLINEAR_EXT.as_raw(), - ExtendedSrgbLinear = ash::vk::ColorSpaceKHR::EXTENDED_SRGB_LINEAR_EXT.as_raw(), - ExtendedSrgbNonLinear = ash::vk::ColorSpaceKHR::EXTENDED_SRGB_NONLINEAR_EXT.as_raw(), - DisplayP3Linear = ash::vk::ColorSpaceKHR::DISPLAY_P3_LINEAR_EXT.as_raw(), - DciP3NonLinear = ash::vk::ColorSpaceKHR::DCI_P3_NONLINEAR_EXT.as_raw(), - Bt709Linear = ash::vk::ColorSpaceKHR::BT709_LINEAR_EXT.as_raw(), - Bt709NonLinear = ash::vk::ColorSpaceKHR::BT709_NONLINEAR_EXT.as_raw(), - Bt2020Linear = ash::vk::ColorSpaceKHR::BT2020_LINEAR_EXT.as_raw(), - Hdr10St2084 = ash::vk::ColorSpaceKHR::HDR10_ST2084_EXT.as_raw(), - DolbyVision = ash::vk::ColorSpaceKHR::DOLBYVISION_EXT.as_raw(), - Hdr10Hlg = ash::vk::ColorSpaceKHR::HDR10_HLG_EXT.as_raw(), - AdobeRgbLinear = ash::vk::ColorSpaceKHR::ADOBERGB_LINEAR_EXT.as_raw(), - AdobeRgbNonLinear = ash::vk::ColorSpaceKHR::ADOBERGB_NONLINEAR_EXT.as_raw(), - PassThrough = ash::vk::ColorSpaceKHR::PASS_THROUGH_EXT.as_raw(), - DisplayNative = ash::vk::ColorSpaceKHR::DISPLAY_NATIVE_AMD.as_raw(), -} - -impl From for ash::vk::ColorSpaceKHR { - #[inline] - fn from(val: ColorSpace) -> Self { - Self::from_raw(val as i32) - } -} - -impl From for ColorSpace { - #[inline] - fn from(val: ash::vk::ColorSpaceKHR) -> Self { - match val { - ash::vk::ColorSpaceKHR::SRGB_NONLINEAR => ColorSpace::SrgbNonLinear, - ash::vk::ColorSpaceKHR::DISPLAY_P3_NONLINEAR_EXT => ColorSpace::DisplayP3NonLinear, - ash::vk::ColorSpaceKHR::EXTENDED_SRGB_LINEAR_EXT => ColorSpace::ExtendedSrgbLinear, - ash::vk::ColorSpaceKHR::EXTENDED_SRGB_NONLINEAR_EXT => { - ColorSpace::ExtendedSrgbNonLinear - } - ash::vk::ColorSpaceKHR::DISPLAY_P3_LINEAR_EXT => ColorSpace::DisplayP3Linear, - ash::vk::ColorSpaceKHR::DCI_P3_NONLINEAR_EXT => ColorSpace::DciP3NonLinear, - ash::vk::ColorSpaceKHR::BT709_LINEAR_EXT => ColorSpace::Bt709Linear, - ash::vk::ColorSpaceKHR::BT709_NONLINEAR_EXT => ColorSpace::Bt709NonLinear, - ash::vk::ColorSpaceKHR::BT2020_LINEAR_EXT => ColorSpace::Bt2020Linear, - ash::vk::ColorSpaceKHR::HDR10_ST2084_EXT => ColorSpace::Hdr10St2084, - ash::vk::ColorSpaceKHR::DOLBYVISION_EXT => ColorSpace::DolbyVision, - ash::vk::ColorSpaceKHR::HDR10_HLG_EXT => ColorSpace::Hdr10Hlg, - ash::vk::ColorSpaceKHR::ADOBERGB_LINEAR_EXT => ColorSpace::AdobeRgbLinear, - ash::vk::ColorSpaceKHR::ADOBERGB_NONLINEAR_EXT => ColorSpace::AdobeRgbNonLinear, - ash::vk::ColorSpaceKHR::PASS_THROUGH_EXT => ColorSpace::PassThrough, - ash::vk::ColorSpaceKHR::DISPLAY_NATIVE_AMD => ColorSpace::DisplayNative, - _ => panic!("Wrong value for color space enum {:?}", val), - } - } -} diff --git a/vulkano/src/swapchain/display.rs b/vulkano/src/swapchain/display.rs index 67f135ea..0e02dd85 100644 --- a/vulkano/src/swapchain/display.rs +++ b/vulkano/src/swapchain/display.rs @@ -22,7 +22,7 @@ //! rate. You can enumerate the modes available on a display with `Display::display_modes`, or //! attempt to create your own mode with `TODO`. //! - Choose a `DisplayPlane`. A display can show multiple planes in a stacking fashion. -//! - Create a `Surface` object with `Surface::from_display_mode` and pass the chosen `DisplayMode` +//! - Create a `Surface` object with `Surface::from_display_plane` and pass the chosen `DisplayMode` //! and `DisplayPlane`. #![allow(dead_code)] // TODO: this module isn't finished diff --git a/vulkano/src/swapchain/mod.rs b/vulkano/src/swapchain/mod.rs index 2ac6e776..d56c8d70 100644 --- a/vulkano/src/swapchain/mod.rs +++ b/vulkano/src/swapchain/mod.rs @@ -40,7 +40,7 @@ //! - `VK_KHR_win32_surface` //! //! For example if you want to create a surface from an Android surface, you will have to enable -//! the `VK_KHR_android_surface` extension and use `Surface::from_anativewindow`. +//! the `VK_KHR_android_surface` extension and use `Surface::from_android`. //! See the documentation of `Surface` for all the possible constructors. //! //! Trying to use one of these functions without enabling the proper extension will result in an @@ -85,7 +85,7 @@ //! let window = build_window(); // Third-party function, not provided by vulkano //! let _surface = unsafe { //! let hinstance: *const () = ptr::null(); // Windows-specific object -//! Surface::from_hwnd(instance.clone(), hinstance, window.hwnd(), Arc::clone(&window)).unwrap() +//! Surface::from_win32(instance.clone(), hinstance, window.hwnd(), Arc::clone(&window)).unwrap() //! }; //! ``` //! @@ -114,8 +114,7 @@ //! - How the alpha of the final output will be interpreted. //! - How to perform the cycling between images in regard to vsync. //! -//! You can query the supported values of all these properties with -//! [`Surface::capabilities`](crate::swapchain::Surface::capabilities). +//! You can query the supported values of all these properties from the physical device. //! //! ## Creating a swapchain //! @@ -131,7 +130,9 @@ //! ``` //! //! Then, query the capabilities of the surface with -//! [`Surface::capabilities`](crate::swapchain::Surface::capabilities) +//! [`PhysicalDevice::surface_capabilities`](crate::device::physical::PhysicalDevice::surface_capabilities) +//! and +//! [`PhysicalDevice::surface_formats`](crate::device::physical::PhysicalDevice::surface_formats) //! and choose which values you are going to use. //! //! ```no_run @@ -140,27 +141,31 @@ //! # use vulkano::swapchain::Surface; //! # use std::cmp::{max, min}; //! # fn choose_caps(device: Arc, surface: Arc>) -> Result<(), Box> { -//! let caps = surface.capabilities(device.physical_device())?; +//! let surface_capabilities = device +//! .physical_device() +//! .surface_capabilities(&surface, Default::default())?; //! //! // Use the current window size or some fixed resolution. -//! let dimensions = caps.current_extent.unwrap_or([640, 480]); +//! let image_extent = surface_capabilities.current_extent.unwrap_or([640, 480]); //! //! // Try to use double-buffering. -//! let buffers_count = match caps.max_image_count { -//! None => max(2, caps.min_image_count), -//! Some(limit) => min(max(2, caps.min_image_count), limit) +//! let min_image_count = match surface_capabilities.max_image_count { +//! None => max(2, surface_capabilities.min_image_count), +//! Some(limit) => min(max(2, surface_capabilities.min_image_count), limit) //! }; //! //! // Preserve the current surface transform. -//! let transform = caps.current_transform; +//! let pre_transform = surface_capabilities.current_transform; //! //! // Use the first available format. -//! let (format, color_space) = caps.supported_formats[0]; +//! let (image_format, color_space) = device +//! .physical_device() +//! .surface_formats(&surface, Default::default())?[0]; //! # Ok(()) //! # } //! ``` //! -//! Then, call [`Swapchain::start`](crate::swapchain::Swapchain::start). +//! Then, call [`Swapchain::new`](crate::swapchain::Swapchain::new). //! //! ```no_run //! # use std::sync::Arc; @@ -168,43 +173,46 @@ //! # use vulkano::image::ImageUsage; //! # use vulkano::sync::SharingMode; //! # use vulkano::format::Format; -//! # use vulkano::swapchain::{Surface, Swapchain, SurfaceTransform, PresentMode, CompositeAlpha, ColorSpace, FullscreenExclusive}; +//! # use vulkano::swapchain::{Surface, Swapchain, SurfaceTransform, PresentMode, CompositeAlpha, ColorSpace, FullScreenExclusive, SwapchainCreateInfo}; //! # fn create_swapchain( -//! # device: Arc, surface: Arc>, present_queue: Arc, -//! # buffers_count: u32, format: Format, dimensions: [u32; 2], -//! # surface_transform: SurfaceTransform, composite_alpha: CompositeAlpha, -//! # present_mode: PresentMode, fullscreen_exclusive: FullscreenExclusive +//! # device: Arc, surface: Arc>, +//! # min_image_count: u32, image_format: Format, image_extent: [u32; 2], +//! # pre_transform: SurfaceTransform, composite_alpha: CompositeAlpha, +//! # present_mode: PresentMode, full_screen_exclusive: FullScreenExclusive //! # ) -> Result<(), Box> { //! // The created swapchain will be used as a color attachment for rendering. -//! let usage = ImageUsage { +//! let image_usage = ImageUsage { //! color_attachment: true, //! .. ImageUsage::none() //! }; //! -//! // Create the swapchain and its buffers. -//! let (swapchain, buffers) = Swapchain::start( +//! // Create the swapchain and its images. +//! let (swapchain, images) = Swapchain::new( //! // Create the swapchain in this `device`'s memory. //! device, //! // The surface where the images will be presented. //! surface, -//! ) -//! // How many buffers to use in the swapchain. -//! .num_images(buffers_count) -//! // The format of the images. -//! .format(format) -//! // The size of each image. -//! .dimensions(dimensions) -//! // What the images are going to be used for. -//! .usage(usage) -//! // What transformation to use with the surface. -//! .transform(surface_transform) -//! // How to handle the alpha channel. -//! .composite_alpha(composite_alpha) -//! // How to present images. -//! .present_mode(present_mode) -//! // How to handle fullscreen exclusivity -//! .fullscreen_exclusive(fullscreen_exclusive) -//! .build()?; +//! // The creation parameters. +//! SwapchainCreateInfo { +//! // How many images to use in the swapchain. +//! min_image_count, +//! // The format of the images. +//! image_format: Some(image_format), +//! // The size of each image. +//! image_extent, +//! // What the images are going to be used for. +//! image_usage, +//! // What transformation to use with the surface. +//! pre_transform, +//! // How to handle the alpha channel. +//! composite_alpha, +//! // How to present images. +//! present_mode, +//! // How to handle full-screen exclusivity +//! full_screen_exclusive, +//! ..Default::default() +//! } +//! )?; //! //! # Ok(()) //! # } @@ -262,22 +270,27 @@ //! //! ``` //! use vulkano::swapchain; -//! use vulkano::swapchain::AcquireError; +//! use vulkano::swapchain::{AcquireError, SwapchainCreateInfo}; //! use vulkano::sync::GpuFuture; //! -//! // let mut swapchain = Swapchain::new(...); -//! # let mut swapchain: (::std::sync::Arc<::vulkano::swapchain::Swapchain<()>>, _) = return; +//! // let (swapchain, images) = Swapchain::new(...); +//! # let mut swapchain: ::std::sync::Arc<::vulkano::swapchain::Swapchain<()>> = return; +//! # let mut images: Vec<::std::sync::Arc<::vulkano::image::SwapchainImage<()>>> = return; //! # let queue: ::std::sync::Arc<::vulkano::device::Queue> = return; //! let mut recreate_swapchain = false; //! //! loop { //! if recreate_swapchain { -//! swapchain = swapchain.0.recreate().dimensions([1024, 768]).build().unwrap(); +//! let (new_swapchain, new_images) = swapchain.recreate(SwapchainCreateInfo { +//! image_extent: [1024, 768], +//! ..swapchain.create_info() +//! }) +//! .unwrap(); +//! swapchain = new_swapchain; +//! images = new_images; //! recreate_swapchain = false; //! } //! -//! let (ref swapchain, ref _images) = swapchain; -//! //! let (index, suboptimal, acq_future) = match swapchain::acquire_next_image(swapchain.clone(), None) { //! Ok(r) => r, //! Err(AcquireError::OutOfDate) => { recreate_swapchain = true; continue; }, @@ -298,35 +311,34 @@ //! ``` //! -pub use self::capabilities::Capabilities; -pub use self::capabilities::ColorSpace; -pub use self::capabilities::CompositeAlpha; -pub use self::capabilities::PresentMode; -pub use self::capabilities::SupportedCompositeAlpha; -pub use self::capabilities::SupportedPresentModes; -pub use self::capabilities::SupportedSurfaceTransforms; -pub use self::capabilities::SurfaceTransform; pub use self::present_region::PresentRegion; pub use self::present_region::RectangleLayer; -pub use self::surface::CapabilitiesError; +pub use self::surface::ColorSpace; +pub use self::surface::CompositeAlpha; +pub use self::surface::PresentMode; +pub use self::surface::SupportedCompositeAlpha; +pub use self::surface::SupportedSurfaceTransforms; pub use self::surface::Surface; +pub use self::surface::SurfaceApi; +pub use self::surface::SurfaceCapabilities; pub use self::surface::SurfaceCreationError; +pub use self::surface::SurfaceTransform; pub use self::swapchain::acquire_next_image; pub use self::swapchain::acquire_next_image_raw; pub use self::swapchain::present; pub use self::swapchain::present_incremental; pub use self::swapchain::AcquireError; pub use self::swapchain::AcquiredImage; -pub use self::swapchain::FullscreenExclusive; -pub use self::swapchain::FullscreenExclusiveError; +pub use self::swapchain::FullScreenExclusive; +pub use self::swapchain::FullScreenExclusiveError; pub use self::swapchain::PresentFuture; pub use self::swapchain::Swapchain; pub use self::swapchain::SwapchainAcquireFuture; -pub use self::swapchain::SwapchainBuilder; +pub use self::swapchain::SwapchainCreateInfo; pub use self::swapchain::SwapchainCreationError; +pub use self::swapchain::Win32Monitor; use std::sync::atomic::AtomicBool; -mod capabilities; pub mod display; mod present_region; mod surface; diff --git a/vulkano/src/swapchain/present_region.rs b/vulkano/src/swapchain/present_region.rs index 7e5fdf42..ee769199 100644 --- a/vulkano/src/swapchain/present_region.rs +++ b/vulkano/src/swapchain/present_region.rs @@ -45,9 +45,9 @@ impl RectangleLayer { // FIXME negative offset is not disallowed by spec, but semantically should not be possible debug_assert!(self.offset[0] >= 0); debug_assert!(self.offset[1] >= 0); - self.offset[0] as u32 + self.extent[0] <= swapchain.dimensions()[0] - && self.offset[1] as u32 + self.extent[1] <= swapchain.dimensions()[1] - && self.layer < swapchain.layers() + self.offset[0] as u32 + self.extent[0] <= swapchain.image_extent()[0] + && self.offset[1] as u32 + self.extent[1] <= swapchain.image_extent()[1] + && self.layer < swapchain.image_array_layers() } } diff --git a/vulkano/src/swapchain/surface.rs b/vulkano/src/swapchain/surface.rs index e33b33f5..edd2fecb 100644 --- a/vulkano/src/swapchain/surface.rs +++ b/vulkano/src/swapchain/surface.rs @@ -8,21 +8,17 @@ // according to those terms. use crate::check_errors; -use crate::device::physical::PhysicalDevice; -use crate::device::physical::QueueFamily; -use crate::format::Format; use crate::image::ImageUsage; use crate::instance::Instance; -use crate::swapchain::capabilities::SupportedSurfaceTransforms; use crate::swapchain::display::DisplayMode; use crate::swapchain::display::DisplayPlane; -use crate::swapchain::Capabilities; use crate::swapchain::SurfaceSwapchainLock; use crate::Error; use crate::OomError; use crate::VulkanObject; use std::error; use std::fmt; +use std::hash::{Hash, Hasher}; use std::mem::MaybeUninit; use std::os::raw::c_ulong; use std::ptr; @@ -33,9 +29,10 @@ use std::sync::Arc; /// /// Creating a `Surface` is platform-specific. pub struct Surface { - window: W, + handle: ash::vk::SurfaceKHR, instance: Arc, - surface: ash::vk::SurfaceKHR, + api: SurfaceApi, + window: W, // If true, a swapchain has been associated to this surface, and that any new swapchain // creation should be forbidden. @@ -43,31 +40,37 @@ pub struct Surface { } impl Surface { - /// Creates a `Surface` given the raw handler. + /// Creates a `Surface` given the raw handle. /// - /// Be careful when using it + /// # Safety /// + /// - `handle` must be a valid Vulkan surface handle owned by `instance`. + /// - `handle` must have been created from `api`. + /// - The window object that `handle` was created from must outlive the created `Surface`. + /// The `win` parameter can be used to ensure this. pub unsafe fn from_raw_surface( instance: Arc, - surface: ash::vk::SurfaceKHR, + handle: ash::vk::SurfaceKHR, + api: SurfaceApi, win: W, ) -> Surface { Surface { - window: win, + handle, instance, - surface, + api, + window: win, + has_swapchain: AtomicBool::new(false), } } - /// Creates a `Surface` that covers a display mode. + /// Creates a `Surface` from a `DisplayPlane`. /// /// # Panic /// /// - Panics if `display_mode` and `plane` don't belong to the same physical device. /// - Panics if `plane` doesn't support the display of `display_mode`. - /// - pub fn from_display_mode( + pub fn from_display_plane( display_mode: &DisplayMode, plane: &DisplayPlane, ) -> Result>, SurfaceCreationError> { @@ -90,29 +93,29 @@ impl Surface { assert!(plane.supports(display_mode.display())); let instance = display_mode.display().physical_device().instance(); - let fns = instance.fns(); - let surface = unsafe { - let infos = ash::vk::DisplaySurfaceCreateInfoKHR { - flags: ash::vk::DisplaySurfaceCreateFlagsKHR::empty(), - display_mode: display_mode.internal_object(), - plane_index: plane.index(), - plane_stack_index: 0, // FIXME: plane.properties.currentStackIndex, - transform: ash::vk::SurfaceTransformFlagsKHR::IDENTITY, // TODO: let user choose - global_alpha: 0.0, // TODO: let user choose - alpha_mode: ash::vk::DisplayPlaneAlphaFlagsKHR::OPAQUE, // TODO: let user choose - image_extent: ash::vk::Extent2D { - // TODO: let user choose - width: display_mode.visible_region()[0], - height: display_mode.visible_region()[1], - }, - ..Default::default() - }; + let create_info = ash::vk::DisplaySurfaceCreateInfoKHR { + flags: ash::vk::DisplaySurfaceCreateFlagsKHR::empty(), + display_mode: display_mode.internal_object(), + plane_index: plane.index(), + plane_stack_index: 0, // FIXME: plane.properties.currentStackIndex, + transform: ash::vk::SurfaceTransformFlagsKHR::IDENTITY, // TODO: let user choose + global_alpha: 0.0, // TODO: let user choose + alpha_mode: ash::vk::DisplayPlaneAlphaFlagsKHR::OPAQUE, // TODO: let user choose + image_extent: ash::vk::Extent2D { + // TODO: let user choose + width: display_mode.visible_region()[0], + height: display_mode.visible_region()[1], + }, + ..Default::default() + }; + let handle = unsafe { + let fns = instance.fns(); let mut output = MaybeUninit::uninit(); check_errors(fns.khr_display.create_display_plane_surface_khr( instance.internal_object(), - &infos, + &create_info, ptr::null(), output.as_mut_ptr(), ))?; @@ -120,48 +123,45 @@ impl Surface { }; Ok(Arc::new(Surface { + handle, + instance: instance.clone(), + api: SurfaceApi::DisplayPlane, window: (), - instance: instance.clone(), - surface, + has_swapchain: AtomicBool::new(false), })) } - /// Creates a `Surface` from a Win32 window. - /// - /// The surface's min, max and current extent will always match the window's dimensions. + /// Creates a `Surface` from an Android window. /// /// # Safety /// - /// The caller must ensure that the `hinstance` and the `hwnd` are both correct and stay - /// alive for the entire lifetime of the surface. The `win` parameter can be used to ensure this. - - pub unsafe fn from_hwnd( + /// - `window` must be a valid handle. + /// - The object referred to by `window` must outlive the created `Surface`. + /// The `win` parameter can be used to ensure this. + pub unsafe fn from_android( instance: Arc, - hinstance: *const T, - hwnd: *const U, + window: *const T, win: W, ) -> Result>, SurfaceCreationError> { - let fns = instance.fns(); - - if !instance.enabled_extensions().khr_win32_surface { + if !instance.enabled_extensions().khr_android_surface { return Err(SurfaceCreationError::MissingExtension { - name: "VK_KHR_win32_surface", + name: "VK_KHR_android_surface", }); } - let surface = { - let infos = ash::vk::Win32SurfaceCreateInfoKHR { - flags: ash::vk::Win32SurfaceCreateFlagsKHR::empty(), - hinstance: hinstance as *mut _, - hwnd: hwnd as *mut _, - ..Default::default() - }; + let create_info = ash::vk::AndroidSurfaceCreateInfoKHR { + flags: ash::vk::AndroidSurfaceCreateFlagsKHR::empty(), + window: window as *mut _, + ..Default::default() + }; + let handle = { + let fns = instance.fns(); let mut output = MaybeUninit::uninit(); - check_errors(fns.khr_win32_surface.create_win32_surface_khr( + check_errors(fns.khr_android_surface.create_android_surface_khr( instance.internal_object(), - &infos, + &create_info, ptr::null(), output.as_mut_ptr(), ))?; @@ -169,47 +169,46 @@ impl Surface { }; Ok(Arc::new(Surface { + handle, + instance, + api: SurfaceApi::Android, window: win, - instance: instance.clone(), - surface, + has_swapchain: AtomicBool::new(false), })) } - /// Creates a `Surface` from an XCB window. - /// - /// The surface's min, max and current extent will always match the window's dimensions. + /// Creates a `Surface` from an iOS `UIView`. /// /// # Safety /// - /// The caller must ensure that the `connection` and the `window` are both correct and stay - /// alive for the entire lifetime of the surface. The `win` parameter can be used to ensure this. - pub unsafe fn from_xcb( + /// - `view` must be a valid handle. + /// - The object referred to by `view` must outlive the created `Surface`. + /// The `win` parameter can be used to ensure this. + /// - The `UIView` must be backed by a `CALayer` instance of type `CAMetalLayer`. + pub unsafe fn from_ios( instance: Arc, - connection: *const C, - window: u32, + view: *const T, win: W, ) -> Result>, SurfaceCreationError> { - let fns = instance.fns(); - - if !instance.enabled_extensions().khr_xcb_surface { + if !instance.enabled_extensions().mvk_ios_surface { return Err(SurfaceCreationError::MissingExtension { - name: "VK_KHR_xcb_surface", + name: "VK_MVK_ios_surface", }); } - let surface = { - let infos = ash::vk::XcbSurfaceCreateInfoKHR { - flags: ash::vk::XcbSurfaceCreateFlagsKHR::empty(), - connection: connection as *mut _, - window, - ..Default::default() - }; + let create_info = ash::vk::IOSSurfaceCreateInfoMVK { + flags: ash::vk::IOSSurfaceCreateFlagsMVK::empty(), + p_view: view as *const _, + ..Default::default() + }; + let handle = { + let fns = instance.fns(); let mut output = MaybeUninit::uninit(); - check_errors(fns.khr_xcb_surface.create_xcb_surface_khr( + check_errors(fns.mvk_ios_surface.create_ios_surface_mvk( instance.internal_object(), - &infos, + &create_info, ptr::null(), output.as_mut_ptr(), ))?; @@ -217,47 +216,46 @@ impl Surface { }; Ok(Arc::new(Surface { + handle, + instance, + api: SurfaceApi::Ios, window: win, - instance: instance.clone(), - surface, + has_swapchain: AtomicBool::new(false), })) } - /// Creates a `Surface` from an Xlib window. - /// - /// The surface's min, max and current extent will always match the window's dimensions. + /// Creates a `Surface` from a MacOS `NSView`. /// /// # Safety /// - /// The caller must ensure that the `display` and the `window` are both correct and stay - /// alive for the entire lifetime of the surface. The `win` parameter can be used to ensure this. - pub unsafe fn from_xlib( + /// - `view` must be a valid handle. + /// - The object referred to by `view` must outlive the created `Surface`. + /// The `win` parameter can be used to ensure this. + /// - The `NSView` must be backed by a `CALayer` instance of type `CAMetalLayer`. + pub unsafe fn from_mac_os( instance: Arc, - display: *const D, - window: c_ulong, + view: *const T, win: W, ) -> Result>, SurfaceCreationError> { - let fns = instance.fns(); - - if !instance.enabled_extensions().khr_xlib_surface { + if !instance.enabled_extensions().mvk_macos_surface { return Err(SurfaceCreationError::MissingExtension { - name: "VK_KHR_xlib_surface", + name: "VK_MVK_macos_surface", }); } - let surface = { - let infos = ash::vk::XlibSurfaceCreateInfoKHR { - flags: ash::vk::XlibSurfaceCreateFlagsKHR::empty(), - dpy: display as *mut _, - window, - ..Default::default() - }; + let create_info = ash::vk::MacOSSurfaceCreateInfoMVK { + flags: ash::vk::MacOSSurfaceCreateFlagsMVK::empty(), + p_view: view as *const _, + ..Default::default() + }; + let handle = { + let fns = instance.fns(); let mut output = MaybeUninit::uninit(); - check_errors(fns.khr_xlib_surface.create_xlib_surface_khr( + check_errors(fns.mvk_macos_surface.create_mac_os_surface_mvk( instance.internal_object(), - &infos, + &create_info, ptr::null(), output.as_mut_ptr(), ))?; @@ -265,9 +263,103 @@ impl Surface { }; Ok(Arc::new(Surface { + handle, + instance, + api: SurfaceApi::MacOs, window: win, - instance: instance.clone(), - surface, + + has_swapchain: AtomicBool::new(false), + })) + } + + /// Creates a `Surface` from a Metal `CAMetalLayer`. + /// + /// # Safety + /// + /// - `layer` must be a valid handle. + /// - The object referred to by `layer` must outlive the created `Surface`. + /// The `win` parameter can be used to ensure this. + pub unsafe fn from_metal( + instance: Arc, + layer: *const T, + win: W, + ) -> Result>, SurfaceCreationError> { + if !instance.enabled_extensions().ext_metal_surface { + return Err(SurfaceCreationError::MissingExtension { + name: "VK_EXT_metal_surface", + }); + } + + let create_info = ash::vk::MetalSurfaceCreateInfoEXT { + flags: ash::vk::MetalSurfaceCreateFlagsEXT::empty(), + p_layer: layer as *const _, + ..Default::default() + }; + + let handle = { + let fns = instance.fns(); + let mut output = MaybeUninit::uninit(); + check_errors(fns.ext_metal_surface.create_metal_surface_ext( + instance.internal_object(), + &create_info, + ptr::null(), + output.as_mut_ptr(), + ))?; + output.assume_init() + }; + + Ok(Arc::new(Surface { + handle, + instance, + api: SurfaceApi::Metal, + window: win, + + has_swapchain: AtomicBool::new(false), + })) + } + + /// Creates a `Surface` from a `code:nn::code:vi::code:Layer`. + /// + /// # Safety + /// + /// - `window` must be a valid handle. + /// - The object referred to by `window` must outlive the created `Surface`. + /// The `win` parameter can be used to ensure this. + pub unsafe fn from_vi( + instance: Arc, + window: *const T, + win: W, + ) -> Result>, SurfaceCreationError> { + if !instance.enabled_extensions().nn_vi_surface { + return Err(SurfaceCreationError::MissingExtension { + name: "VK_NN_vi_surface", + }); + } + + let create_info = ash::vk::ViSurfaceCreateInfoNN { + flags: ash::vk::ViSurfaceCreateFlagsNN::empty(), + window: window as *mut _, + ..Default::default() + }; + + let handle = { + let fns = instance.fns(); + let mut output = MaybeUninit::uninit(); + check_errors(fns.nn_vi_surface.create_vi_surface_nn( + instance.internal_object(), + &create_info, + ptr::null(), + output.as_mut_ptr(), + ))?; + output.assume_init() + }; + + Ok(Arc::new(Surface { + handle, + instance, + api: SurfaceApi::Vi, + window: win, + has_swapchain: AtomicBool::new(false), })) } @@ -278,34 +370,34 @@ impl Surface { /// /// # Safety /// - /// The caller must ensure that the `display` and the `surface` are both correct and stay - /// alive for the entire lifetime of the surface. The `win` parameter can be used to ensure this. + /// - `display` and `surface` must be valid handles. + /// - The objects referred to by `display` and `surface` must outlive the created `Surface`. + /// The `win` parameter can be used to ensure this. pub unsafe fn from_wayland( instance: Arc, display: *const D, surface: *const S, win: W, ) -> Result>, SurfaceCreationError> { - let fns = instance.fns(); - if !instance.enabled_extensions().khr_wayland_surface { return Err(SurfaceCreationError::MissingExtension { name: "VK_KHR_wayland_surface", }); } - let surface = { - let infos = ash::vk::WaylandSurfaceCreateInfoKHR { - flags: ash::vk::WaylandSurfaceCreateFlagsKHR::empty(), - display: display as *mut _, - surface: surface as *mut _, - ..Default::default() - }; + let create_info = ash::vk::WaylandSurfaceCreateInfoKHR { + flags: ash::vk::WaylandSurfaceCreateFlagsKHR::empty(), + display: display as *mut _, + surface: surface as *mut _, + ..Default::default() + }; + let handle = { + let fns = instance.fns(); let mut output = MaybeUninit::uninit(); check_errors(fns.khr_wayland_surface.create_wayland_surface_khr( instance.internal_object(), - &infos, + &create_info, ptr::null(), output.as_mut_ptr(), ))?; @@ -313,43 +405,49 @@ impl Surface { }; Ok(Arc::new(Surface { + handle, + instance, + api: SurfaceApi::Wayland, window: win, - instance: instance.clone(), - surface, + has_swapchain: AtomicBool::new(false), })) } - /// Creates a `Surface` from an Android window. + /// Creates a `Surface` from a Win32 window. + /// + /// The surface's min, max and current extent will always match the window's dimensions. /// /// # Safety /// - /// The caller must ensure that the `window` is correct and stays alive for the entire - /// lifetime of the surface. The `win` parameter can be used to ensure this. - pub unsafe fn from_anativewindow( + /// - `hinstance` and `hwnd` must be valid handles. + /// - The objects referred to by `hwnd` and `hinstance` must outlive the created `Surface`. + /// The `win` parameter can be used to ensure this. + pub unsafe fn from_win32( instance: Arc, - window: *const T, + hinstance: *const T, + hwnd: *const U, win: W, ) -> Result>, SurfaceCreationError> { - let fns = instance.fns(); - - if !instance.enabled_extensions().khr_android_surface { + if !instance.enabled_extensions().khr_win32_surface { return Err(SurfaceCreationError::MissingExtension { - name: "VK_KHR_android_surface", + name: "VK_KHR_win32_surface", }); } - let surface = { - let infos = ash::vk::AndroidSurfaceCreateInfoKHR { - flags: ash::vk::AndroidSurfaceCreateFlagsKHR::empty(), - window: window as *mut _, - ..Default::default() - }; + let create_info = ash::vk::Win32SurfaceCreateInfoKHR { + flags: ash::vk::Win32SurfaceCreateFlagsKHR::empty(), + hinstance: hinstance as *mut _, + hwnd: hwnd as *mut _, + ..Default::default() + }; + let handle = { + let fns = instance.fns(); let mut output = MaybeUninit::uninit(); - check_errors(fns.khr_android_surface.create_android_surface_khr( + check_errors(fns.khr_win32_surface.create_win32_surface_khr( instance.internal_object(), - &infos, + &create_info, ptr::null(), output.as_mut_ptr(), ))?; @@ -357,44 +455,49 @@ impl Surface { }; Ok(Arc::new(Surface { + handle, + instance, + api: SurfaceApi::Win32, window: win, - instance: instance.clone(), - surface, + has_swapchain: AtomicBool::new(false), })) } - /// Creates a `Surface` from an iOS `UIView`. + /// Creates a `Surface` from an XCB window. + /// + /// The surface's min, max and current extent will always match the window's dimensions. /// /// # Safety /// - /// - The caller must ensure that the `view` is correct and stays alive for the entire - /// lifetime of the surface. The win parameter can be used to ensure this. - /// - The `UIView` must be backed by a `CALayer` instance of type `CAMetalLayer`. - pub unsafe fn from_ios_moltenvk( + /// - `connection` and `window` must be valid handles. + /// - The objects referred to by `connection` and `window` must outlive the created `Surface`. + /// The `win` parameter can be used to ensure this. + pub unsafe fn from_xcb( instance: Arc, - view: *const T, + connection: *const C, + window: u32, win: W, ) -> Result>, SurfaceCreationError> { - let fns = instance.fns(); - - if !instance.enabled_extensions().mvk_ios_surface { + if !instance.enabled_extensions().khr_xcb_surface { return Err(SurfaceCreationError::MissingExtension { - name: "VK_MVK_ios_surface", + name: "VK_KHR_xcb_surface", }); } - let surface = { - let infos = ash::vk::IOSSurfaceCreateInfoMVK { - flags: ash::vk::IOSSurfaceCreateFlagsMVK::empty(), - p_view: view as *const _, - ..Default::default() - }; + let create_info = ash::vk::XcbSurfaceCreateInfoKHR { + flags: ash::vk::XcbSurfaceCreateFlagsKHR::empty(), + connection: connection as *mut _, + window, + ..Default::default() + }; + let handle = { + let fns = instance.fns(); let mut output = MaybeUninit::uninit(); - check_errors(fns.mvk_ios_surface.create_ios_surface_mvk( + check_errors(fns.khr_xcb_surface.create_xcb_surface_khr( instance.internal_object(), - &infos, + &create_info, ptr::null(), output.as_mut_ptr(), ))?; @@ -402,44 +505,49 @@ impl Surface { }; Ok(Arc::new(Surface { + handle, + instance, + api: SurfaceApi::Xcb, window: win, - instance: instance.clone(), - surface, + has_swapchain: AtomicBool::new(false), })) } - /// Creates a `Surface` from a MacOS `NSView`. + /// Creates a `Surface` from an Xlib window. + /// + /// The surface's min, max and current extent will always match the window's dimensions. /// /// # Safety /// - /// - The caller must ensure that the `view` is correct and stays alive for the entire - /// lifetime of the surface. The `win` parameter can be used to ensure this. - /// - The `NSView` must be backed by a `CALayer` instance of type `CAMetalLayer`. - pub unsafe fn from_macos_moltenvk( + /// - `display` and `window` must be valid handles. + /// - The objects referred to by `display` and `window` must outlive the created `Surface`. + /// The `win` parameter can be used to ensure this. + pub unsafe fn from_xlib( instance: Arc, - view: *const T, + display: *const D, + window: c_ulong, win: W, ) -> Result>, SurfaceCreationError> { - let fns = instance.fns(); - - if !instance.enabled_extensions().mvk_macos_surface { + if !instance.enabled_extensions().khr_xlib_surface { return Err(SurfaceCreationError::MissingExtension { - name: "VK_MVK_macos_surface", + name: "VK_KHR_xlib_surface", }); } - let surface = { - let infos = ash::vk::MacOSSurfaceCreateInfoMVK { - flags: ash::vk::MacOSSurfaceCreateFlagsMVK::empty(), - p_view: view as *const _, - ..Default::default() - }; + let create_info = ash::vk::XlibSurfaceCreateInfoKHR { + flags: ash::vk::XlibSurfaceCreateFlagsKHR::empty(), + dpy: display as *mut _, + window, + ..Default::default() + }; + let handle = { + let fns = instance.fns(); let mut output = MaybeUninit::uninit(); - check_errors(fns.mvk_macos_surface.create_mac_os_surface_mvk( + check_errors(fns.khr_xlib_surface.create_xlib_surface_khr( instance.internal_object(), - &infos, + &create_info, ptr::null(), output.as_mut_ptr(), ))?; @@ -447,277 +555,31 @@ impl Surface { }; Ok(Arc::new(Surface { + handle, + instance, + api: SurfaceApi::Xlib, window: win, - instance: instance.clone(), - surface, + has_swapchain: AtomicBool::new(false), })) } - /// Creates a `Surface` from a `CAMetalLayer`. - /// - /// # Safety - /// - /// - The caller must ensure that the `layer` is correct and stays alive for the entire - /// lifetime of the surface. The `win` parameter can be used to ensure this. - pub unsafe fn from_metal_layer( - instance: Arc, - layer: *const T, - win: W, - ) -> Result>, SurfaceCreationError> { - let fns = instance.fns(); - - if !instance.enabled_extensions().ext_metal_surface { - return Err(SurfaceCreationError::MissingExtension { - name: "VK_EXT_metal_surface", - }); - } - - let surface = { - let infos = ash::vk::MetalSurfaceCreateInfoEXT { - flags: ash::vk::MetalSurfaceCreateFlagsEXT::empty(), - p_layer: layer as *const _, - ..Default::default() - }; - - let mut output = MaybeUninit::uninit(); - check_errors(fns.ext_metal_surface.create_metal_surface_ext( - instance.internal_object(), - &infos, - ptr::null(), - output.as_mut_ptr(), - ))?; - output.assume_init() - }; - - Ok(Arc::new(Surface { - window: win, - instance: instance.clone(), - surface, - has_swapchain: AtomicBool::new(false), - })) - } - - /// Creates a `Surface` from a `code:nn::code:vi::code:Layer`. - /// - /// # Safety - /// - /// The caller must ensure that the `window` is correct and stays alive for the entire - /// lifetime of the surface. The `win` parameter can be used to ensure this. - pub unsafe fn from_vi_surface( - instance: Arc, - window: *const T, - win: W, - ) -> Result>, SurfaceCreationError> { - let fns = instance.fns(); - - if !instance.enabled_extensions().nn_vi_surface { - return Err(SurfaceCreationError::MissingExtension { - name: "VK_NN_vi_surface", - }); - } - - let surface = { - let infos = ash::vk::ViSurfaceCreateInfoNN { - flags: ash::vk::ViSurfaceCreateFlagsNN::empty(), - window: window as *mut _, - ..Default::default() - }; - - let mut output = MaybeUninit::uninit(); - check_errors(fns.nn_vi_surface.create_vi_surface_nn( - instance.internal_object(), - &infos, - ptr::null(), - output.as_mut_ptr(), - ))?; - output.assume_init() - }; - - Ok(Arc::new(Surface { - window: win, - instance: instance.clone(), - surface, - has_swapchain: AtomicBool::new(false), - })) - } - - /// Returns true if the given queue family can draw on this surface. - // FIXME: vulkano doesn't check this for the moment! - pub fn is_supported(&self, queue: QueueFamily) -> Result { - unsafe { - let fns = self.instance.fns(); - - let mut output = MaybeUninit::uninit(); - check_errors(fns.khr_surface.get_physical_device_surface_support_khr( - queue.physical_device().internal_object(), - queue.id(), - self.surface, - output.as_mut_ptr(), - ))?; - Ok(output.assume_init() != 0) - } - } - - /// Retrieves the capabilities of a surface when used by a certain device. - /// - /// # Notes - /// - /// - Capabilities that are not supported in `vk-sys` are silently dropped - /// - /// # Panic - /// - /// - Panics if the device and the surface don't belong to the same instance. - /// - pub fn capabilities(&self, device: PhysicalDevice) -> Result { - unsafe { - assert_eq!( - &*self.instance as *const _, - &**device.instance() as *const _, - "Instance mismatch in Surface::capabilities" - ); - - let fns = self.instance.fns(); - - let caps = { - let mut out: MaybeUninit = MaybeUninit::uninit(); - check_errors( - fns.khr_surface - .get_physical_device_surface_capabilities_khr( - device.internal_object(), - self.surface, - out.as_mut_ptr(), - ), - )?; - out.assume_init() - }; - - let formats = { - let mut num = 0; - check_errors(fns.khr_surface.get_physical_device_surface_formats_khr( - device.internal_object(), - self.surface, - &mut num, - ptr::null_mut(), - ))?; - - let mut formats = Vec::with_capacity(num as usize); - check_errors(fns.khr_surface.get_physical_device_surface_formats_khr( - device.internal_object(), - self.surface, - &mut num, - formats.as_mut_ptr(), - ))?; - formats.set_len(num as usize); - formats - }; - - let modes = { - let mut num = 0; - check_errors( - fns.khr_surface - .get_physical_device_surface_present_modes_khr( - device.internal_object(), - self.surface, - &mut num, - ptr::null_mut(), - ), - )?; - - let mut modes = Vec::with_capacity(num as usize); - check_errors( - fns.khr_surface - .get_physical_device_surface_present_modes_khr( - device.internal_object(), - self.surface, - &mut num, - modes.as_mut_ptr(), - ), - )?; - modes.set_len(num as usize); - debug_assert!(modes - .iter() - .find(|&&m| m == ash::vk::PresentModeKHR::FIFO) - .is_some()); - debug_assert!(modes.iter().count() > 0); - modes.into_iter().collect() - }; - - Ok(Capabilities { - min_image_count: caps.min_image_count, - max_image_count: if caps.max_image_count == 0 { - None - } else { - Some(caps.max_image_count) - }, - current_extent: if caps.current_extent.width == 0xffffffff - && caps.current_extent.height == 0xffffffff - { - None - } else { - Some([caps.current_extent.width, caps.current_extent.height]) - }, - min_image_extent: [caps.min_image_extent.width, caps.min_image_extent.height], - max_image_extent: [caps.max_image_extent.width, caps.max_image_extent.height], - max_image_array_layers: caps.max_image_array_layers, - supported_transforms: caps.supported_transforms.into(), - - current_transform: SupportedSurfaceTransforms::from(caps.current_transform) - .iter() - .next() - .unwrap(), // TODO: - supported_composite_alpha: caps.supported_composite_alpha.into(), - supported_usage_flags: { - let usage = ImageUsage::from(caps.supported_usage_flags); - debug_assert!(usage.color_attachment); // specs say that this must be true - usage - }, - supported_formats: formats - .into_iter() - .filter_map(|f| { - // TODO: Change the way capabilities not supported in vk-sys are handled - Format::try_from(f.format) - .ok() - .map(|format| (format, f.color_space.into())) - }) - .collect(), - present_modes: modes, - }) - } - } - - #[inline] - pub fn window(&self) -> &W { - &self.window - } - /// Returns the instance this surface was created with. #[inline] pub fn instance(&self) -> &Arc { &self.instance } -} -unsafe impl SurfaceSwapchainLock for Surface { + /// Returns the windowing API that was used to construct the surface. #[inline] - fn flag(&self) -> &AtomicBool { - &self.has_swapchain + pub fn api(&self) -> SurfaceApi { + self.api } -} - -unsafe impl VulkanObject for Surface { - type Object = ash::vk::SurfaceKHR; + /// Returns a reference to the `W` type parameter that was passed when creating the surface. #[inline] - fn internal_object(&self) -> ash::vk::SurfaceKHR { - self.surface - } -} - -impl fmt::Debug for Surface { - #[inline] - fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!(fmt, "", self.surface) + pub fn window(&self) -> &W { + &self.window } } @@ -728,13 +590,68 @@ impl Drop for Surface { let fns = self.instance.fns(); fns.khr_surface.destroy_surface_khr( self.instance.internal_object(), - self.surface, + self.handle, ptr::null(), ); } } } +unsafe impl VulkanObject for Surface { + type Object = ash::vk::SurfaceKHR; + + #[inline] + fn internal_object(&self) -> ash::vk::SurfaceKHR { + self.handle + } +} + +impl fmt::Debug for Surface { + #[inline] + fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { + let Self { + handle, + instance, + api, + window: _, + + has_swapchain, + } = self; + + fmt.debug_struct("Surface") + .field("handle", handle) + .field("instance", instance) + .field("api", api) + .field("window", &()) + .field("has_swapchain", &has_swapchain) + .finish() + } +} + +impl PartialEq for Surface { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.handle == other.handle && self.instance() == other.instance() + } +} + +impl Eq for Surface {} + +impl Hash for Surface { + #[inline] + fn hash(&self, state: &mut H) { + self.handle.hash(state); + self.instance().hash(state); + } +} + +unsafe impl SurfaceSwapchainLock for Surface { + #[inline] + fn flag(&self) -> &AtomicBool { + &self.has_swapchain + } +} + /// Error that can happen when creating a debug callback. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum SurfaceCreationError { @@ -792,60 +709,543 @@ impl From for SurfaceCreationError { } } -/// Error that can happen when retrieving a surface's capabilities. +/// The windowing API that was used to construct a surface. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[non_exhaustive] +pub enum SurfaceApi { + DisplayPlane, + + // Alphabetical order + Android, + Ios, + MacOs, + Metal, + Vi, + Wayland, + Win32, + Xcb, + Xlib, +} + +/// The way presenting a swapchain is accomplished. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[repr(i32)] +#[non_exhaustive] +pub enum PresentMode { + /// Immediately shows the image to the user. May result in visible tearing. + Immediate = ash::vk::PresentModeKHR::IMMEDIATE.as_raw(), + + /// The action of presenting an image puts it in wait. When the next vertical blanking period + /// happens, the waiting image is effectively shown to the user. If an image is presented while + /// another one is waiting, it is replaced. + Mailbox = ash::vk::PresentModeKHR::MAILBOX.as_raw(), + + /// The action of presenting an image adds it to a queue of images. At each vertical blanking + /// period, the queue is popped and an image is presented. + /// + /// Guaranteed to be always supported. + /// + /// This is the equivalent of OpenGL's `SwapInterval` with a value of 1. + Fifo = ash::vk::PresentModeKHR::FIFO.as_raw(), + + /// Same as `Fifo`, except that if the queue was empty during the previous vertical blanking + /// period then it is equivalent to `Immediate`. + /// + /// This is the equivalent of OpenGL's `SwapInterval` with a value of -1. + FifoRelaxed = ash::vk::PresentModeKHR::FIFO_RELAXED.as_raw(), + // TODO: These can't be enabled yet because they have to be used with shared present surfaces + // which vulkano doesnt support yet. + //SharedDemand = ash::vk::PresentModeKHR::SHARED_DEMAND_REFRESH, + //SharedContinuous = ash::vk::PresentModeKHR::SHARED_CONTINUOUS_REFRESH, +} + +impl From for ash::vk::PresentModeKHR { + #[inline] + fn from(val: PresentMode) -> Self { + Self::from_raw(val as i32) + } +} + +impl TryFrom for PresentMode { + type Error = (); + + fn try_from(value: ash::vk::PresentModeKHR) -> Result { + Ok(match value { + ash::vk::PresentModeKHR::IMMEDIATE => Self::Immediate, + ash::vk::PresentModeKHR::MAILBOX => Self::Mailbox, + ash::vk::PresentModeKHR::FIFO => Self::Fifo, + ash::vk::PresentModeKHR::FIFO_RELAXED => Self::FifoRelaxed, + //ash::vk::PresentModeKHR::SHARED_DEMAND_REFRESH => Self::SharedDemandRefresh, + //ash::vk::PresentModeKHR::SHARED_CONTINUOUS_REFRESH => Self::SharedContinuousRefresh, + _ => return Err(()), + }) + } +} + +/// A transformation to apply to the image before showing it on the screen. #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[repr(u32)] -pub enum CapabilitiesError { - /// Not enough memory. - OomError(OomError), - - /// The surface is no longer accessible and must be recreated. - SurfaceLost, +#[non_exhaustive] +pub enum SurfaceTransform { + /// Don't transform the image. + Identity = ash::vk::SurfaceTransformFlagsKHR::IDENTITY.as_raw(), + /// Rotate 90 degrees. + Rotate90 = ash::vk::SurfaceTransformFlagsKHR::ROTATE_90.as_raw(), + /// Rotate 180 degrees. + Rotate180 = ash::vk::SurfaceTransformFlagsKHR::ROTATE_180.as_raw(), + /// Rotate 270 degrees. + Rotate270 = ash::vk::SurfaceTransformFlagsKHR::ROTATE_270.as_raw(), + /// Mirror the image horizontally. + HorizontalMirror = ash::vk::SurfaceTransformFlagsKHR::HORIZONTAL_MIRROR.as_raw(), + /// Mirror the image horizontally and rotate 90 degrees. + HorizontalMirrorRotate90 = + ash::vk::SurfaceTransformFlagsKHR::HORIZONTAL_MIRROR_ROTATE_90.as_raw(), + /// Mirror the image horizontally and rotate 180 degrees. + HorizontalMirrorRotate180 = + ash::vk::SurfaceTransformFlagsKHR::HORIZONTAL_MIRROR_ROTATE_180.as_raw(), + /// Mirror the image horizontally and rotate 270 degrees. + HorizontalMirrorRotate270 = + ash::vk::SurfaceTransformFlagsKHR::HORIZONTAL_MIRROR_ROTATE_270.as_raw(), + /// Let the operating system or driver implementation choose. + Inherit = ash::vk::SurfaceTransformFlagsKHR::INHERIT.as_raw(), } -impl error::Error for CapabilitiesError { +impl From for ash::vk::SurfaceTransformFlagsKHR { #[inline] - fn source(&self) -> Option<&(dyn error::Error + 'static)> { - match *self { - CapabilitiesError::OomError(ref err) => Some(err), - _ => None, - } + fn from(val: SurfaceTransform) -> Self { + Self::from_raw(val as u32) } } -impl fmt::Display for CapabilitiesError { +/// List of supported composite alpha modes. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[non_exhaustive] +pub struct SupportedSurfaceTransforms { + pub identity: bool, + pub rotate90: bool, + pub rotate180: bool, + pub rotate270: bool, + pub horizontal_mirror: bool, + pub horizontal_mirror_rotate90: bool, + pub horizontal_mirror_rotate180: bool, + pub horizontal_mirror_rotate270: bool, + pub inherit: bool, +} + +impl From for SupportedSurfaceTransforms { + fn from(val: ash::vk::SurfaceTransformFlagsKHR) -> Self { + macro_rules! v { + ($val:expr, $out:ident, $e:expr, $f:ident) => { + if !($val & $e).is_empty() { + $out.$f = true; + } + }; + } + + let mut result = SupportedSurfaceTransforms::none(); + v!( + val, + result, + ash::vk::SurfaceTransformFlagsKHR::IDENTITY, + identity + ); + v!( + val, + result, + ash::vk::SurfaceTransformFlagsKHR::ROTATE_90, + rotate90 + ); + v!( + val, + result, + ash::vk::SurfaceTransformFlagsKHR::ROTATE_180, + rotate180 + ); + v!( + val, + result, + ash::vk::SurfaceTransformFlagsKHR::ROTATE_270, + rotate270 + ); + v!( + val, + result, + ash::vk::SurfaceTransformFlagsKHR::HORIZONTAL_MIRROR, + horizontal_mirror + ); + v!( + val, + result, + ash::vk::SurfaceTransformFlagsKHR::HORIZONTAL_MIRROR_ROTATE_90, + horizontal_mirror_rotate90 + ); + v!( + val, + result, + ash::vk::SurfaceTransformFlagsKHR::HORIZONTAL_MIRROR_ROTATE_180, + horizontal_mirror_rotate180 + ); + v!( + val, + result, + ash::vk::SurfaceTransformFlagsKHR::HORIZONTAL_MIRROR_ROTATE_270, + horizontal_mirror_rotate270 + ); + v!( + val, + result, + ash::vk::SurfaceTransformFlagsKHR::INHERIT, + inherit + ); + result + } +} + +impl SupportedSurfaceTransforms { + /// Builds a `SupportedSurfaceTransforms` with all fields set to false. #[inline] - fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!( - fmt, - "{}", - match *self { - CapabilitiesError::OomError(_) => "not enough memory", - CapabilitiesError::SurfaceLost => "the surface is no longer valid", + pub fn none() -> SupportedSurfaceTransforms { + SupportedSurfaceTransforms { + identity: false, + rotate90: false, + rotate180: false, + rotate270: false, + horizontal_mirror: false, + horizontal_mirror_rotate90: false, + horizontal_mirror_rotate180: false, + horizontal_mirror_rotate270: false, + inherit: false, + } + } + + /// Returns true if the given `SurfaceTransform` is in this list. + #[inline] + pub fn supports(&self, value: SurfaceTransform) -> bool { + match value { + SurfaceTransform::Identity => self.identity, + SurfaceTransform::Rotate90 => self.rotate90, + SurfaceTransform::Rotate180 => self.rotate180, + SurfaceTransform::Rotate270 => self.rotate270, + SurfaceTransform::HorizontalMirror => self.horizontal_mirror, + SurfaceTransform::HorizontalMirrorRotate90 => self.horizontal_mirror_rotate90, + SurfaceTransform::HorizontalMirrorRotate180 => self.horizontal_mirror_rotate180, + SurfaceTransform::HorizontalMirrorRotate270 => self.horizontal_mirror_rotate270, + SurfaceTransform::Inherit => self.inherit, + } + } + + /// Returns an iterator to the list of supported composite alpha. + #[inline] + pub fn iter(&self) -> impl Iterator { + let moved = *self; + [ + SurfaceTransform::Identity, + SurfaceTransform::Rotate90, + SurfaceTransform::Rotate180, + SurfaceTransform::Rotate270, + SurfaceTransform::HorizontalMirror, + SurfaceTransform::HorizontalMirrorRotate90, + SurfaceTransform::HorizontalMirrorRotate180, + SurfaceTransform::HorizontalMirrorRotate270, + SurfaceTransform::Inherit, + ] + .into_iter() + .filter(move |&mode| moved.supports(mode)) + } +} + +impl Default for SurfaceTransform { + #[inline] + fn default() -> SurfaceTransform { + SurfaceTransform::Identity + } +} + +/// How the alpha values of the pixels of the window are treated. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[repr(u32)] +#[non_exhaustive] +pub enum CompositeAlpha { + /// The alpha channel of the image is ignored. All the pixels are considered as if they have a + /// value of 1.0. + Opaque = ash::vk::CompositeAlphaFlagsKHR::OPAQUE.as_raw(), + + /// The alpha channel of the image is respected. The color channels are expected to have + /// already been multiplied by the alpha value. + PreMultiplied = ash::vk::CompositeAlphaFlagsKHR::PRE_MULTIPLIED.as_raw(), + + /// The alpha channel of the image is respected. The color channels will be multiplied by the + /// alpha value by the compositor before being added to what is behind. + PostMultiplied = ash::vk::CompositeAlphaFlagsKHR::POST_MULTIPLIED.as_raw(), + + /// Let the operating system or driver implementation choose. + Inherit = ash::vk::CompositeAlphaFlagsKHR::INHERIT.as_raw(), +} + +impl From for ash::vk::CompositeAlphaFlagsKHR { + #[inline] + fn from(val: CompositeAlpha) -> Self { + Self::from_raw(val as u32) + } +} + +/// List of supported composite alpha modes. +/// +/// See the docs of `CompositeAlpha`. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[non_exhaustive] +pub struct SupportedCompositeAlpha { + pub opaque: bool, + pub pre_multiplied: bool, + pub post_multiplied: bool, + pub inherit: bool, +} + +impl From for SupportedCompositeAlpha { + #[inline] + fn from(val: ash::vk::CompositeAlphaFlagsKHR) -> SupportedCompositeAlpha { + let mut result = SupportedCompositeAlpha::none(); + if !(val & ash::vk::CompositeAlphaFlagsKHR::OPAQUE).is_empty() { + result.opaque = true; + } + if !(val & ash::vk::CompositeAlphaFlagsKHR::PRE_MULTIPLIED).is_empty() { + result.pre_multiplied = true; + } + if !(val & ash::vk::CompositeAlphaFlagsKHR::POST_MULTIPLIED).is_empty() { + result.post_multiplied = true; + } + if !(val & ash::vk::CompositeAlphaFlagsKHR::INHERIT).is_empty() { + result.inherit = true; + } + result + } +} + +impl SupportedCompositeAlpha { + /// Builds a `SupportedCompositeAlpha` with all fields set to false. + #[inline] + pub fn none() -> SupportedCompositeAlpha { + SupportedCompositeAlpha { + opaque: false, + pre_multiplied: false, + post_multiplied: false, + inherit: false, + } + } + + /// Returns true if the given `CompositeAlpha` is in this list. + #[inline] + pub fn supports(&self, value: CompositeAlpha) -> bool { + match value { + CompositeAlpha::Opaque => self.opaque, + CompositeAlpha::PreMultiplied => self.pre_multiplied, + CompositeAlpha::PostMultiplied => self.post_multiplied, + CompositeAlpha::Inherit => self.inherit, + } + } + + /// Returns an iterator to the list of supported composite alpha. + #[inline] + pub fn iter(&self) -> impl Iterator { + let moved = *self; + [ + CompositeAlpha::Opaque, + CompositeAlpha::PreMultiplied, + CompositeAlpha::PostMultiplied, + CompositeAlpha::Inherit, + ] + .into_iter() + .filter(move |&mode| moved.supports(mode)) + } +} + +/// How the presentation engine should interpret the data. +/// +/// # A quick lesson about color spaces +/// +/// ## What is a color space? +/// +/// Each pixel of a monitor is made of three components: one red, one green, and one blue. In the +/// past, computers would simply send to the monitor the intensity of each of the three components. +/// +/// This proved to be problematic, because depending on the brand of the monitor the colors would +/// not exactly be the same. For example on some monitors, a value of `[1.0, 0.0, 0.0]` would be a +/// bit more orange than on others. +/// +/// In order to standardize this, there exist what are called *color spaces*: sRGB, AdobeRGB, +/// DCI-P3, scRGB, etc. When you manipulate RGB values in a specific color space, these values have +/// a precise absolute meaning in terms of color, that is the same across all systems and monitors. +/// +/// > **Note**: Color spaces are orthogonal to concept of RGB. *RGB* only indicates what is the +/// > representation of the data, but not how it is interpreted. You can think of this a bit like +/// > text encoding. An *RGB* value is a like a byte, in other words it is the medium by which +/// > values are communicated, and a *color space* is like a text encoding (eg. UTF-8), in other +/// > words it is the way the value should be interpreted. +/// +/// The most commonly used color space today is sRGB. Most monitors today use this color space, +/// and most images files are encoded in this color space. +/// +/// ## Pixel formats and linear vs non-linear +/// +/// In Vulkan all images have a specific format in which the data is stored. The data of an image +/// consists of pixels in RGB but contains no information about the color space (or lack thereof) +/// of these pixels. You are free to store them in whatever color space you want. +/// +/// But one big practical problem with color spaces is that they are sometimes not linear, and in +/// particular the popular sRGB color space is not linear. In a non-linear color space, a value of +/// `[0.6, 0.6, 0.6]` for example is **not** twice as bright as a value of `[0.3, 0.3, 0.3]`. This +/// is problematic, because operations such as taking the average of two colors or calculating the +/// lighting of a texture with a dot product are mathematically incorrect and will produce +/// incorrect colors. +/// +/// > **Note**: If the texture format has an alpha component, it is not affected by the color space +/// > and always behaves linearly. +/// +/// In order to solve this Vulkan also provides image formats with the `Srgb` suffix, which are +/// expected to contain RGB data in the sRGB color space. When you sample an image with such a +/// format from a shader, the implementation will automatically turn the pixel values into a linear +/// color space that is suitable for linear operations (such as additions or multiplications). +/// When you write to a framebuffer attachment with such a format, the implementation will +/// automatically perform the opposite conversion. These conversions are most of the time performed +/// by the hardware and incur no additional cost. +/// +/// ## Color space of the swapchain +/// +/// The color space that you specify when you create a swapchain is how the implementation will +/// interpret the raw data inside of the image. +/// +/// > **Note**: The implementation can choose to send the data in the swapchain image directly to +/// > the monitor, but it can also choose to write it in an intermediary buffer that is then read +/// > by the operating system or windowing system. Therefore the color space that the +/// > implementation supports is not necessarily the same as the one supported by the monitor. +/// +/// It is *your* job to ensure that the data in the swapchain image is in the color space +/// that is specified here, otherwise colors will be incorrect. +/// The implementation will never perform any additional automatic conversion after the colors have +/// been written to the swapchain image. +/// +/// # How do I handle this correctly? +/// +/// The easiest way to handle color spaces in a cross-platform program is: +/// +/// - Always request the `SrgbNonLinear` color space when creating the swapchain. +/// - Make sure that all your image files use the sRGB color space, and load them in images whose +/// format has the `Srgb` suffix. Only use non-sRGB image formats for intermediary computations +/// or to store non-color data. +/// - Swapchain images should have a format with the `Srgb` suffix. +/// +/// > **Note**: It is unclear whether the `SrgbNonLinear` color space is always supported by the +/// > the implementation or not. See . +/// +/// > **Note**: Lots of developers are confused by color spaces. You can sometimes find articles +/// > talking about gamma correction and suggestion to put your colors to the power 2.2 for +/// > example. These are all hacks and you should use the sRGB pixel formats instead. +/// +/// If you follow these three rules, then everything should render the same way on all platforms. +/// +/// Additionally you can try detect whether the implementation supports any additional color space +/// and perform a manual conversion to that color space from inside your shader. +/// +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[repr(i32)] +#[non_exhaustive] +pub enum ColorSpace { + SrgbNonLinear = ash::vk::ColorSpaceKHR::SRGB_NONLINEAR.as_raw(), + DisplayP3NonLinear = ash::vk::ColorSpaceKHR::DISPLAY_P3_NONLINEAR_EXT.as_raw(), + ExtendedSrgbLinear = ash::vk::ColorSpaceKHR::EXTENDED_SRGB_LINEAR_EXT.as_raw(), + ExtendedSrgbNonLinear = ash::vk::ColorSpaceKHR::EXTENDED_SRGB_NONLINEAR_EXT.as_raw(), + DisplayP3Linear = ash::vk::ColorSpaceKHR::DISPLAY_P3_LINEAR_EXT.as_raw(), + DciP3NonLinear = ash::vk::ColorSpaceKHR::DCI_P3_NONLINEAR_EXT.as_raw(), + Bt709Linear = ash::vk::ColorSpaceKHR::BT709_LINEAR_EXT.as_raw(), + Bt709NonLinear = ash::vk::ColorSpaceKHR::BT709_NONLINEAR_EXT.as_raw(), + Bt2020Linear = ash::vk::ColorSpaceKHR::BT2020_LINEAR_EXT.as_raw(), + Hdr10St2084 = ash::vk::ColorSpaceKHR::HDR10_ST2084_EXT.as_raw(), + DolbyVision = ash::vk::ColorSpaceKHR::DOLBYVISION_EXT.as_raw(), + Hdr10Hlg = ash::vk::ColorSpaceKHR::HDR10_HLG_EXT.as_raw(), + AdobeRgbLinear = ash::vk::ColorSpaceKHR::ADOBERGB_LINEAR_EXT.as_raw(), + AdobeRgbNonLinear = ash::vk::ColorSpaceKHR::ADOBERGB_NONLINEAR_EXT.as_raw(), + PassThrough = ash::vk::ColorSpaceKHR::PASS_THROUGH_EXT.as_raw(), + DisplayNative = ash::vk::ColorSpaceKHR::DISPLAY_NATIVE_AMD.as_raw(), +} + +impl From for ash::vk::ColorSpaceKHR { + #[inline] + fn from(val: ColorSpace) -> Self { + Self::from_raw(val as i32) + } +} + +impl From for ColorSpace { + #[inline] + fn from(val: ash::vk::ColorSpaceKHR) -> Self { + match val { + ash::vk::ColorSpaceKHR::SRGB_NONLINEAR => ColorSpace::SrgbNonLinear, + ash::vk::ColorSpaceKHR::DISPLAY_P3_NONLINEAR_EXT => ColorSpace::DisplayP3NonLinear, + ash::vk::ColorSpaceKHR::EXTENDED_SRGB_LINEAR_EXT => ColorSpace::ExtendedSrgbLinear, + ash::vk::ColorSpaceKHR::EXTENDED_SRGB_NONLINEAR_EXT => { + ColorSpace::ExtendedSrgbNonLinear } - ) - } -} - -impl From for CapabilitiesError { - #[inline] - fn from(err: OomError) -> CapabilitiesError { - CapabilitiesError::OomError(err) - } -} - -impl From for CapabilitiesError { - #[inline] - fn from(err: Error) -> CapabilitiesError { - match err { - err @ Error::OutOfHostMemory => CapabilitiesError::OomError(OomError::from(err)), - err @ Error::OutOfDeviceMemory => CapabilitiesError::OomError(OomError::from(err)), - Error::SurfaceLost => CapabilitiesError::SurfaceLost, - _ => panic!("unexpected error: {:?}", err), + ash::vk::ColorSpaceKHR::DISPLAY_P3_LINEAR_EXT => ColorSpace::DisplayP3Linear, + ash::vk::ColorSpaceKHR::DCI_P3_NONLINEAR_EXT => ColorSpace::DciP3NonLinear, + ash::vk::ColorSpaceKHR::BT709_LINEAR_EXT => ColorSpace::Bt709Linear, + ash::vk::ColorSpaceKHR::BT709_NONLINEAR_EXT => ColorSpace::Bt709NonLinear, + ash::vk::ColorSpaceKHR::BT2020_LINEAR_EXT => ColorSpace::Bt2020Linear, + ash::vk::ColorSpaceKHR::HDR10_ST2084_EXT => ColorSpace::Hdr10St2084, + ash::vk::ColorSpaceKHR::DOLBYVISION_EXT => ColorSpace::DolbyVision, + ash::vk::ColorSpaceKHR::HDR10_HLG_EXT => ColorSpace::Hdr10Hlg, + ash::vk::ColorSpaceKHR::ADOBERGB_LINEAR_EXT => ColorSpace::AdobeRgbLinear, + ash::vk::ColorSpaceKHR::ADOBERGB_NONLINEAR_EXT => ColorSpace::AdobeRgbNonLinear, + ash::vk::ColorSpaceKHR::PASS_THROUGH_EXT => ColorSpace::PassThrough, + ash::vk::ColorSpaceKHR::DISPLAY_NATIVE_AMD => ColorSpace::DisplayNative, + _ => panic!("Wrong value for color space enum {:?}", val), } } } +/// The capabilities of a surface when used by a physical device. +/// +/// You have to match these capabilities when you create a swapchain. +#[derive(Clone, Debug)] +#[non_exhaustive] +pub struct SurfaceCapabilities { + /// Minimum number of images that must be present in the swapchain. + pub min_image_count: u32, + + /// Maximum number of images that must be present in the swapchain, or `None` if there is no + /// maximum value. Note that "no maximum" doesn't mean that you can set a very high value, as + /// 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. + pub current_extent: Option<[u32; 2]>, + + /// Minimum width and height of a swapchain that uses this surface. + pub min_image_extent: [u32; 2], + + /// Maximum width and height of a swapchain that uses this surface. + pub max_image_extent: [u32; 2], + + /// Maximum number of image layers if you create an image array. The minimum is 1. + pub max_image_array_layers: u32, + + /// List of transforms supported for the swapchain. + pub supported_transforms: SupportedSurfaceTransforms, + + /// Current transform used by the surface. + pub current_transform: SurfaceTransform, + + /// List of composite alpha modes supports for the swapchain. + pub supported_composite_alpha: SupportedCompositeAlpha, + + /// List of image usages that are supported for images of the swapchain. Only + /// the `color_attachment` usage is guaranteed to be supported. + pub supported_usage_flags: ImageUsage, + + /// Whether full-screen exclusivity is supported. + pub full_screen_exclusive_supported: bool, +} + #[cfg(test)] mod tests { use crate::swapchain::Surface; @@ -855,7 +1255,7 @@ mod tests { #[test] fn khr_win32_surface_ext_missing() { let instance = instance!(); - match unsafe { Surface::from_hwnd(instance, ptr::null::(), ptr::null::(), ()) } { + match unsafe { Surface::from_win32(instance, ptr::null::(), ptr::null::(), ()) } { Err(SurfaceCreationError::MissingExtension { .. }) => (), _ => panic!(), } @@ -891,7 +1291,7 @@ mod tests { #[test] fn khr_android_surface_ext_missing() { let instance = instance!(); - match unsafe { Surface::from_anativewindow(instance, ptr::null::(), ()) } { + match unsafe { Surface::from_android(instance, ptr::null::(), ()) } { Err(SurfaceCreationError::MissingExtension { .. }) => (), _ => panic!(), } diff --git a/vulkano/src/swapchain/swapchain.rs b/vulkano/src/swapchain/swapchain.rs index 0830a2aa..39db9556 100644 --- a/vulkano/src/swapchain/swapchain.rs +++ b/vulkano/src/swapchain/swapchain.rs @@ -7,12 +7,16 @@ // notice may not be copied, modified, or distributed except // according to those terms. +use super::SupportedCompositeAlpha; +use super::SupportedSurfaceTransforms; use crate::buffer::BufferAccess; use crate::check_errors; use crate::command_buffer::submit::SubmitAnyBuilder; use crate::command_buffer::submit::SubmitPresentBuilder; use crate::command_buffer::submit::SubmitPresentError; use crate::command_buffer::submit::SubmitSemaphoresWaitBuilder; +use crate::device::physical::SurfaceInfo; +use crate::device::physical::SurfacePropertiesError; use crate::device::Device; use crate::device::DeviceOwned; use crate::device::Queue; @@ -28,12 +32,12 @@ use crate::image::ImageTiling; use crate::image::ImageType; use crate::image::ImageUsage; use crate::image::SampleCount; -use crate::swapchain::CapabilitiesError; use crate::swapchain::ColorSpace; use crate::swapchain::CompositeAlpha; use crate::swapchain::PresentMode; use crate::swapchain::PresentRegion; use crate::swapchain::Surface; +use crate::swapchain::SurfaceApi; use crate::swapchain::SurfaceSwapchainLock; use crate::swapchain::SurfaceTransform; use crate::sync::semaphore::SemaphoreError; @@ -45,14 +49,15 @@ use crate::sync::FlushError; use crate::sync::GpuFuture; use crate::sync::PipelineStages; use crate::sync::Semaphore; -use crate::sync::SharingMode; +use crate::sync::Sharing; use crate::Error; use crate::OomError; use crate::Success; use crate::VulkanObject; +use smallvec::SmallVec; use std::error; use std::fmt; -use std::mem; +use std::hash::{Hash, Hasher}; use std::mem::MaybeUninit; use std::ptr; use std::sync::atomic::AtomicBool; @@ -61,34 +66,1308 @@ use std::sync::Arc; use std::sync::Mutex; use std::time::Duration; -/// The way fullscreen exclusivity is handled. +/// Contains the swapping system and the images that can be shown on a surface. +#[derive(Debug)] +pub struct Swapchain { + handle: ash::vk::SwapchainKHR, + device: Arc, + surface: Arc>, + + 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, + + // 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 { + // The actual image. + image: UnsafeImage, + // If true, then the image is still in the undefined layout and must be transitioned. + undefined_layout: 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> { + assert_eq!( + device.instance().internal_object(), + surface.instance().internal_object() + ); + + if !device.enabled_extensions().khr_swapchain { + return Err(SwapchainCreationError::ExtensionNotEnabled { + extension: "khr_swapchain", + reason: "created a new swapchain", + }); + } + + 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, images) = unsafe { + let (handle, image_handles) = Self::create(&device, &surface, &create_info, None)?; + let images = Self::wrap_images(&device, image_handles, &create_info); + (handle, images) + }; + + 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, + + 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, + + full_screen_exclusive_held: AtomicBool::new(false), + images, + retired: Mutex::new(false), + }); + + let swapchain_images = (0..swapchain.images.len()) + .map(|n| unsafe { SwapchainImage::from_raw(swapchain.clone(), n) }) + .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. + #[inline] + 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().unwrap(); + + // 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, images) = unsafe { + let (handle, image_handles) = + Self::create(&self.device, &self.surface, &create_info, Some(self))?; + let images = Self::wrap_images(&self.device, image_handles, &create_info); + (handle, images) + }; + + 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(), + + 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, + + full_screen_exclusive_held: AtomicBool::new(full_screen_exclusive_held), + images, + retired: Mutex::new(false), + }); + + let swapchain_images = (0..swapchain.images.len()) + .map(|n| unsafe { SwapchainImage::from_raw(swapchain.clone(), n) }) + .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; + + // VUID-VkSwapchainCreateInfoKHR-imageUsage-requiredbitmask + assert!(image_usage != ImageUsage::none()); + + if full_screen_exclusive != FullScreenExclusive::Default + && !device.enabled_extensions().ext_full_screen_exclusive + { + return Err(SwapchainCreationError::ExtensionNotEnabled { + extension: "ext_full_screen_exclusive", + reason: "`full_screen_exclusive` was not `FullScreenExclusive::Default`", + }); + } + + 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 + *image_format = Some({ + let surface_formats = device.physical_device().surface_formats( + &surface, + SurfaceInfo { + full_screen_exclusive, + win32_monitor, + ..Default::default() + }, + )?; + + if let Some(format) = image_format { + // 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(|| f) + }) + .ok_or_else(|| SwapchainCreationError::FormatColorSpaceNotSupported)? + } + }); + + let surface_capabilities = device.physical_device().surface_capabilities( + &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 + // Shouldn't be possible with a properly behaving device + assert!(image_extent[0] != 0 || image_extent[1] != 0); + + // 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(ids) => { + // VUID-VkSwapchainCreateInfoKHR-imageSharingMode-01278 + // VUID-VkSwapchainCreateInfoKHR-imageSharingMode-01428 + ids.sort_unstable(); + ids.dedup(); + assert!(ids.len() >= 2); + + for &id in ids.iter() { + // VUID-VkSwapchainCreateInfoKHR-imageSharingMode-01428 + if device.physical_device().queue_family_by_id(id).is_none() { + return Err(SwapchainCreationError::ImageSharingInvalidQueueFamilyId { + id, + }); + } + } + } + }; + + // VUID-VkSwapchainCreateInfoKHR-preTransform-01279 + if !surface_capabilities + .supported_transforms + .supports(pre_transform) + { + return Err(SwapchainCreationError::PreTransformNotSupported { + provided: pre_transform, + supported: surface_capabilities.supported_transforms, + }); + } + + // VUID-VkSwapchainCreateInfoKHR-compositeAlpha-01280 + if !surface_capabilities + .supported_composite_alpha + .supports(composite_alpha) + { + return Err(SwapchainCreationError::CompositeAlphaNotSupported { + provided: composite_alpha, + supported: surface_capabilities.supported_composite_alpha, + }); + } + + // VUID-VkSwapchainCreateInfoKHR-presentMode-01281 + if !device + .physical_device() + .surface_present_modes(&surface)? + .any(|mode| mode == present_mode) + { + return Err(SwapchainCreationError::PresentModeNotSupported); + } + + // VUID-VkSwapchainCreateInfoKHR-imageFormat-01778 + if device + .physical_device() + .image_format_properties( + image_format.unwrap(), + ImageType::Dim2d, + ImageTiling::Optimal, + image_usage, + ImageCreateFlags::none(), + None, + None, + )? + .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 surface_full_screen_exclusive_info = + if full_screen_exclusive != FullScreenExclusive::Default { + Some(ash::vk::SurfaceFullScreenExclusiveInfoEXT { + full_screen_exclusive: full_screen_exclusive.into(), + ..Default::default() + }) + } else { + None + }; + + let mut surface_full_screen_exclusive_win32_info = + if let Some(Win32Monitor(hmonitor)) = win32_monitor { + Some(ash::vk::SurfaceFullScreenExclusiveWin32InfoEXT { + hmonitor, + ..Default::default() + }) + } else { + None + }; + + let mut create_info = ash::vk::SwapchainCreateInfoKHR { + flags: ash::vk::SwapchainCreateFlagsKHR::empty(), + surface: surface.internal_object(), + 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() + }; + + if let Some(surface_full_screen_exclusive_info) = + surface_full_screen_exclusive_info.as_mut() + { + surface_full_screen_exclusive_info.p_next = create_info.p_next as *mut _; + create_info.p_next = surface_full_screen_exclusive_info 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 = create_info.p_next as *mut _; + create_info.p_next = surface_full_screen_exclusive_win32_info as *const _ as *const _; + } + + let handle = { + let fns = device.fns(); + let mut output = MaybeUninit::uninit(); + check_errors(fns.khr_swapchain.create_swapchain_khr( + device.internal_object(), + &create_info, + ptr::null(), + output.as_mut_ptr(), + ))?; + output.assume_init() + }; + + let image_handles = { + let fns = device.fns(); + let mut num = 0; + check_errors(fns.khr_swapchain.get_swapchain_images_khr( + device.internal_object(), + handle, + &mut num, + ptr::null_mut(), + ))?; + + let mut images = Vec::with_capacity(num as usize); + check_errors(fns.khr_swapchain.get_swapchain_images_khr( + device.internal_object(), + handle, + &mut num, + images.as_mut_ptr(), + ))?; + images.set_len(num as usize); + images + }; + + Ok((handle, image_handles)) + } + + unsafe fn wrap_images( + device: &Arc, + image_handles: Vec, + create_info: &SwapchainCreateInfo, + ) -> Vec { + let &SwapchainCreateInfo { + image_format, + image_extent, + image_array_layers, + image_usage, + ref image_sharing, // TODO: put this in the image too + .. + } = create_info; + + image_handles + .into_iter() + .map(|handle| { + let dims = ImageDimensions::Dim2d { + width: image_extent[0], + height: image_extent[1], + array_layers: image_array_layers, + }; + + let img = unsafe { + UnsafeImage::from_raw( + device.clone(), + handle, + image_usage, + image_format.unwrap(), + ImageCreateFlags::none(), + dims, + SampleCount::Sample1, + 1, + ) + }; + + ImageEntry { + image: img, + undefined_layout: AtomicBool::new(true), + } + }) + .collect() + } + + /// 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 + } + + /// Returns of the images that belong to this swapchain. + #[inline] + pub fn raw_image(&self, offset: usize) -> Option { + self.images.get(offset).map(|i| ImageInner { + image: &i.image, + first_layer: 0, + num_layers: self.image_array_layers as usize, + first_mipmap_level: 0, + num_mipmap_levels: 1, + }) + } + + /// 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 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`. + 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 { + check_errors( + self.device + .fns() + .ext_full_screen_exclusive + .acquire_full_screen_exclusive_mode_ext( + self.device.internal_object(), + self.handle, + ), + )?; + } + + Ok(()) + } + + /// Releases full-screen exclusivity. + /// + /// The swapchain must have been created with [`FullScreenExclusive::ApplicationControlled`], + /// and must currently hold full-screen exclusivity. + 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 { + check_errors( + self.device + .fns() + .ext_full_screen_exclusive + .release_full_screen_exclusive_mode_ext( + self.device.internal_object(), + self.handle, + ), + )?; + } + + 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. + 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_offset: usize) { + let image_entry = self.images.get(image_offset); + if let Some(ref image_entry) = image_entry { + image_entry.undefined_layout.store(false, Ordering::SeqCst); + } + } + + pub(crate) fn is_image_layout_initialized(&self, image_offset: usize) -> bool { + let image_entry = self.images.get(image_offset); + if let Some(ref image_entry) = image_entry { + !image_entry.undefined_layout.load(Ordering::SeqCst) + } 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.internal_object(), + self.handle, + ptr::null(), + ); + self.surface.flag().store(false, Ordering::Release); + } + } +} + +unsafe impl VulkanObject for Swapchain { + type Object = ash::vk::SwapchainKHR; + + #[inline] + fn internal_object(&self) -> ash::vk::SwapchainKHR { + self.handle + } +} + +unsafe impl DeviceOwned for Swapchain { + fn device(&self) -> &Arc { + &self.device + } +} + +impl PartialEq for Swapchain { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.handle == other.handle && self.device() == other.device() + } +} + +impl Eq for Swapchain {} + +impl Hash for Swapchain { + #[inline] + fn hash(&self, state: &mut H) { + self.handle.hash(state); + self.device().hash(state); + } +} + +/// 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 set to `None`, the value of + /// [`SurfaceCapabilities::current_extent`](crate::swapchain::SurfaceCapabilities) will be used. + /// + /// The default value is `None`. + 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::none()`], 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::none(), + 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, + + ExtensionNotEnabled { + extension: &'static str, + reason: &'static str, + }, + + /// The provided `composite_alpha` is not supported by the surface for this device. + CompositeAlphaNotSupported { + provided: CompositeAlpha, + supported: SupportedCompositeAlpha, + }, + + /// 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 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 + /// ids was not valid. + ImageSharingInvalidQueueFamilyId { id: 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: SupportedSurfaceTransforms, + }, + + /// 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::Error for SwapchainCreationError { + #[inline] + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + match *self { + Self::OomError(ref err) => Some(err), + _ => None, + } + } +} + +impl fmt::Display for SwapchainCreationError { + #[inline] + fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { + match *self { + Self::OomError(_) => write!(fmt, "not enough memory available",), + Self::DeviceLost => write!(fmt, "the device was lost",), + Self::SurfaceLost => write!(fmt, "the surface was lost",), + Self::SurfaceInUse => { + write!(fmt, "the surface is already used by another swapchain",) + } + Self::NativeWindowInUse => { + write!(fmt, "the window is already in use by another API") + } + + Self::ExtensionNotEnabled { extension, reason } => write!( + fmt, + "the extension {} must be enabled: {}", + extension, reason + ), + + Self::CompositeAlphaNotSupported { .. } => write!( + fmt, + "the provided `composite_alpha` is not supported by the surface for this device", + ), + Self::FormatColorSpaceNotSupported => write!( + fmt, + "the provided `format` and `color_space` are not supported by the surface for this device", + ), + Self::ImageArrayLayersNotSupported { provided, max_supported } => write!( + fmt, + "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!( + fmt, + "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::ImageFormatPropertiesNotSupported => write!( + fmt, + "the provided image parameters are not supported as queried from `image_format_properties`", + ), + Self::ImageSharingInvalidQueueFamilyId { id } => write!( + fmt, + "the provided `image_sharing` was set to `Concurrent`, but one of the specified queue family ids ({}) was not valid", + id, + ), + Self::ImageUsageNotSupported { .. } => write!( + fmt, + "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!( + fmt, + "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!( + fmt, + "the provided `present_mode` is not supported by the surface for this device", + ), + Self::PreTransformNotSupported { .. } => write!( + fmt, + "the provided `pre_transform` is not supported by the surface for this device", + ), + Self::SwapchainAlreadyRetired => write!( + fmt, + "the swapchain has already been used to create a new one", + ), + Self::Win32MonitorInvalid => write!( + fmt, + "the `win32_monitor` value was `Some` when it must be `None` or vice-versa", + ), + } + } +} + +impl From for SwapchainCreationError { + #[inline] + fn from(err: Error) -> SwapchainCreationError { + match err { + err @ Error::OutOfHostMemory => Self::OomError(OomError::from(err)), + err @ Error::OutOfDeviceMemory => Self::OomError(OomError::from(err)), + Error::DeviceLost => Self::DeviceLost, + Error::SurfaceLost => Self::SurfaceLost, + Error::NativeWindowInUse => Self::NativeWindowInUse, + _ => panic!("unexpected error: {:?}", err), + } + } +} + +impl From for SwapchainCreationError { + #[inline] + fn from(err: OomError) -> SwapchainCreationError { + Self::OomError(err) + } +} + +impl From for SwapchainCreationError { + #[inline] + fn from(err: SurfacePropertiesError) -> SwapchainCreationError { + match err { + SurfacePropertiesError::OomError(err) => Self::OomError(err), + SurfacePropertiesError::SurfaceLost => Self::SurfaceLost, + SurfacePropertiesError::NotSupported => unreachable!(), + } + } +} + +/// The way full-screen exclusivity is handled. #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[repr(i32)] -pub enum FullscreenExclusive { +#[non_exhaustive] +pub enum FullScreenExclusive { /// Indicates that the driver should determine the appropriate full-screen method /// by whatever means it deems appropriate. Default = ash::vk::FullScreenExclusiveEXT::DEFAULT.as_raw(), + /// 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 = ash::vk::FullScreenExclusiveEXT::ALLOWED.as_raw(), + /// Indicates that the driver should avoid using full-screen mechanisms which rely /// on disruptive transitions. Disallowed = ash::vk::FullScreenExclusiveEXT::DISALLOWED.as_raw(), - /// Indicates the application will manage full-screen exclusive mode by using - /// `Swapchain::acquire_fullscreen_exclusive()` and - /// `Swapchain::release_fullscreen_exclusive()` functions. - AppControlled = ash::vk::FullScreenExclusiveEXT::APPLICATION_CONTROLLED.as_raw(), + + /// 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 = ash::vk::FullScreenExclusiveEXT::APPLICATION_CONTROLLED.as_raw(), } -impl From for ash::vk::FullScreenExclusiveEXT { +impl From for ash::vk::FullScreenExclusiveEXT { #[inline] - fn from(val: FullscreenExclusive) -> Self { + fn from(val: FullScreenExclusive) -> Self { Self::from_raw(val as i32) } } +/// 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. + #[inline] + 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::Error for FullScreenExclusiveError { + #[inline] + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + match *self { + FullScreenExclusiveError::OomError(ref err) => Some(err), + _ => None, + } + } +} + +impl fmt::Display for FullScreenExclusiveError { + #[inline] + fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!( + fmt, + "{}", + 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 { + #[inline] + fn from(err: Error) -> FullScreenExclusiveError { + match err { + err @ Error::OutOfHostMemory => FullScreenExclusiveError::OomError(OomError::from(err)), + err @ Error::OutOfDeviceMemory => { + FullScreenExclusiveError::OomError(OomError::from(err)) + } + Error::SurfaceLost => FullScreenExclusiveError::SurfaceLost, + Error::InitializationFailed => FullScreenExclusiveError::InitializationFailed, + _ => panic!("unexpected error: {:?}", err), + } + } +} + +impl From for FullScreenExclusiveError { + #[inline] + 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 @@ -112,17 +1391,17 @@ pub fn acquire_next_image( // 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 stale = swapchain.stale.lock().unwrap(); - if *stale { + let retired = swapchain.retired.lock().unwrap(); + if *retired { return Err(AcquireError::OutOfDate); } let acquire_result = unsafe { acquire_next_image_raw(&swapchain, timeout, Some(&semaphore), Some(&fence)) }; - if let &Err(AcquireError::FullscreenExclusiveLost) = &acquire_result { + if let &Err(AcquireError::FullScreenExclusiveLost) = &acquire_result { swapchain - .fullscreen_exclusive_held + .full_screen_exclusive_held .store(false, Ordering::SeqCst); } @@ -214,933 +1493,6 @@ where } } -/// Contains the swapping system and the images that can be shown on a surface. -pub struct Swapchain { - // The Vulkan device this swapchain was created with. - device: Arc, - // The surface, which we need to keep alive. - surface: Arc>, - // The swapchain object. - swapchain: ash::vk::SwapchainKHR, - - // 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. - stale: Mutex, - - // Parameters passed to the constructor. - num_images: u32, - format: Format, - color_space: ColorSpace, - dimensions: [u32; 2], - layers: u32, - usage: ImageUsage, - sharing_mode: SharingMode, - transform: SurfaceTransform, - composite_alpha: CompositeAlpha, - present_mode: PresentMode, - fullscreen_exclusive: FullscreenExclusive, - fullscreen_exclusive_held: AtomicBool, - clipped: bool, -} - -struct ImageEntry { - // The actual image. - image: UnsafeImage, - // If true, then the image is still in the undefined layout and must be transitioned. - undefined_layout: AtomicBool, -} - -impl Swapchain { - /// Starts the process of building a new swapchain, using default values for the parameters. - #[inline] - pub fn start(device: Arc, surface: Arc>) -> SwapchainBuilder { - SwapchainBuilder { - device, - surface, - - num_images: 2, - format: None, - color_space: ColorSpace::SrgbNonLinear, - dimensions: None, - layers: 1, - usage: ImageUsage::none(), - sharing_mode: SharingMode::Exclusive, - transform: Default::default(), - composite_alpha: CompositeAlpha::Opaque, - present_mode: PresentMode::Fifo, - fullscreen_exclusive: FullscreenExclusive::Default, - clipped: true, - - old_swapchain: None, - } - } - - /// Starts building a new swapchain from an existing swapchain. - /// - /// Use this when a swapchain has become invalidated, such as due to window resizes. - /// The builder is pre-filled with the parameters of the old one, except for `dimensions`, - /// which is set to `None`. - #[inline] - pub fn recreate(self: &Arc) -> SwapchainBuilder { - SwapchainBuilder { - device: self.device().clone(), - surface: self.surface().clone(), - - num_images: self.images.len() as u32, - format: Some(self.format), - color_space: self.color_space, - dimensions: None, - layers: self.layers, - usage: self.usage, - sharing_mode: self.sharing_mode.clone(), - transform: self.transform, - composite_alpha: self.composite_alpha, - present_mode: self.present_mode, - fullscreen_exclusive: self.fullscreen_exclusive, - clipped: self.clipped, - - old_swapchain: Some(self.clone()), - } - } - - /// Returns the saved Surface, from the Swapchain creation. - #[inline] - pub fn surface(&self) -> &Arc> { - &self.surface - } - - /// Returns of the images that belong to this swapchain. - #[inline] - pub fn raw_image(&self, offset: usize) -> Option { - self.images.get(offset).map(|i| ImageInner { - image: &i.image, - first_layer: 0, - num_layers: self.layers as usize, - first_mipmap_level: 0, - num_mipmap_levels: 1, - }) - } - - /// Returns the number of images of the swapchain. - #[inline] - pub fn num_images(&self) -> u32 { - self.images.len() as u32 - } - - /// Returns the format of the images of the swapchain. - #[inline] - pub fn format(&self) -> Format { - self.format - } - - /// Returns the dimensions of the images of the swapchain. - #[inline] - pub fn dimensions(&self) -> [u32; 2] { - self.dimensions - } - - /// Returns the number of layers of the images of the swapchain. - #[inline] - pub fn layers(&self) -> u32 { - self.layers - } - - /// Returns the transform that was passed when creating the swapchain. - #[inline] - pub fn transform(&self) -> SurfaceTransform { - self.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 'fullscreen_exclusive` that was passed when creating the swapchain. - #[inline] - pub fn fullscreen_exclusive(&self) -> FullscreenExclusive { - self.fullscreen_exclusive - } - - /// `FullscreenExclusive::AppControlled` must be the active fullscreen exclusivity mode. - /// Acquire fullscreen exclusivity until either the `release_fullscreen_exclusive` is - /// called, or if any of the the other `Swapchain` functions return `FullscreenExclusiveLost`. - /// Requires: `FullscreenExclusive::AppControlled` - pub fn acquire_fullscreen_exclusive(&self) -> Result<(), FullscreenExclusiveError> { - if self.fullscreen_exclusive != FullscreenExclusive::AppControlled { - return Err(FullscreenExclusiveError::NotAppControlled); - } - - if self.fullscreen_exclusive_held.swap(true, Ordering::SeqCst) { - return Err(FullscreenExclusiveError::DoubleAcquire); - } - - unsafe { - check_errors( - self.device - .fns() - .ext_full_screen_exclusive - .acquire_full_screen_exclusive_mode_ext( - self.device.internal_object(), - self.swapchain, - ), - )?; - } - - Ok(()) - } - - /// `FullscreenExclusive::AppControlled` must be the active fullscreen exclusivity mode. - /// Release fullscreen exclusivity. - pub fn release_fullscreen_exclusive(&self) -> Result<(), FullscreenExclusiveError> { - if self.fullscreen_exclusive != FullscreenExclusive::AppControlled { - return Err(FullscreenExclusiveError::NotAppControlled); - } - - if !self.fullscreen_exclusive_held.swap(false, Ordering::SeqCst) { - return Err(FullscreenExclusiveError::DoubleRelease); - } - - unsafe { - check_errors( - self.device - .fns() - .ext_full_screen_exclusive - .release_full_screen_exclusive_mode_ext( - self.device.internal_object(), - self.swapchain, - ), - )?; - } - - Ok(()) - } - - /// `FullscreenExclusive::AppControlled` is not the active fullscreen exclusivity mode, - /// then this function will always return false. If true is returned the swapchain - /// is in `FullscreenExclusive::AppControlled` fullscreen exclusivity mode and exclusivity - /// is currently acquired. - pub fn is_fullscreen_exclusive(&self) -> bool { - if self.fullscreen_exclusive != FullscreenExclusive::AppControlled { - false - } else { - self.fullscreen_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_offset: usize) { - let image_entry = self.images.get(image_offset); - if let Some(ref image_entry) = image_entry { - image_entry.undefined_layout.store(false, Ordering::SeqCst); - } - } - - pub(crate) fn is_image_layout_initialized(&self, image_offset: usize) -> bool { - let image_entry = self.images.get(image_offset); - if let Some(ref image_entry) = image_entry { - !image_entry.undefined_layout.load(Ordering::SeqCst) - } else { - false - } - } -} - -unsafe impl VulkanObject for Swapchain { - type Object = ash::vk::SwapchainKHR; - - #[inline] - fn internal_object(&self) -> ash::vk::SwapchainKHR { - self.swapchain - } -} - -unsafe impl DeviceOwned for Swapchain { - fn device(&self) -> &Arc { - &self.device - } -} - -impl fmt::Debug for Swapchain { - #[inline] - fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!(fmt, "", self.swapchain) - } -} - -impl Drop for Swapchain { - #[inline] - fn drop(&mut self) { - unsafe { - let fns = self.device.fns(); - fns.khr_swapchain.destroy_swapchain_khr( - self.device.internal_object(), - self.swapchain, - ptr::null(), - ); - self.surface.flag().store(false, Ordering::Release); - } - } -} - -/// Builder for a [`Swapchain`]. -#[derive(Debug)] -pub struct SwapchainBuilder { - device: Arc, - surface: Arc>, - old_swapchain: Option>>, - - num_images: u32, - format: Option, // None = use a default - color_space: ColorSpace, - dimensions: Option<[u32; 2]>, - layers: u32, - usage: ImageUsage, - sharing_mode: SharingMode, - transform: SurfaceTransform, - composite_alpha: CompositeAlpha, - present_mode: PresentMode, - fullscreen_exclusive: FullscreenExclusive, - clipped: bool, -} - -impl SwapchainBuilder { - /// Builds a new swapchain. Allocates images who content can be made visible on a surface. - /// - /// See also the `Surface::get_capabilities` function which returns the values that are - /// supported by the implementation. All the parameters that you pass to the builder - /// must be supported. - /// - /// 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. - /// - /// # Panic - /// - /// - Panics if the device and the surface don't belong to the same instance. - /// - Panics if `usage` is empty. - /// - // TODO: isn't it unsafe to take the surface through an Arc when it comes to vulkano-win? - pub fn build( - self, - ) -> Result<(Arc>, Vec>>), SwapchainCreationError> { - let SwapchainBuilder { - device, - surface, - old_swapchain, - - num_images, - format, - color_space, - dimensions, - layers, - usage, - sharing_mode, - transform, - composite_alpha, - present_mode, - fullscreen_exclusive, - clipped, - } = self; - - assert_eq!( - device.instance().internal_object(), - surface.instance().internal_object() - ); - - // Checking that the requested parameters match the capabilities. - let capabilities = surface.capabilities(device.physical_device())?; - if num_images < capabilities.min_image_count { - return Err(SwapchainCreationError::UnsupportedMinImagesCount); - } - if let Some(c) = capabilities.max_image_count { - if num_images > c { - return Err(SwapchainCreationError::UnsupportedMaxImagesCount); - } - } - - let format = { - if let Some(format) = format { - if !capabilities - .supported_formats - .iter() - .any(|&(f, c)| f == format && c == color_space) - { - return Err(SwapchainCreationError::UnsupportedFormat); - } - format - } else { - if let Some(format) = [Format::R8G8B8A8_UNORM, Format::B8G8R8A8_UNORM] - .iter() - .copied() - .find(|&format| { - capabilities - .supported_formats - .iter() - .any(|&(f, c)| f == format && c == color_space) - }) - { - format - } else { - return Err(SwapchainCreationError::UnsupportedFormat); - } - } - }; - - let dimensions = if let Some(dimensions) = dimensions { - if dimensions[0] < capabilities.min_image_extent[0] { - return Err(SwapchainCreationError::UnsupportedDimensions); - } - if dimensions[1] < capabilities.min_image_extent[1] { - return Err(SwapchainCreationError::UnsupportedDimensions); - } - if dimensions[0] > capabilities.max_image_extent[0] { - return Err(SwapchainCreationError::UnsupportedDimensions); - } - if dimensions[1] > capabilities.max_image_extent[1] { - return Err(SwapchainCreationError::UnsupportedDimensions); - } - dimensions - } else { - capabilities.current_extent.unwrap() - }; - if layers < 1 || layers > capabilities.max_image_array_layers { - return Err(SwapchainCreationError::UnsupportedArrayLayers); - } - if (ash::vk::ImageUsageFlags::from(usage) - & ash::vk::ImageUsageFlags::from(capabilities.supported_usage_flags)) - != ash::vk::ImageUsageFlags::from(usage) - { - return Err(SwapchainCreationError::UnsupportedUsageFlags); - } - if !capabilities.supported_transforms.supports(transform) { - return Err(SwapchainCreationError::UnsupportedSurfaceTransform); - } - if !capabilities - .supported_composite_alpha - .supports(composite_alpha) - { - return Err(SwapchainCreationError::UnsupportedCompositeAlpha); - } - if !capabilities.present_modes.supports(present_mode) { - return Err(SwapchainCreationError::UnsupportedPresentMode); - } - - let flags = ImageCreateFlags::none(); - - // check that the physical device supports the swapchain image configuration - if device - .physical_device() - .image_format_properties( - format, - ImageType::Dim2d, - ImageTiling::Optimal, - usage, - flags, - None, - None, - )? - .is_none() - { - return Err(SwapchainCreationError::UnsupportedImageConfiguration); - } - - // If we recreate a swapchain, make sure that the surface is the same. - if let Some(ref sc) = old_swapchain { - if surface.internal_object() != sc.surface.internal_object() { - return Err(SwapchainCreationError::OldSwapchainSurfaceMismatch); - } - } else { - // Checking that the surface doesn't already have a swapchain. - let has_already = surface.flag().swap(true, Ordering::AcqRel); - if has_already { - return Err(SwapchainCreationError::SurfaceInUse); - } - } - - if !device.enabled_extensions().khr_swapchain { - return Err(SwapchainCreationError::MissingExtensionKHRSwapchain); - } - - let mut surface_full_screen_exclusive_info = None; - - // TODO: VK_EXT_FULL_SCREEN_EXCLUSIVE requires these extensions, so they should always - // be enabled if it is. A separate check here is unnecessary; this should be checked at - // device creation. - if device.enabled_extensions().ext_full_screen_exclusive - && surface - .instance() - .enabled_extensions() - .khr_get_physical_device_properties2 - && surface - .instance() - .enabled_extensions() - .khr_get_surface_capabilities2 - { - surface_full_screen_exclusive_info = Some(ash::vk::SurfaceFullScreenExclusiveInfoEXT { - full_screen_exclusive: fullscreen_exclusive.into(), - ..Default::default() - }); - } - - let p_next = match surface_full_screen_exclusive_info.as_ref() { - Some(some) => unsafe { mem::transmute(some as *const _) }, - None => ptr::null(), - }; - - // Required by the specs. - assert_ne!(usage, ImageUsage::none()); - - if let Some(ref old_swapchain) = old_swapchain { - let mut stale = old_swapchain.stale.lock().unwrap(); - - // The swapchain has already been used to create a new one. - if *stale { - return Err(SwapchainCreationError::OldSwapchainAlreadyUsed); - } 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 stale to true and keep it to true even if the call to `vkCreateSwapchainKHR` below fails. - *stale = true; - } - } - - let fns = device.fns(); - - let swapchain = unsafe { - let (sh_mode, sh_count, sh_indices) = match sharing_mode { - SharingMode::Exclusive => (ash::vk::SharingMode::EXCLUSIVE, 0, ptr::null()), - SharingMode::Concurrent(ref ids) => ( - ash::vk::SharingMode::CONCURRENT, - ids.len() as u32, - ids.as_ptr(), - ), - }; - - let infos = ash::vk::SwapchainCreateInfoKHR { - p_next, - flags: ash::vk::SwapchainCreateFlagsKHR::empty(), - surface: surface.internal_object(), - min_image_count: num_images, - image_format: format.into(), - image_color_space: color_space.into(), - image_extent: ash::vk::Extent2D { - width: dimensions[0], - height: dimensions[1], - }, - image_array_layers: layers, - image_usage: usage.into(), - image_sharing_mode: sh_mode, - queue_family_index_count: sh_count, - p_queue_family_indices: sh_indices, - pre_transform: transform.into(), - composite_alpha: composite_alpha.into(), - present_mode: present_mode.into(), - clipped: if clipped { - ash::vk::TRUE - } else { - ash::vk::FALSE - }, - old_swapchain: if let Some(ref old_swapchain) = old_swapchain { - old_swapchain.swapchain - } else { - ash::vk::SwapchainKHR::null() - }, - ..Default::default() - }; - - let mut output = MaybeUninit::uninit(); - check_errors(fns.khr_swapchain.create_swapchain_khr( - device.internal_object(), - &infos, - ptr::null(), - output.as_mut_ptr(), - ))?; - output.assume_init() - }; - - let image_handles = unsafe { - let mut num = 0; - check_errors(fns.khr_swapchain.get_swapchain_images_khr( - device.internal_object(), - swapchain, - &mut num, - ptr::null_mut(), - ))?; - - let mut images = Vec::with_capacity(num as usize); - check_errors(fns.khr_swapchain.get_swapchain_images_khr( - device.internal_object(), - swapchain, - &mut num, - images.as_mut_ptr(), - ))?; - images.set_len(num as usize); - images - }; - - let images = image_handles - .into_iter() - .map(|image| unsafe { - let dims = ImageDimensions::Dim2d { - width: dimensions[0], - height: dimensions[1], - array_layers: layers, - }; - - let img = UnsafeImage::from_raw( - device.clone(), - image, - usage, - format, - flags, - dims, - SampleCount::Sample1, - 1, - ); - - ImageEntry { - image: img, - undefined_layout: AtomicBool::new(true), - } - }) - .collect::>(); - - let fullscreen_exclusive_held = old_swapchain - .as_ref() - .map(|old_swapchain| { - if old_swapchain.fullscreen_exclusive != FullscreenExclusive::AppControlled { - false - } else { - old_swapchain - .fullscreen_exclusive_held - .load(Ordering::SeqCst) - } - }) - .unwrap_or(false); - - let swapchain = Arc::new(Swapchain { - device: device.clone(), - surface: surface.clone(), - swapchain, - images, - stale: Mutex::new(false), - num_images, - format, - color_space, - dimensions, - layers, - usage: usage.clone(), - sharing_mode, - transform, - composite_alpha, - present_mode, - fullscreen_exclusive, - fullscreen_exclusive_held: AtomicBool::new(fullscreen_exclusive_held), - clipped, - }); - - let swapchain_images = unsafe { - let mut swapchain_images = Vec::with_capacity(swapchain.images.len()); - for n in 0..swapchain.images.len() { - swapchain_images.push(SwapchainImage::from_raw(swapchain.clone(), n)?); - } - swapchain_images - }; - - Ok((swapchain, swapchain_images)) - } - - /// Sets the number of images that will be created. - /// - /// The default is 2. - #[inline] - pub fn num_images(mut self, num_images: u32) -> Self { - self.num_images = num_images; - self - } - - /// Sets the pixel format that will be used for the images. - /// - /// The default is either `R8G8B8A8Unorm` or `B8G8R8A8Unorm`, whichever is supported. - #[inline] - pub fn format(mut self, format: Format) -> Self { - self.format = Some(format); - self - } - - /// Sets the color space that will be used for the images. - /// - /// The default is `SrgbNonLinear`. - #[inline] - pub fn color_space(mut self, color_space: ColorSpace) -> Self { - self.color_space = color_space; - self - } - - /// Sets the dimensions of the images. - /// - /// The default is `None`, which means the value of - /// [`Capabilities::current_extent`](crate::swapchain::Capabilities::current_extent) will be - /// used. Setting this will override it with a custom `Some` value. - #[inline] - pub fn dimensions(mut self, dimensions: [u32; 2]) -> Self { - self.dimensions = Some(dimensions); - self - } - - /// Sets the number of layers for each image. - /// - /// The default is 1. - #[inline] - pub fn layers(mut self, layers: u32) -> Self { - self.layers = layers; - self - } - - /// Sets how the images will be used. - /// - /// The default is `ImageUsage::none()`. - #[inline] - pub fn usage(mut self, usage: ImageUsage) -> Self { - self.usage = usage; - self - } - - /// Sets the sharing mode of the images. - /// - /// The default is `Exclusive`. - #[inline] - pub fn sharing_mode(mut self, sharing_mode: S) -> Self - where - S: Into, - { - self.sharing_mode = sharing_mode.into(); - self - } - - /// Sets the transform that is to be applied to the surface. - /// - /// The default is `Identity`. - #[inline] - pub fn transform(mut self, transform: SurfaceTransform) -> Self { - self.transform = transform; - self - } - - /// Sets how alpha values of the pixels in the image are to be treated. - /// - /// The default is `Opaque`. - #[inline] - pub fn composite_alpha(mut self, composite_alpha: CompositeAlpha) -> Self { - self.composite_alpha = composite_alpha; - self - } - - /// Sets the present mode for the swapchain. - /// - /// The default is `Fifo`. - #[inline] - pub fn present_mode(mut self, present_mode: PresentMode) -> Self { - self.present_mode = present_mode; - self - } - - /// Sets how fullscreen exclusivity is to be handled. - /// - /// The default is `Default`. - #[inline] - pub fn fullscreen_exclusive(mut self, fullscreen_exclusive: FullscreenExclusive) -> Self { - self.fullscreen_exclusive = fullscreen_exclusive; - self - } - - /// Sets 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 is `true`. - #[inline] - pub fn clipped(mut self, clipped: bool) -> Self { - self.clipped = clipped; - self - } -} - -/// Error that can happen when creation 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, - /// The `VK_KHR_swapchain` extension was not enabled. - MissingExtensionKHRSwapchain, - /// The `VK_EXT_full_screen_exclusive` extension was not enabled. - MissingExtensionExtFullScreenExclusive, - /// Surface mismatch between old and new swapchain. - OldSwapchainSurfaceMismatch, - /// The old swapchain has already been used to recreate another one. - OldSwapchainAlreadyUsed, - /// The requested number of swapchain images is not supported by the surface. - UnsupportedMinImagesCount, - /// The requested number of swapchain images is not supported by the surface. - UnsupportedMaxImagesCount, - /// The requested image format is not supported by the surface. - UnsupportedFormat, - /// The requested dimensions are not supported by the surface. - UnsupportedDimensions, - /// The requested array layers count is not supported by the surface. - UnsupportedArrayLayers, - /// The requested image usage is not supported by the surface. - UnsupportedUsageFlags, - /// The requested surface transform is not supported by the surface. - UnsupportedSurfaceTransform, - /// The requested composite alpha is not supported by the surface. - UnsupportedCompositeAlpha, - /// The requested present mode is not supported by the surface. - UnsupportedPresentMode, - /// The image configuration is not supported by the physical device. - UnsupportedImageConfiguration, -} - -impl error::Error for SwapchainCreationError { - #[inline] - fn source(&self) -> Option<&(dyn error::Error + 'static)> { - match *self { - SwapchainCreationError::OomError(ref err) => Some(err), - _ => None, - } - } -} - -impl fmt::Display for SwapchainCreationError { - #[inline] - fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!( - fmt, - "{}", - match *self { - SwapchainCreationError::OomError(_) => "not enough memory available", - SwapchainCreationError::DeviceLost => "the device was lost", - SwapchainCreationError::SurfaceLost => "the surface was lost", - SwapchainCreationError::SurfaceInUse => { - "the surface is already used by another swapchain" - } - SwapchainCreationError::NativeWindowInUse => { - "the window is already in use by another API" - } - SwapchainCreationError::MissingExtensionKHRSwapchain => { - "the `VK_KHR_swapchain` extension was not enabled" - } - SwapchainCreationError::MissingExtensionExtFullScreenExclusive => { - "the `VK_EXT_full_screen_exclusive` extension was not enabled" - } - SwapchainCreationError::OldSwapchainSurfaceMismatch => { - "surface mismatch between old and new swapchain" - } - SwapchainCreationError::OldSwapchainAlreadyUsed => { - "old swapchain has already been used to recreate a new one" - } - SwapchainCreationError::UnsupportedMinImagesCount => { - "the requested number of swapchain images is not supported by the surface" - } - SwapchainCreationError::UnsupportedMaxImagesCount => { - "the requested number of swapchain images is not supported by the surface" - } - SwapchainCreationError::UnsupportedFormat => { - "the requested image format is not supported by the surface" - } - SwapchainCreationError::UnsupportedDimensions => { - "the requested dimensions are not supported by the surface" - } - SwapchainCreationError::UnsupportedArrayLayers => { - "the requested array layers count is not supported by the surface" - } - SwapchainCreationError::UnsupportedUsageFlags => { - "the requested image usage is not supported by the surface" - } - SwapchainCreationError::UnsupportedSurfaceTransform => { - "the requested surface transform is not supported by the surface" - } - SwapchainCreationError::UnsupportedCompositeAlpha => { - "the requested composite alpha is not supported by the surface" - } - SwapchainCreationError::UnsupportedPresentMode => { - "the requested present mode is not supported by the surface" - } - SwapchainCreationError::UnsupportedImageConfiguration => { - "the requested image configuration is not supported by the physical device" - } - } - ) - } -} - -impl From for SwapchainCreationError { - #[inline] - fn from(err: Error) -> SwapchainCreationError { - match err { - err @ Error::OutOfHostMemory => SwapchainCreationError::OomError(OomError::from(err)), - err @ Error::OutOfDeviceMemory => SwapchainCreationError::OomError(OomError::from(err)), - Error::DeviceLost => SwapchainCreationError::DeviceLost, - Error::SurfaceLost => SwapchainCreationError::SurfaceLost, - Error::NativeWindowInUse => SwapchainCreationError::NativeWindowInUse, - _ => panic!("unexpected error: {:?}", err), - } - } -} - -impl From for SwapchainCreationError { - #[inline] - fn from(err: OomError) -> SwapchainCreationError { - SwapchainCreationError::OomError(err) - } -} - -impl From for SwapchainCreationError { - #[inline] - fn from(err: CapabilitiesError) -> SwapchainCreationError { - match err { - CapabilitiesError::OomError(err) => SwapchainCreationError::OomError(err), - CapabilitiesError::SurfaceLost => SwapchainCreationError::SurfaceLost, - } - } -} - /// Represents the moment when the GPU will have access to a swapchain image. #[must_use] pub struct SwapchainAcquireFuture { @@ -1250,13 +1602,6 @@ unsafe impl GpuFuture for SwapchainAcquireFuture { } } -unsafe impl DeviceOwned for SwapchainAcquireFuture { - #[inline] - fn device(&self) -> &Arc { - &self.swapchain.device - } -} - impl Drop for SwapchainAcquireFuture { fn drop(&mut self) { if let Some(ref fence) = self.fence { @@ -1269,83 +1614,10 @@ impl Drop for SwapchainAcquireFuture { } } -/// Error that can happen when calling `Swapchain::acquire_fullscreen_exclusive` or `Swapchain::release_fullscreen_exclusive` -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[repr(u32)] -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, - - /// Fullscreen exclusivity is already acquired. - DoubleAcquire, - - /// Fullscreen exclusivity is not current acquired. - DoubleRelease, - - /// Swapchain is not in fullscreen exclusive app controlled mode - NotAppControlled, -} - -impl From for FullscreenExclusiveError { +unsafe impl DeviceOwned for SwapchainAcquireFuture { #[inline] - fn from(err: Error) -> FullscreenExclusiveError { - match err { - err @ Error::OutOfHostMemory => FullscreenExclusiveError::OomError(OomError::from(err)), - err @ Error::OutOfDeviceMemory => { - FullscreenExclusiveError::OomError(OomError::from(err)) - } - Error::SurfaceLost => FullscreenExclusiveError::SurfaceLost, - Error::InitializationFailed => FullscreenExclusiveError::InitializationFailed, - _ => panic!("unexpected error: {:?}", err), - } - } -} - -impl From for FullscreenExclusiveError { - #[inline] - fn from(err: OomError) -> FullscreenExclusiveError { - FullscreenExclusiveError::OomError(err) - } -} - -impl error::Error for FullscreenExclusiveError { - #[inline] - fn source(&self) -> Option<&(dyn error::Error + 'static)> { - match *self { - FullscreenExclusiveError::OomError(ref err) => Some(err), - _ => None, - } - } -} - -impl fmt::Display for FullscreenExclusiveError { - #[inline] - fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!( - fmt, - "{}", - 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 => - "fullscreen exclusivity is already acquired", - FullscreenExclusiveError::DoubleRelease => "fullscreen exclusivity is not acquired", - FullscreenExclusiveError::NotAppControlled => { - "swapchain is not in fullscreen exclusive app controlled mode" - } - } - ) + fn device(&self) -> &Arc { + &self.swapchain.device } } @@ -1365,9 +1637,9 @@ pub enum AcquireError { /// The surface is no longer accessible and must be recreated. SurfaceLost, - /// The swapchain has lost or doesn't have fullscreen exclusivity possibly for + /// The swapchain has lost or doesn't have full-screen exclusivity possibly for /// implementation-specific reasons outside of the application’s control. - FullscreenExclusiveLost, + FullScreenExclusiveLost, /// 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. @@ -1399,8 +1671,8 @@ impl fmt::Display for AcquireError { 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::FullscreenExclusiveLost => { - "the swapchain no longer has fullscreen exclusivity" + AcquireError::FullScreenExclusiveLost => { + "the swapchain no longer has full-screen exclusivity" } AcquireError::SemaphoreError(_) => "error creating semaphore", } @@ -1430,7 +1702,7 @@ impl From for AcquireError { Error::DeviceLost => AcquireError::DeviceLost, Error::SurfaceLost => AcquireError::SurfaceLost, Error::OutOfDate => AcquireError::OutOfDate, - Error::FullscreenExclusiveLost => AcquireError::FullscreenExclusiveLost, + Error::FullScreenExclusiveLost => AcquireError::FullScreenExclusiveLost, _ => panic!("unexpected error: {:?}", err), } } @@ -1554,9 +1826,9 @@ where let build_submission_result = self.build_submission(); - if let &Err(FlushError::FullscreenExclusiveLost) = &build_submission_result { + if let &Err(FlushError::FullScreenExclusiveLost) = &build_submission_result { self.swapchain - .fullscreen_exclusive_held + .full_screen_exclusive_held .store(false, Ordering::SeqCst); } @@ -1565,9 +1837,9 @@ where SubmitAnyBuilder::QueuePresent(present) => { let present_result = present.submit(&self.queue); - if let &Err(SubmitPresentError::FullscreenExclusiveLost) = &present_result { + if let &Err(SubmitPresentError::FullScreenExclusiveLost) = &present_result { self.swapchain - .fullscreen_exclusive_held + .full_screen_exclusive_held .store(false, Ordering::SeqCst); } @@ -1701,7 +1973,7 @@ pub unsafe fn acquire_next_image_raw( let r = check_errors( fns.khr_swapchain.acquire_next_image_khr( swapchain.device.internal_object(), - swapchain.swapchain, + swapchain.handle, timeout_ns, semaphore .map(|s| s.internal_object()) diff --git a/vulkano/src/sync/future/mod.rs b/vulkano/src/sync/future/mod.rs index 7d23c41a..930f8d96 100644 --- a/vulkano/src/sync/future/mod.rs +++ b/vulkano/src/sync/future/mod.rs @@ -490,9 +490,9 @@ pub enum FlushError { /// surface's new properties and recreate a new swapchain if you want to continue drawing. OutOfDate, - /// The swapchain has lost or doesn't have fullscreen exclusivity possibly for + /// The swapchain has lost or doesn't have full screen exclusivity possibly for /// implementation-specific reasons outside of the application’s control. - FullscreenExclusiveLost, + FullScreenExclusiveLost, /// The flush operation needed to block, but the timeout has elapsed. Timeout, @@ -521,8 +521,8 @@ impl fmt::Display for FlushError { 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::FullscreenExclusiveLost => { - "the swapchain no longer has fullscreen exclusivity" + FlushError::FullScreenExclusiveLost => { + "the swapchain no longer has full screen exclusivity" } FlushError::Timeout => { "the flush operation needed to block, but the timeout has \ @@ -548,7 +548,7 @@ impl From for FlushError { SubmitPresentError::DeviceLost => FlushError::DeviceLost, SubmitPresentError::SurfaceLost => FlushError::SurfaceLost, SubmitPresentError::OutOfDate => FlushError::OutOfDate, - SubmitPresentError::FullscreenExclusiveLost => FlushError::FullscreenExclusiveLost, + SubmitPresentError::FullScreenExclusiveLost => FlushError::FullScreenExclusiveLost, } } }