Vulkan (gfx-hal) 實現 OpenGL / ES Framebuffer

文檔列表見:Rust 移動端跨平臺複雜圖形渲染項目開發系列總結(目錄)html

上次修改:2019.6.4 刪除CommandBufferSubmission等無關內容。android

Vulkan(gfx-hal) 接口模擬實現 OpenGL / ES Framebuffer 功能的過程比較複雜,涉及多個組件:SurfaceSwapchainRenderPassFramebufferImageView。其中,SurfaceSwapchain只用於實現EGL提供的 System Framebuffer,即 Default Framebuffer,對於用戶建立(User defined)的 Framebuffer是非必需的。app

Surface

/// A `Surface` abstracts the surface of a native window, which will be presented
/// on the display.
pub trait Surface<B: Backend>: fmt::Debug + Any + Send + Sync {
    /// Retrieve the surface image kind.
    fn kind(&self) -> image::Kind;

    /// Check if the queue family supports presentation to this surface.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// # surface.supports_queue_family(family)
    /// ```
    fn supports_queue_family(&self, family: &B::QueueFamily) -> bool;

    /// Query surface capabilities, formats, and present modes for this physical device.
    ///
    /// Use this function for configuring swapchain creation.
    ///
    /// Returns a tuple of surface capabilities and formats.
    /// If formats is `None` than the surface has no preferred format and the
    /// application may use any desired format.
    fn compatibility(
        &self,
        physical_device: &B::PhysicalDevice,
    ) -> (SurfaceCapabilities, Option<Vec<Format>>, Vec<PresentMode>);
}
複製代碼

函數compatibility(...) -> (SurfaceCapabilities...)從返回值看是歧義的,所以我提議重命名以便於理解,如下是討論記錄。ide

Michael(LAI) @Michael-Lfx: Should we rename Surface::compatibility() to Surface::capabilities()?函數

Dzmitry Malyshau @kvark: the idea was that it's a compatibility between the surface and the physical device... I think we should just split the function into multiple smaller ones insteadpost

(as suggested by someone before)ui

Surface建立流程

Vulkan定義Surface爲VkSurfaceKHR,從KHR後綴可知它是平臺相關的,所以gfx-hal讓具體backend提供建立Surface實例的接口,好比instance.create_surface_android(...),若是開發跨平臺應用,可以使用統一接口instance.create_surface(&window);,此接口須要winit::Windowthis

第三方winit庫提供了管理macOS/Android/Windows等平臺的窗口建立、事件響應等,功能相似SDL。idea

SurfaceCapabilities

/// Describes information about what a `Surface`'s properties are.
/// Fetch this with `surface.compatibility(device)`.
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct SurfaceCapabilities {
    /// Number of presentable images supported by the adapter for a swapchain
    /// created from this surface.
    ///
    /// - `image_count.start` must be at least 1.
    /// - `image_count.end` must be larger of equal to `image_count.start`.
    pub image_count: Range<SwapImageIndex>,

    /// Current extent of the surface.
    ///
    /// `None` if the surface has no explicit size, depending on the swapchain extent.
    pub current_extent: Option<Extent2D>,

    /// Range of supported extents.
    ///
    /// `current_extent` must be inside this range.
    pub extents: Range<Extent2D>,

    /// Maximum number of layers supported for presentable images.
    ///
    /// Must be at least 1.
    pub max_image_layers: image::Layer,

    /// Supported image usage flags.
    pub usage: image::Usage,

    /// A bitmask of supported alpha composition modes.
    pub composite_alpha: CompositeAlpha,
}
複製代碼

PresentMode

/// Specifies the mode regulating how a swapchain presents frames.
#[repr(C)]
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub enum PresentMode {
    /// Don't ever wait for v-sync.
    Immediate = 0,
    /// Wait for v-sync, overwrite the last rendered frame.
    Mailbox = 1,
    /// Present frames in the same order they are rendered.
    Fifo = 2,
    /// Don't wait for the next v-sync if we just missed it.
    Relaxed = 3,
}
複製代碼

Swapchain

