文檔列表見:Rust 移動端跨平臺複雜圖形渲染項目開發系列總結(目錄)html
上次修改:2019.6.4 刪除
CommandBuffer
、Submission
等無關內容。android
Vulkan(gfx-hal) 接口模擬實現 OpenGL / ES Framebuffer 功能的過程比較複雜,涉及多個組件:Surface
、Swapchain
、RenderPass
、Framebuffer
、ImageView
。其中,Surface
、Swapchain
只用於實現EGL提供的 System Framebuffer,即 Default Framebuffer,對於用戶建立(User defined)的 Framebuffer是非必需的。app
/// 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()
toSurface::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
Vulkan
定義Surface爲VkSurfaceKHR
,從KHR
後綴可知它是平臺相關的,所以gfx-hal讓具體backend提供建立Surface
實例的接口,好比instance.create_surface_android(...)
,若是開發跨平臺應用,可以使用統一接口instance.create_surface(&window);
,此接口須要winit::Window
。this
第三方winit
庫提供了管理macOS/Android/Windows等平臺的窗口建立、事件響應等,功能相似SDL。idea
/// 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,
}
複製代碼
/// 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,
}
複製代碼
/// 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_image
再present
。present_without_semaphores
提供了便捷操做。spa
/// 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將建立ImageView
和Framebuffer
,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]),
};
複製代碼
/// 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的色彩優點。
/// 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);
複製代碼
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.
RenderPass
包含Attachment
、SubpassDesc
和SubpassDependency
。RTT場景因爲渲染到Image
,Attachment::format
得用輸出紋理Image
生成的ImageView
的format
值。
根據前面可知,建立RenderPass須要先建立它的子組件,下面逐次描述。
建立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
分別表示顏色與深度數據。
建立SubpassDesc
let subpass = pass::SubpassDesc {
colors: &[(0, image::Layout::ColorAttachmentOptimal)],
depth_stencil: None,
inputs: &[],
resolves: &[],
preserves: &[],
};
複製代碼
建立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),
};
複製代碼
建立RenderPass 前面組件就緒後,可從Device
建立一個RenderPass
。
let render_pass = device.create_render_pass(&[attachment], &[subpass], &[dependency]);
複製代碼