blob: 0434a57664db7f647298fba9a1094abe54d4f59f [file] [log] [blame]
// 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;
}