/// The `Swapchain` is the backend representation of the surface.
/// It consists of multiple buffers, which will be presented on the surface.
pub trait Swapchain<B: Backend>: fmt::Debug + Any + Send + Sync {
    /// Acquire a new swapchain image for rendering. This needs to be called before presenting.
    ///
    /// May fail according to one of the reasons indicated in `AcquireError` enum.
    ///
    /// # Synchronization
    ///
    /// The acquired image will not be immediately available when the function returns.
    /// Once available the provided [`Semaphore`](../trait.Resources.html#associatedtype.Semaphore)
    /// and [`Fence`](../trait.Resources.html#associatedtype.Fence) will be signaled.
    ///
    /// # Examples
    ///
    /// ```no_run
    ///
    /// ```
    unsafe fn acquire_image(
        &mut self,
        timeout_ns: u64,
        semaphore: Option<&B::Semaphore>,
        fence: Option<&B::Fence>,
    ) -> Result<(SwapImageIndex, Option<Suboptimal>), AcquireError>;

    /// Present one acquired image.
    ///
    /// # Safety
    ///
    /// The passed queue _must_ support presentation on the surface, which is
    /// used for creating this swapchain.
    ///
    /// # Examples
    ///
    /// ```no_run
    ///
    /// ```
    unsafe fn present<'a, C, S, Iw>(
        &'a self,
        present_queue: &mut CommandQueue<B, C>,
        image_index: SwapImageIndex,
        wait_semaphores: Iw,
    ) -> Result<Option<Suboptimal>, PresentError>
    where
        Self: 'a + Sized + Borrow<B::Swapchain>,
        C: Capability,
        S: 'a + Borrow<B::Semaphore>,
        Iw: IntoIterator<Item = &'a S>,
    {
        present_queue.present(iter::once((self, image_index)), wait_semaphores)
    }

    /// Present one acquired image without any semaphore synchronization.
    unsafe fn present_without_semaphores<C>(
        &self,
        present_queue: &mut CommandQueue<B, C>,
        image_index: SwapImageIndex,
    ) -> Result<Option<Suboptimal>, PresentError>
    where
        Self: Sized + Borrow<B::Swapchain>,
        C: Capability,
    {
        self.present::<_, B::Semaphore, _>(present_queue, image_index, iter::empty())
    }
}
複製代碼

Swapchain支持兩個操做,先acquire_imagepresentpresent_without_semaphores提供了便捷操做。spa

Backbuffer

/// Swapchain backbuffer type
#[derive(Debug)]
pub enum Backbuffer<B: Backend> {
    /// Color image chain
    Images(Vec<B::Image>),
    /// A single opaque framebuffer
    Framebuffer(B::Framebuffer),
}
複製代碼

Images用於Metal、Vulkan等非OpenGL圖形庫,Framebuffer只爲兼容OpenGL而存在。這種劃分在設計上顯示有些分裂,最近gfx-hal會統一這一行爲。

建立Swapchain時獲得當前底層支持的Backbuffer類型,Vukan同一時期的API將建立ImageViewFramebuffer,OpenGL則直接返回Framebuffer

let (mut swapchain, mut backbuffer) =
    unsafe { device.create_swapchain(&mut surface, swapchain_config, Some(old_swapchain)) }
        .expect("Can't create swapchain");
        
let (mut frame_images, mut framebuffers) = match backbuffer {
    Backbuffer::Images(images) => {
        let pairs = images
            .into_iter()
            .map(|image| unsafe {
                let rtv = device
                    .create_image_view(
                        &image,
                        i::ViewKind::D2,
                        format,
                        Swizzle::NO,
                        Extent {
                            width: swapchain.extent.width as _,
                            height: swapchain.extent.height as _,
                            depth: 1,
                        },
                    )
                    .unwrap();
                (image, rtv)
            })
            .collect::<Vec<_>>();
        let fbos = pairs
            .iter()
            .map(|&(_, ref rtv)| unsafe {
                device
                    .create_framebuffer(&render_pass, Some(rtv), extent)
                    .unwrap()
            })
            .collect();
        (pairs, fbos)
    }
    Backbuffer::Framebuffer(fbo) => (Vec::new(), vec![fbo]),
};
複製代碼

Suboptimal

/// Marker value returned if the swapchain no longer matches the surface properties exactly,
/// but can still be used to present to the surface successfully.
pub struct Suboptimal;
複製代碼

什麼狀況下會出現Suboptimal

