diff options
Diffstat (limited to 'src/swapchain/mod.rs')
-rw-r--r-- | src/swapchain/mod.rs | 342 |
1 files changed, 342 insertions, 0 deletions
diff --git a/src/swapchain/mod.rs b/src/swapchain/mod.rs new file mode 100644 index 0000000..0434a57 --- /dev/null +++ b/src/swapchain/mod.rs @@ -0,0 +1,342 @@ +// Copyright (c) 2016 The vulkano developers +// Licensed under the Apache License, Version 2.0 +// <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, +// at your option. All files in the project carrying such +// notice may not be copied, modified, or distributed except +// according to those terms. + +//! Link between Vulkan and a window and/or the screen. +//! +//! Before you can draw on the screen or a window, you have to create two objects: +//! +//! - Create a `Surface` object that represents the location where the image will show up (either +//! a window or a monitor). +//! - Create a `Swapchain` that uses that `Surface`. +//! +//! Creating a surface can be done with only an `Instance` object. However creating a swapchain +//! requires a `Device` object. +//! +//! Once you have a swapchain, you can retrieve `Image` objects from it and draw to them just like +//! you would draw on any other image. +//! +//! # Surfaces +//! +//! A surface is an object that represents a location where to render. It can be created from an +//! instance and either a window handle (in a platform-specific way) or a monitor. +//! +//! In order to use surfaces, you will have to enable the `VK_KHR_surface` extension on the +//! instance. See the `instance` module for more information about how to enable extensions. +//! +//! ## Creating a surface from a window +//! +//! There are 5 extensions that each allow you to create a surface from a type of window: +//! +//! - `VK_KHR_xlib_surface` +//! - `VK_KHR_xcb_surface` +//! - `VK_KHR_wayland_surface` +//! - `VK_KHR_android_surface` +//! - `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`. +//! 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 +//! error. +//! +//! **Note that the `Surface` object is potentially unsafe**. It is your responsibility to +//! keep the window alive for at least as long as the surface exists. In many cases Surface +//! may be able to do this for you, if you pass it ownership of your Window (or a +//! reference-counting container for it). +//! +//! ### Example +//! +//! ```no_run +//! use std::ptr; +//! use vulkano::instance::Instance; +//! use vulkano::instance::InstanceExtensions; +//! use vulkano::swapchain::Surface; +//! use vulkano::Version; +//! +//! let instance = { +//! let extensions = InstanceExtensions { +//! khr_surface: true, +//! khr_win32_surface: true, // If you don't enable this, `from_hwnd` will fail. +//! .. InstanceExtensions::none() +//! }; +//! +//! match Instance::new(None, Version::V1_1, &extensions, None) { +//! Ok(i) => i, +//! Err(err) => panic!("Couldn't build instance: {:?}", err) +//! } +//! }; +//! +//! # use std::sync::Arc; +//! # struct Window(*const u32); +//! # impl Window { +//! # fn hwnd(&self) -> *const u32 { self.0 } +//! # } +//! # +//! # fn build_window() -> Arc<Window> { Arc::new(Window(ptr::null())) } +//! 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() +//! }; +//! ``` +//! +//! ## Creating a surface from a monitor +//! +//! Currently no system provides the `VK_KHR_display` extension that contains this feature. +//! This feature is still a work-in-progress in vulkano and will reside in the `display` module. +//! +//! # Swapchains +//! +//! A surface represents a location on the screen and can be created from an instance. Once you +//! have a surface, the next step is to create a swapchain. Creating a swapchain requires a device, +//! and allocates the resources that will be used to display images on the screen. +//! +//! A swapchain is composed of one or multiple images. Each image of the swapchain is presented in +//! turn on the screen, one after another. More information below. +//! +//! Swapchains have several properties: +//! +//! - The number of images that will cycle on the screen. +//! - The format of the images. +//! - The 2D dimensions of the images, plus a number of layers, for a total of three dimensions. +//! - The usage of the images, similar to creating other images. +//! - The queue families that are going to use the images, similar to creating other images. +//! - An additional transformation (rotation or mirroring) to perform on the final output. +//! - 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()]`](struct.Surface.html#method.capabilities). +//! +//! ## Creating a swapchain +//! +//! In order to create a swapchain, you will first have to enable the `VK_KHR_swapchain` extension +//! on the device (and not on the instance like `VK_KHR_surface`): +//! +//! ```no_run +//! # use vulkano::device::DeviceExtensions; +//! let ext = DeviceExtensions { +//! khr_swapchain: true, +//! .. DeviceExtensions::none() +//! }; +//! ``` +//! +//! Then, query the capabilities of the surface with +//! [`Surface::capabilities()`](struct.Surface.html#method.capabilities) +//! and choose which values you are going to use. +//! +//! ```no_run +//! # use std::sync::Arc; +//! # use vulkano::device::Device; +//! # use vulkano::swapchain::Surface; +//! # use std::cmp::{max, min}; +//! # fn choose_caps(device: Arc<Device>, surface: Arc<Surface<()>>) -> Result<(), Box<std::error::Error>> { +//! let caps = surface.capabilities(device.physical_device())?; +//! +//! // Use the current window size or some fixed resolution. +//! let dimensions = caps.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) +//! }; +//! +//! // Preserve the current surface transform. +//! let transform = caps.current_transform; +//! +//! // Use the first available format. +//! let (format, color_space) = caps.supported_formats[0]; +//! # Ok(()) +//! # } +//! ``` +//! +//! Then, call [`Swapchain::new()`](struct.Swapchain.html#method.new). +//! +//! ```no_run +//! # use std::sync::Arc; +//! # use vulkano::device::{Device, Queue}; +//! # use vulkano::image::ImageUsage; +//! # use vulkano::sync::SharingMode; +//! # use vulkano::format::Format; +//! # use vulkano::swapchain::{Surface, Swapchain, SurfaceTransform, PresentMode, CompositeAlpha, ColorSpace, FullscreenExclusive}; +//! # fn create_swapchain( +//! # device: Arc<Device>, surface: Arc<Surface<()>>, present_queue: Arc<Queue>, +//! # buffers_count: u32, format: Format, dimensions: [u32; 2], +//! # surface_transform: SurfaceTransform, composite_alpha: CompositeAlpha, +//! # present_mode: PresentMode, fullscreen_exclusive: FullscreenExclusive +//! # ) -> Result<(), Box<dyn std::error::Error>> { +//! // The created swapchain will be used as a color attachment for rendering. +//! let usage = ImageUsage { +//! color_attachment: true, +//! .. ImageUsage::none() +//! }; +//! +//! // Create the swapchain and its buffers. +//! let (swapchain, buffers) = Swapchain::start( +//! // 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()?; +//! +//! # Ok(()) +//! # } +//! ``` +//! +//! Creating a swapchain not only returns the swapchain object, but also all the images that belong +//! to it. +//! +//! ## Acquiring and presenting images +//! +//! Once you created a swapchain and retrieved all the images that belong to it (see previous +//! section), you can draw on it. This is done in three steps: +//! +//! - Call `swapchain::acquire_next_image`. This function will return the index of the image +//! (within the list returned by `Swapchain::new`) that is available to draw, plus a future +//! representing the moment when the GPU will gain access to that image. +//! - Draw on that image just like you would draw to any other image (see the documentation of +//! the `pipeline` module). You need to chain the draw after the future that was returned by +//! `acquire_next_image`. +//! - Call `Swapchain::present` with the same index and by chaining the futures, in order to tell +//! the implementation that you are finished drawing to the image and that it can queue a +//! command to present the image on the screen after the draw operations are finished. +//! +//! ``` +//! use vulkano::swapchain; +//! use vulkano::sync::GpuFuture; +//! # let queue: ::std::sync::Arc<::vulkano::device::Queue> = return; +//! # let mut swapchain: ::std::sync::Arc<swapchain::Swapchain<()>> = return; +//! // let mut (swapchain, images) = Swapchain::new(...); +//! loop { +//! # let mut command_buffer: ::vulkano::command_buffer::PrimaryAutoCommandBuffer<()> = return; +//! let (image_num, suboptimal, acquire_future) +//! = swapchain::acquire_next_image(swapchain.clone(), None).unwrap(); +//! +//! // The command_buffer contains the draw commands that modify the framebuffer +//! // constructed from images[image_num] +//! acquire_future +//! .then_execute(queue.clone(), command_buffer).unwrap() +//! .then_swapchain_present(queue.clone(), swapchain.clone(), image_num) +//! .then_signal_fence_and_flush().unwrap(); +//! } +//! ``` +//! +//! ## Recreating a swapchain +//! +//! In some situations, the swapchain will become invalid by itself. This includes for example when +//! the window is resized (as the images of the swapchain will no longer match the window's) or, +//! on Android, when the application went to the background and goes back to the foreground. +//! +//! In this situation, acquiring a swapchain image or presenting it will return an error. Rendering +//! to an image of that swapchain will not produce any error, but may or may not work. To continue +//! rendering, you will need to *recreate* the swapchain by creating a new swapchain and passing +//! as last parameter the old swapchain. +//! +//! +//! ``` +//! use vulkano::swapchain; +//! use vulkano::swapchain::AcquireError; +//! use vulkano::sync::GpuFuture; +//! +//! // let mut swapchain = Swapchain::new(...); +//! # let mut swapchain: (::std::sync::Arc<::vulkano::swapchain::Swapchain<()>>, _) = 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(); +//! 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; }, +//! Err(err) => panic!("{:?}", err) +//! }; +//! +//! // ... +//! +//! let final_future = acq_future +//! // .then_execute(...) +//! .then_swapchain_present(queue.clone(), swapchain.clone(), index) +//! .then_signal_fence_and_flush().unwrap(); // TODO: PresentError? +//! +//! if suboptimal { +//! recreate_swapchain = true; +//! } +//! } +//! ``` +//! + +use std::sync::atomic::AtomicBool; + +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::SupportedCompositeAlphaIter; +pub use self::capabilities::SupportedPresentModes; +pub use self::capabilities::SupportedPresentModesIter; +pub use self::capabilities::SupportedSurfaceTransforms; +pub use self::capabilities::SupportedSurfaceTransformsIter; +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::Surface; +pub use self::surface::SurfaceCreationError; +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::PresentFuture; +pub use self::swapchain::Swapchain; +pub use self::swapchain::SwapchainAcquireFuture; +pub use self::swapchain::SwapchainBuilder; +pub use self::swapchain::SwapchainCreationError; + +mod capabilities; +pub mod display; +mod present_region; +mod surface; +mod swapchain; + +/// Internal trait so that creating/destroying a swapchain can access the surface's "has_swapchain" +/// flag. +// TODO: use pub(crate) maybe? +unsafe trait SurfaceSwapchainLock { + fn flag(&self) -> &AtomicBool; +} |