好比,當拖到窗口從非HDR到HDR顯示器時出現Suboptimal,此時可處理這個錯誤,也可不處理。不處理則發揮不了HDR的色彩優點。

SwapchainConfig

/// Contains all the data necessary to create a new `Swapchain`:
/// color, depth, and number of images.
///
/// # Examples
///
/// This type implements the builder pattern, method calls can be
/// easily chained.
///
/// ```no_run
/// # extern crate gfx_hal;
/// # fn main() {
/// # use gfx_hal::{SwapchainConfig};
/// # use gfx_hal::format::Format;
/// let config = SwapchainConfig::new(100, 100, Format::Bgra8Unorm, 2);
/// # }
/// ```
#[derive(Debug, Clone)]
pub struct SwapchainConfig {
    /// Presentation mode.
    pub present_mode: PresentMode,
    /// Alpha composition mode.
    pub composite_alpha: CompositeAlpha,
    /// Format of the backbuffer images.
    pub format: Format,
    /// Requested image extent. Must be in
    /// `SurfaceCapabilities::extents` range.
    pub extent: Extent2D,
    /// Number of images in the swapchain. Must be in
    /// `SurfaceCapabilities::image_count` range.
    pub image_count: SwapImageIndex,
    /// Number of image layers. Must be lower or equal to
    /// `SurfaceCapabilities::max_image_layers`.
    pub image_layers: image::Layer,
    /// Image usage of the backbuffer images.
    pub image_usage: image::Usage,
}
複製代碼

SwapchainConfig初始化流程以下所示:

let (caps, formats, present_modes) = surface.compatibility(&physical_device);
println!("formats: {:?}", formats);
let format = formats
    .map_or(format::Format::Rgba8Srgb, |formats| {
        formats
            .iter()
            .find(|format| format.base_format().1 == ChannelType::Srgb)
            .map(|format| *format)
            .unwrap_or(formats[0])
    });

println!("Surface format: {:?}", format);
let swap_config = SwapchainConfig::from_caps(&caps, format);
複製代碼

RenderPass

A render pass represents a collection of attachments, subpasses, and dependencies between the subpasses, and describes how the attachments are used over the course of the subpasses. The use of a render pass in a command buffer is a render pass instance.

www.khronos.org/registry/vu…

RenderPass包含AttachmentSubpassDescSubpassDependency。RTT場景因爲渲染到ImageAttachment::format得用輸出紋理Image生成的ImageViewformat值。

初始化RenderPass

根據前面可知,建立RenderPass須要先建立它的子組件,下面逐次描述。

  1. 建立Attachment

    let attachment = pass::Attachment {
        format: Some(image.format),
        samples: 1,
        ops: pass::AttachmentOps::new(
            pass::AttachmentLoadOp::Clear,
            pass::AttachmentStoreOp::Store,
        ),
        stencil_ops: pass::AttachmentOps::DONT_CARE,
        layouts: image::Layout::Undefined..image::Layout::Present,
    };
    複製代碼

    值得注意的是,渲染到View和RTT須要配置不一樣的layouts值。同理,AttachmentOps也要根據Blend等操做進行相應配置。若是支持Depth/Stencil,則須要建立新的Attachment,即須要兩個Attachment分別表示顏色與深度數據。

  2. 建立SubpassDesc

    let subpass = pass::SubpassDesc {
        colors: &[(0, image::Layout::ColorAttachmentOptimal)],
        depth_stencil: None,
        inputs: &[],
        resolves: &[],
        preserves: &[],
    };
    複製代碼
  3. 建立SubpassDependency

    let dependency = pass::SubpassDependency {
        passes: pass::SubpassRef::External..pass::SubpassRef::Pass(0),
        stages: PipelineStage::COLOR_ATTACHMENT_OUTPUT
            ..PipelineStage::COLOR_ATTACHMENT_OUTPUT,
        accesses: image::Access::empty()
            ..(image::Access::COLOR_ATTACHMENT_READ | image::Access::COLOR_ATTACHMENT_WRITE),
    };
    複製代碼
  4. 建立RenderPass 前面組件就緒後,可從Device建立一個RenderPass

    let render_pass = device.create_render_pass(&[attachment], &[subpass], &[dependency]);
    複製代碼
相關文章
相關標籤/搜索