From 2aa4a82499d4becd2284cdb482213d541b8804dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 28 Apr 2024 16:29:10 +0200 Subject: Adding upstream version 86.0.1. Signed-off-by: Daniel Baumann --- third_party/rust/gfx-hal/.cargo-checksum.json | 1 + third_party/rust/gfx-hal/Cargo.toml | 28 + third_party/rust/gfx-hal/src/adapter.rs | 179 ++++ third_party/rust/gfx-hal/src/buffer.rs | 192 ++++ third_party/rust/gfx-hal/src/command/clear.rs | 73 ++ third_party/rust/gfx-hal/src/command/mod.rs | 645 +++++++++++++ third_party/rust/gfx-hal/src/command/structs.rs | 86 ++ third_party/rust/gfx-hal/src/device.rs | 997 +++++++++++++++++++++ third_party/rust/gfx-hal/src/format.rs | 625 +++++++++++++ third_party/rust/gfx-hal/src/image.rs | 753 ++++++++++++++++ third_party/rust/gfx-hal/src/lib.rs | 643 +++++++++++++ third_party/rust/gfx-hal/src/memory.rs | 130 +++ third_party/rust/gfx-hal/src/pass.rs | 197 ++++ third_party/rust/gfx-hal/src/pool.rs | 65 ++ third_party/rust/gfx-hal/src/pso/compute.rs | 31 + third_party/rust/gfx-hal/src/pso/descriptor.rs | 316 +++++++ third_party/rust/gfx-hal/src/pso/graphics.rs | 298 ++++++ .../rust/gfx-hal/src/pso/input_assembler.rs | 145 +++ third_party/rust/gfx-hal/src/pso/mod.rs | 220 +++++ third_party/rust/gfx-hal/src/pso/output_merger.rs | 359 ++++++++ third_party/rust/gfx-hal/src/pso/specialization.rs | 134 +++ third_party/rust/gfx-hal/src/query.rs | 119 +++ third_party/rust/gfx-hal/src/queue/family.rs | 55 ++ third_party/rust/gfx-hal/src/queue/mod.rs | 139 +++ third_party/rust/gfx-hal/src/window.rs | 563 ++++++++++++ 25 files changed, 6993 insertions(+) create mode 100644 third_party/rust/gfx-hal/.cargo-checksum.json create mode 100644 third_party/rust/gfx-hal/Cargo.toml create mode 100644 third_party/rust/gfx-hal/src/adapter.rs create mode 100644 third_party/rust/gfx-hal/src/buffer.rs create mode 100644 third_party/rust/gfx-hal/src/command/clear.rs create mode 100644 third_party/rust/gfx-hal/src/command/mod.rs create mode 100644 third_party/rust/gfx-hal/src/command/structs.rs create mode 100644 third_party/rust/gfx-hal/src/device.rs create mode 100644 third_party/rust/gfx-hal/src/format.rs create mode 100644 third_party/rust/gfx-hal/src/image.rs create mode 100644 third_party/rust/gfx-hal/src/lib.rs create mode 100644 third_party/rust/gfx-hal/src/memory.rs create mode 100644 third_party/rust/gfx-hal/src/pass.rs create mode 100644 third_party/rust/gfx-hal/src/pool.rs create mode 100644 third_party/rust/gfx-hal/src/pso/compute.rs create mode 100644 third_party/rust/gfx-hal/src/pso/descriptor.rs create mode 100644 third_party/rust/gfx-hal/src/pso/graphics.rs create mode 100644 third_party/rust/gfx-hal/src/pso/input_assembler.rs create mode 100644 third_party/rust/gfx-hal/src/pso/mod.rs create mode 100644 third_party/rust/gfx-hal/src/pso/output_merger.rs create mode 100644 third_party/rust/gfx-hal/src/pso/specialization.rs create mode 100644 third_party/rust/gfx-hal/src/query.rs create mode 100644 third_party/rust/gfx-hal/src/queue/family.rs create mode 100644 third_party/rust/gfx-hal/src/queue/mod.rs create mode 100644 third_party/rust/gfx-hal/src/window.rs (limited to 'third_party/rust/gfx-hal') diff --git a/third_party/rust/gfx-hal/.cargo-checksum.json b/third_party/rust/gfx-hal/.cargo-checksum.json new file mode 100644 index 0000000000..b1f24c056b --- /dev/null +++ b/third_party/rust/gfx-hal/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.toml":"7e23678de266bc2fbbd13526cc72b2798889a9f947f6341a3361f64ed1ac3444","src/adapter.rs":"cb59da51cba0d8c4fe9921d9343426361b2360373b0a67a00f8fc66291a28650","src/buffer.rs":"483730967af6860e1f0704ab95e83306d19a6bb3684a2b08c6bde86bb3c77ee9","src/command/clear.rs":"c1105fc5fa3ffdf2a77226e8451a7237b6c7583e5e83120f99692a304a355a3d","src/command/mod.rs":"61aa276c165c0b4273bcb47569e6e228e077455016101247a25fa751ac8f8a60","src/command/structs.rs":"00b5850540ae21227c6578866e86cc741d074601239f8b1bbd0342a5e5f74623","src/device.rs":"66a5d7ef2953b8c72d31cc3c29852f395780693de94897835e41313dcd3c6187","src/format.rs":"43411bb348d24847a51c4f404110865eee9d9d68c9bc9c61bab6c6b0f92e5bee","src/image.rs":"c3a8be75ef64874860d8c8924b2f35c4abd3b51035459daab13ece375ad27a8d","src/lib.rs":"40b8b104fc796fffb1e5ed115825b58666c5d2a17e69b9e97e20687b9faaea35","src/memory.rs":"cb6af7749c0a9d9b33c77dcefe9bb424281112145cc302decec7ded729c5adb4","src/pass.rs":"10e60b840ceca18d1a37003653d14e47c1d9e31948dab554f933c1778e46639f","src/pool.rs":"59357105050616b833d2dd23e31e67b2f89da52c593a833be20e820cfecfbcce","src/pso/compute.rs":"fb9a5748c3b9174924db13c1c59388bcd75279ff6d40d1a068fb52e70e5ccb94","src/pso/descriptor.rs":"0c780f1a42ba85624e0cd09fcbd1d4ca9962e3df7dde822bd412c4778706199f","src/pso/graphics.rs":"989c02c95181d704c092dcfc9135c9fcc2ab7592d186f928c65116168fd487d4","src/pso/input_assembler.rs":"0f75dcbc26e253a3ad531089b969e25b9cc86ec9c938f056a62e82ad36383ccb","src/pso/mod.rs":"35573da503ba7168c98ded648209bd1f707338f48a0f2fbb194739b832fa6c8f","src/pso/output_merger.rs":"cfd3b9ba8b8ba85c497e8bdb9f594a1bd83b1c12b99f40c833e1be4ceadace80","src/pso/specialization.rs":"c6335efebcd358250c54c3698e86b2897bcc04e9e1318708b80330f09a0b8521","src/query.rs":"6df341eb2a887c32f9f2b767718550404e684f12b3f8adb64f635999bff1b2e9","src/queue/family.rs":"a769ed5ba31a1cb41c962d17786d00c41cb3e84c4d17e29732416786259c95a9","src/queue/mod.rs":"9cbd2076812339f2111305d93e4d4cdfffa7906ec5c9d2af24c72a0452bd10e7","src/window.rs":"0921a59aeef918e04f12ca07e7660d81b0c21407aaa3e986f3a0a197ac01bf72"},"package":null} \ No newline at end of file diff --git a/third_party/rust/gfx-hal/Cargo.toml b/third_party/rust/gfx-hal/Cargo.toml new file mode 100644 index 0000000000..bce6f9760b --- /dev/null +++ b/third_party/rust/gfx-hal/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "gfx-hal" +version = "0.6.0" +description = "gfx-rs hardware abstraction layer" +homepage = "https://github.com/gfx-rs/gfx" +repository = "https://github.com/gfx-rs/gfx" +keywords = ["graphics"] +license = "MIT OR Apache-2.0" +authors = ["The Gfx-rs Developers"] +documentation = "https://docs.rs/gfx-hal" +workspace = "../.." +edition = "2018" + +[features] +unstable = [] + +[lib] +name = "gfx_hal" +path = "src/lib.rs" + +[dependencies] +bitflags = "1.0" +mint = { version = "0.5", optional = true } +raw-window-handle = "0.3" +serde = { version = "1", features = ["serde_derive"], optional = true } + +[dev-dependencies] +gfx-backend-empty = { path = "../backend/empty", version = "0.6" } diff --git a/third_party/rust/gfx-hal/src/adapter.rs b/third_party/rust/gfx-hal/src/adapter.rs new file mode 100644 index 0000000000..3674a058dc --- /dev/null +++ b/third_party/rust/gfx-hal/src/adapter.rs @@ -0,0 +1,179 @@ +//! Physical graphics devices. +//! +//! The [`PhysicalDevice`][PhysicalDevice] trait specifies the API a backend +//! must provide for dealing with and querying a physical device, such as +//! a particular GPU. +//! +//! An [adapter][Adapter] is a struct containing a physical device and metadata +//! for a particular GPU, generally created from an [instance][crate::Instance] +//! of that [backend][crate::Backend]. + +use std::{any::Any, fmt}; + +use crate::{ + device, format, image, memory, + queue::{QueueGroup, QueuePriority}, + Backend, Features, Hints, Limits, +}; + +/// A description for a single type of memory in a heap. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct MemoryType { + /// Properties of the associated memory, such as synchronization + /// properties or whether it's on the CPU or GPU. + pub properties: memory::Properties, + /// Index to the underlying memory heap in `Gpu::memory_heaps` + pub heap_index: usize, +} + +/// A description for a memory heap. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct MemoryHeap { + /// Total size of the heap. + pub size: u64, + /// Heap flags. + pub flags: memory::HeapFlags, +} + +/// Types of memory supported by this adapter and available memory. +#[derive(Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct MemoryProperties { + /// Each memory type is associated with one heap of `memory_heaps`. + /// Multiple types can point to the same heap. + pub memory_types: Vec, + /// Memory heaps with their size in bytes. + pub memory_heaps: Vec, +} + +/// Represents a combination of a [logical device][crate::device::Device] and the +/// [hardware queues][QueueGroup] it provides. +/// +/// This structure is typically created from an [adapter][crate::adapter::Adapter]. +#[derive(Debug)] +pub struct Gpu { + /// [Logical device][crate::device::Device] for a given backend. + pub device: B::Device, + /// The [command queues][crate::queue::CommandQueue] that the device provides. + pub queue_groups: Vec>, +} + +/// Represents a physical device (such as a GPU) capable of supporting the given backend. +pub trait PhysicalDevice: fmt::Debug + Any + Send + Sync { + /// Create a new [logical device][crate::device::Device] with the requested features. + /// If `requested_features` is [empty][crate::Features::empty], then only + /// the core features are supported. + /// + /// # Arguments + /// + /// * `families` - which [queue families][crate::queue::family::QueueFamily] + /// to create queues from. The implementation may allocate more processing time to + /// the queues with higher [priority][QueuePriority]. + /// * `requested_features` - device features to enable. Must be a subset of + /// the [features][PhysicalDevice::features] supported by this device. + /// + /// # Errors + /// + /// - Returns `TooManyObjects` if the implementation can't create a new logical device. + /// - Returns `MissingFeature` if the implementation does not support a requested feature. + /// + /// # Examples + /// + /// ```no_run + /// # extern crate gfx_backend_empty as empty; + /// # extern crate gfx_hal; + /// # fn main() { + /// use gfx_hal::{adapter::PhysicalDevice, Features}; + /// + /// # let physical_device: empty::PhysicalDevice = return; + /// # let family: empty::QueueFamily = return; + /// # unsafe { + /// let gpu = physical_device.open(&[(&family, &[1.0; 1])], Features::empty()); + /// # }} + /// ``` + unsafe fn open( + &self, + families: &[(&B::QueueFamily, &[QueuePriority])], + requested_features: Features, + ) -> Result, device::CreationError>; + + /// Fetch details for a particular format. + fn format_properties(&self, format: Option) -> format::Properties; + + /// Fetch details for a particular image format. + fn image_format_properties( + &self, + format: format::Format, + dimensions: u8, + tiling: image::Tiling, + usage: image::Usage, + view_caps: image::ViewCapabilities, + ) -> Option; + + /// Fetch details for the memory regions provided by the device. + fn memory_properties(&self) -> MemoryProperties; + + /// Returns the features of this `PhysicalDevice`. This usually depends on the graphics API being + /// used. + fn features(&self) -> Features; + + /// Returns the performance hints of this `PhysicalDevice`. + fn hints(&self) -> Hints; + + /// Returns the resource limits of this `PhysicalDevice`. + fn limits(&self) -> Limits; + + /// Check cache compatibility with the `PhysicalDevice`. + fn is_valid_cache(&self, _cache: &[u8]) -> bool { + false + } +} + +/// The type of a physical graphics device +#[derive(Clone, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum DeviceType { + /// Other + Other = 0, + /// Integrated + IntegratedGpu = 1, + /// Discrete + DiscreteGpu = 2, + /// Virtual / Hosted + VirtualGpu = 3, + /// CPU / Software Rendering + Cpu = 4, +} + +/// Metadata about a backend [adapter][Adapter]. +#[derive(Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct AdapterInfo { + /// Adapter name + pub name: String, + /// PCI ID of the device vendor + pub vendor: usize, + /// PCI ID of the device + pub device: usize, + /// Type of device + pub device_type: DeviceType, +} + +/// Information about a graphics device, supported by the backend. +/// +/// The list of available adapters is obtained by calling +/// [`Instance::enumerate_adapters`][crate::Instance::enumerate_adapters]. +/// +/// To create a [`Gpu`][Gpu] from this type you can use the [`open`](PhysicalDevice::open) +/// method on its [`physical_device`][Adapter::physical_device] field. +#[derive(Debug)] +pub struct Adapter { + /// General information about this adapter. + pub info: AdapterInfo, + /// Actual [physical device][PhysicalDevice]. + pub physical_device: B::PhysicalDevice, + /// [Queue families][crate::queue::family::QueueFamily] supported by this adapter. + pub queue_families: Vec, +} diff --git a/third_party/rust/gfx-hal/src/buffer.rs b/third_party/rust/gfx-hal/src/buffer.rs new file mode 100644 index 0000000000..a772ae2a9d --- /dev/null +++ b/third_party/rust/gfx-hal/src/buffer.rs @@ -0,0 +1,192 @@ +//! Memory buffers. +//! +//! Buffers interpret memory slices as linear contiguous data array. +//! +//! They can be used as shader resources, vertex buffers, index buffers or for +//! specifying the action commands for indirect execution. + +use crate::{device::OutOfMemory, format::Format}; + +/// An offset inside a buffer, in bytes. +pub type Offset = u64; + +/// A subrange of the buffer. +#[derive(Clone, Debug, Default, Hash, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct SubRange { + /// Offset to the subrange. + pub offset: Offset, + /// Size of the subrange, or None for the remaining size of the buffer. + pub size: Option, +} + +impl SubRange { + /// Whole buffer subrange. + pub const WHOLE: Self = SubRange { + offset: 0, + size: None, + }; + + /// Return the stored size, if present, or computed size based on the limit. + pub fn size_to(&self, limit: Offset) -> Offset { + self.size.unwrap_or(limit - self.offset) + } +} + +/// Buffer state. +pub type State = Access; + +/// Error creating a buffer. +#[derive(Clone, Debug, PartialEq)] +pub enum CreationError { + /// Out of either host or device memory. + OutOfMemory(OutOfMemory), + + /// Requested buffer usage is not supported. + /// + /// Older GL version don't support constant buffers or multiple usage flags. + UnsupportedUsage { + /// Unsupported usage passed on buffer creation. + usage: Usage, + }, +} + +impl From for CreationError { + fn from(error: OutOfMemory) -> Self { + CreationError::OutOfMemory(error) + } +} + +impl std::fmt::Display for CreationError { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + CreationError::OutOfMemory(err) => write!(fmt, "Failed to create buffer: {}", err), + CreationError::UnsupportedUsage { usage } => write!( + fmt, + "Failed to create buffer: Unsupported usage: {:?}", + usage + ), + } + } +} + +impl std::error::Error for CreationError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + CreationError::OutOfMemory(err) => Some(err), + _ => None, + } + } +} + +/// Error creating a buffer view. +#[derive(Clone, Debug, PartialEq)] +pub enum ViewCreationError { + /// Out of either host or device memory. + OutOfMemory(OutOfMemory), + + /// Buffer view format is not supported. + UnsupportedFormat(Option), +} + +impl From for ViewCreationError { + fn from(error: OutOfMemory) -> Self { + ViewCreationError::OutOfMemory(error) + } +} + +impl std::fmt::Display for ViewCreationError { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ViewCreationError::OutOfMemory(err) => { + write!(fmt, "Failed to create buffer view: {}", err) + } + ViewCreationError::UnsupportedFormat(Some(format)) => write!( + fmt, + "Failed to create buffer view: Unsupported format {:?}", + format + ), + ViewCreationError::UnsupportedFormat(None) => { + write!(fmt, "Failed to create buffer view: Unspecified format") + } + } + } +} + +impl std::error::Error for ViewCreationError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + ViewCreationError::OutOfMemory(err) => Some(err), + _ => None, + } + } +} + +bitflags!( + /// Buffer usage flags. + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + pub struct Usage: u32 { + /// + const TRANSFER_SRC = 0x1; + /// + const TRANSFER_DST = 0x2; + /// + const UNIFORM_TEXEL = 0x4; + /// + const STORAGE_TEXEL = 0x8; + /// + const UNIFORM = 0x10; + /// + const STORAGE = 0x20; + /// + const INDEX = 0x40; + /// + const VERTEX = 0x80; + /// + const INDIRECT = 0x100; + } +); + +impl Usage { + /// Returns if the buffer can be used in transfer operations. + pub fn can_transfer(&self) -> bool { + self.intersects(Usage::TRANSFER_SRC | Usage::TRANSFER_DST) + } +} + +bitflags!( + /// Buffer access flags. + /// + /// Access of buffers by the pipeline or shaders. + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + pub struct Access: u32 { + /// Read commands instruction for indirect execution. + const INDIRECT_COMMAND_READ = 0x1; + /// Read index values for indexed draw commands. + /// + /// See [`draw_indexed`](../command/trait.RawCommandBuffer.html#tymethod.draw_indexed) + /// and [`draw_indexed_indirect`](../command/trait.RawCommandBuffer.html#tymethod.draw_indexed_indirect). + const INDEX_BUFFER_READ = 0x2; + /// Read vertices from vertex buffer for draw commands in the [`VERTEX_INPUT`]( + /// ../pso/struct.PipelineStage.html#associatedconstant.VERTEX_INPUT) stage. + const VERTEX_BUFFER_READ = 0x4; + /// + const UNIFORM_READ = 0x8; + /// + const SHADER_READ = 0x20; + /// + const SHADER_WRITE = 0x40; + /// + const TRANSFER_READ = 0x800; + /// + const TRANSFER_WRITE = 0x1000; + /// + const HOST_READ = 0x2000; + /// + const HOST_WRITE = 0x4000; + /// + const MEMORY_READ = 0x8000; + /// + const MEMORY_WRITE = 0x10000; + } +); diff --git a/third_party/rust/gfx-hal/src/command/clear.rs b/third_party/rust/gfx-hal/src/command/clear.rs new file mode 100644 index 0000000000..b620e472de --- /dev/null +++ b/third_party/rust/gfx-hal/src/command/clear.rs @@ -0,0 +1,73 @@ +use crate::pso; +use std::fmt; + +/// A clear color union, which can be either `f32`, `i32`, or `u32`. +#[repr(C)] +#[derive(Clone, Copy)] +pub union ClearColor { + /// `f32` variant + pub float32: [f32; 4], + /// `i32` variant + pub sint32: [i32; 4], + /// `u32` variant + pub uint32: [u32; 4], +} + +impl fmt::Debug for ClearColor { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + writeln![f, "ClearColor"] + } +} + +/// A combination of depth and stencil clear values. +#[repr(C)] +#[derive(Clone, Copy, Debug)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct ClearDepthStencil { + /// Depth value + pub depth: f32, + /// Stencil value + pub stencil: u32, +} + +/// A set of clear values for a single attachment. +/// +/// These are passed to a command buffer by +/// [beginning a render pass][crate::command::CommandBuffer::begin_render_pass]. +#[repr(C)] +#[derive(Clone, Copy)] +pub union ClearValue { + /// Clear color + pub color: ClearColor, + /// Clear depth and stencil + pub depth_stencil: ClearDepthStencil, + _align: [u32; 4], +} + +impl fmt::Debug for ClearValue { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("ClearValue") + .field("color", unsafe { &self.color.uint32 }) + .field("depth_stencil", unsafe { &self.depth_stencil }) + .finish() + } +} + +/// Attachment clear description for the current subpass. +#[derive(Clone, Copy, Debug)] +pub enum AttachmentClear { + /// Clear color attachment. + Color { + /// Index inside the `SubpassDesc::colors` array. + index: usize, + /// Value to clear with. + value: ClearColor, + }, + /// Clear depth-stencil attachment. + DepthStencil { + /// Depth value to clear with. + depth: Option, + /// Stencil value to clear with. + stencil: Option, + }, +} diff --git a/third_party/rust/gfx-hal/src/command/mod.rs b/third_party/rust/gfx-hal/src/command/mod.rs new file mode 100644 index 0000000000..6c90f97e74 --- /dev/null +++ b/third_party/rust/gfx-hal/src/command/mod.rs @@ -0,0 +1,645 @@ +//! Command buffers. +//! +//! A command buffer collects a list of commands to be submitted to the device. +//! +//! Each command buffer has specific capabilities for graphics, compute or transfer operations, +//! and can be either a *primary* command buffer or a *secondary* command buffer. +//! +//! Operations are always initiated by a primary command buffer, +//! but a primary command buffer can contain calls to secondary command buffers +//! that contain snippets of commands that do specific things, similar to function calls. +//! +//! All the possible commands are exposed by the [`CommandBuffer`][CommandBuffer] trait. + +// TODO: Document pipelines and subpasses better. + +mod clear; +mod structs; + +use std::{any::Any, borrow::Borrow, fmt, ops::Range}; + +use crate::{ + buffer, + image::{Filter, Layout, SubresourceRange}, + memory::{Barrier, Dependencies}, + pass, pso, query, Backend, DrawCount, IndexCount, IndexType, InstanceCount, TaskCount, + VertexCount, VertexOffset, WorkGroupCount, +}; + +pub use self::clear::*; +pub use self::structs::*; + +/// Offset for dynamic descriptors. +pub type DescriptorSetOffset = u32; + +bitflags! { + /// Option flags for various command buffer settings. + #[derive(Default)] + pub struct CommandBufferFlags: u32 { + /// Says that the command buffer will be recorded, submitted only once, and then reset and re-filled + /// for another submission. + const ONE_TIME_SUBMIT = 0x1; + + /// If set on a secondary command buffer, it says the command buffer takes place entirely inside + /// a render pass. Ignored on primary command buffer. + const RENDER_PASS_CONTINUE = 0x2; + + /// Says that a command buffer can be recorded into multiple primary command buffers, + /// and submitted to a queue while it is still pending. + const SIMULTANEOUS_USE = 0x4; + } +} + +/// An enum that indicates whether a command buffer is primary or secondary. +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum Level { + /// Can be submitted to a queue for execution, but cannot be called from other + /// command buffers. + Primary, + /// Cannot be submitted directly, but can be called from primary command buffers. + Secondary, +} + +/// Specifies how commands for the following renderpasses will be recorded. +#[derive(Debug)] +pub enum SubpassContents { + /// Contents of the subpass will be inline in the command buffer, + /// NOT in secondary command buffers. + Inline, + /// Contents of the subpass will be in secondary command buffers, and + /// the primary command buffer will only contain `execute_command()` calls + /// until the subpass or render pass is complete. + SecondaryBuffers, +} + +#[allow(missing_docs)] +#[derive(Debug)] +pub struct CommandBufferInheritanceInfo<'a, B: Backend> { + pub subpass: Option>, + pub framebuffer: Option<&'a B::Framebuffer>, + pub occlusion_query_enable: bool, + pub occlusion_query_flags: query::ControlFlags, + pub pipeline_statistics: query::PipelineStatistic, +} + +impl<'a, B: Backend> Default for CommandBufferInheritanceInfo<'a, B> { + fn default() -> Self { + CommandBufferInheritanceInfo { + subpass: None, + framebuffer: None, + occlusion_query_enable: false, + occlusion_query_flags: query::ControlFlags::empty(), + pipeline_statistics: query::PipelineStatistic::empty(), + } + } +} + +/// A trait that describes all the operations that must be +/// provided by a `Backend`'s command buffer. +pub trait CommandBuffer: fmt::Debug + Any + Send + Sync { + /// Begins recording commands to a command buffer. + unsafe fn begin( + &mut self, + flags: CommandBufferFlags, + inheritance_info: CommandBufferInheritanceInfo, + ); + + /// Begins recording a primary command buffer + /// (that has no inheritance information). + unsafe fn begin_primary(&mut self, flags: CommandBufferFlags) { + self.begin(flags, CommandBufferInheritanceInfo::default()); + } + + /// Finish recording commands to a command buffer. + unsafe fn finish(&mut self); + + /// Empties the command buffer, optionally releasing all + /// resources from the commands that have been submitted. + unsafe fn reset(&mut self, release_resources: bool); + + // TODO: This REALLY needs to be deeper, but it's complicated. + // Should probably be a whole book chapter on synchronization and stuff really. + /// Inserts a synchronization dependency between pipeline stages + /// in the command buffer. + unsafe fn pipeline_barrier<'a, T>( + &mut self, + stages: Range, + dependencies: Dependencies, + barriers: T, + ) where + T: IntoIterator, + T::Item: Borrow>; + + /// Fill a buffer with the given `u32` value. + unsafe fn fill_buffer(&mut self, buffer: &B::Buffer, range: buffer::SubRange, data: u32); + + /// Copy data from the given slice into a buffer. + unsafe fn update_buffer(&mut self, buffer: &B::Buffer, offset: buffer::Offset, data: &[u8]); + + /// Clears an image to the given color/depth/stencil. + unsafe fn clear_image( + &mut self, + image: &B::Image, + layout: Layout, + value: ClearValue, + subresource_ranges: T, + ) where + T: IntoIterator, + T::Item: Borrow; + + /// Takes an iterator of attachments and an iterator of rect's, + /// and clears the given rect's for *each* attachment. + unsafe fn clear_attachments(&mut self, clears: T, rects: U) + where + T: IntoIterator, + T::Item: Borrow, + T::IntoIter: ExactSizeIterator, + U: IntoIterator, + U::Item: Borrow, + U::IntoIter: ExactSizeIterator; + + /// "Resolves" a multisampled image, converting it into a non-multisampled + /// image. Takes an iterator of regions to apply the resolution to. + unsafe fn resolve_image( + &mut self, + src: &B::Image, + src_layout: Layout, + dst: &B::Image, + dst_layout: Layout, + regions: T, + ) where + T: IntoIterator, + T::Item: Borrow, + T::IntoIter: ExactSizeIterator; + + /// Copies regions from the source to destination image, + /// applying scaling, filtering and potentially format conversion. + unsafe fn blit_image( + &mut self, + src: &B::Image, + src_layout: Layout, + dst: &B::Image, + dst_layout: Layout, + filter: Filter, + regions: T, + ) where + T: IntoIterator, + T::Item: Borrow, + T::IntoIter: ExactSizeIterator; + + /// Bind the index buffer view, making it the "current" one that draw commands + /// will operate on. + unsafe fn bind_index_buffer( + &mut self, + buffer: &B::Buffer, + sub: buffer::SubRange, + ty: IndexType, + ); + + /// Bind the vertex buffer set, making it the "current" one that draw commands + /// will operate on. + /// + /// Each buffer passed corresponds to the vertex input binding with the same index, + /// starting from an offset index `first_binding`. For example an iterator with + /// two items and `first_binding` of 1 would fill vertex buffer binding numbers + /// 1 and 2. + /// + /// This binding number refers only to binding points for vertex buffers and is + /// completely separate from the binding numbers of `Descriptor`s in `DescriptorSet`s. + /// It needs to match with the `VertexBufferDesc` and `AttributeDesc`s to which the + /// data from each bound vertex buffer should flow. + /// + /// The `buffers` iterator should yield the `Buffer` to bind, as well as a subrange, + /// in bytes, into that buffer where the vertex data that should be bound. + unsafe fn bind_vertex_buffers(&mut self, first_binding: pso::BufferIndex, buffers: I) + where + I: IntoIterator, + T: Borrow, + I::IntoIter: ExactSizeIterator; + + /// Set the [viewport][crate::pso::Viewport] parameters for the rasterizer. + /// + /// Each viewport passed corresponds to the viewport with the same index, + /// starting from an offset index `first_viewport`. + /// + /// # Errors + /// + /// This function does not return an error. Invalid usage of this function + /// will result in undefined behavior. + /// + /// - Command buffer must be in recording state. + /// - Number of viewports must be between 1 and `max_viewports - first_viewport`. + /// - The first viewport must be less than `max_viewports`. + /// - Only queues with graphics capability support this function. + /// - The bound pipeline must not have baked viewport state. + /// - All viewports used by the pipeline must be specified before the first + /// draw call. + unsafe fn set_viewports(&mut self, first_viewport: u32, viewports: T) + where + T: IntoIterator, + T::Item: Borrow, + T::IntoIter: ExactSizeIterator; + + /// Set the scissor rectangles for the rasterizer. + /// + /// Each scissor corresponds to the viewport with the same index, starting + /// from an offset index `first_scissor`. + /// + /// # Errors + /// + /// This function does not return an error. Invalid usage of this function + /// will result in undefined behavior. + /// + /// - Command buffer must be in recording state. + /// - Number of scissors must be between 1 and `max_viewports - first_scissor`. + /// - The first scissor must be less than `max_viewports`. + /// - Only queues with graphics capability support this function. + /// - The bound pipeline must not have baked scissor state. + /// - All scissors used by the pipeline must be specified before the first draw + /// call. + unsafe fn set_scissors(&mut self, first_scissor: u32, rects: T) + where + T: IntoIterator, + T::Item: Borrow, + T::IntoIter: ExactSizeIterator; + + /// Sets the stencil reference value for comparison operations and store operations. + /// Will be used on the LHS of stencil compare ops and as store value when the + /// store op is Reference. + unsafe fn set_stencil_reference(&mut self, faces: pso::Face, value: pso::StencilValue); + + /// Sets the stencil read mask. + unsafe fn set_stencil_read_mask(&mut self, faces: pso::Face, value: pso::StencilValue); + + /// Sets the stencil write mask. + unsafe fn set_stencil_write_mask(&mut self, faces: pso::Face, value: pso::StencilValue); + + /// Set the blend constant values dynamically. + unsafe fn set_blend_constants(&mut self, color: pso::ColorValue); + + /// Set the depth bounds test values dynamically. + unsafe fn set_depth_bounds(&mut self, bounds: Range); + + /// Set the line width dynamically. + /// + /// Only valid to call if `Features::LINE_WIDTH` is enabled. + unsafe fn set_line_width(&mut self, width: f32); + + /// Set the depth bias dynamically. + unsafe fn set_depth_bias(&mut self, depth_bias: pso::DepthBias); + + /// Begins recording commands for a render pass on the given framebuffer. + /// + /// # Arguments + /// + /// * `render_area` - section of the framebuffer to render. + /// * `clear_values` - iterator of [clear values][crate::command::ClearValue] + /// to use to use for `clear_*` commands, one for each attachment of the render pass + /// that has a clear operation. + /// * `first_subpass` - specifies, for the first subpass, whether the + /// rendering commands are provided inline or whether the render + /// pass is composed of subpasses. + unsafe fn begin_render_pass( + &mut self, + render_pass: &B::RenderPass, + framebuffer: &B::Framebuffer, + render_area: pso::Rect, + clear_values: T, + first_subpass: SubpassContents, + ) where + T: IntoIterator, + T::Item: Borrow, + T::IntoIter: ExactSizeIterator; + + /// Steps to the next subpass in the current render pass. + unsafe fn next_subpass(&mut self, contents: SubpassContents); + + /// Finishes recording commands for the current a render pass. + unsafe fn end_render_pass(&mut self); + + /// Bind a graphics pipeline. + /// + /// # Errors + /// + /// This function does not return an error. Invalid usage of this function + /// will result in an error on `finish`. + /// + /// - Command buffer must be in recording state. + /// - Only queues with graphics capability support this function. + unsafe fn bind_graphics_pipeline(&mut self, pipeline: &B::GraphicsPipeline); + + /// Takes an iterator of graphics `DescriptorSet`'s, and binds them to the command buffer. + /// `first_set` is the index that the first descriptor is mapped to in the command buffer. + unsafe fn bind_graphics_descriptor_sets( + &mut self, + layout: &B::PipelineLayout, + first_set: usize, + sets: I, + offsets: J, + ) where + I: IntoIterator, + I::Item: Borrow, + I::IntoIter: ExactSizeIterator, + J: IntoIterator, + J::Item: Borrow, + J::IntoIter: ExactSizeIterator; + + /// Bind a compute pipeline. + /// + /// # Errors + /// + /// This function does not return an error. Invalid usage of this function + /// will result in an error on `finish`. + /// + /// - Command buffer must be in recording state. + /// - Only queues with compute capability support this function. + unsafe fn bind_compute_pipeline(&mut self, pipeline: &B::ComputePipeline); + + /// Takes an iterator of compute `DescriptorSet`'s, and binds them to the command buffer, + /// `first_set` is the index that the first descriptor is mapped to in the command buffer. + unsafe fn bind_compute_descriptor_sets( + &mut self, + layout: &B::PipelineLayout, + first_set: usize, + sets: I, + offsets: J, + ) where + I: IntoIterator, + I::Item: Borrow, + I::IntoIter: ExactSizeIterator, + J: IntoIterator, + J::Item: Borrow, + J::IntoIter: ExactSizeIterator; + + /// Execute a workgroup in the compute pipeline. `x`, `y` and `z` are the + /// number of local workgroups to dispatch along each "axis"; a total of `x`*`y`*`z` + /// local workgroups will be created. + /// + /// # Errors + /// + /// This function does not return an error. Invalid usage of this function + /// will result in an error on `finish`. + /// + /// - Command buffer must be in recording state. + /// - A compute pipeline must be bound using `bind_compute_pipeline`. + /// - Only queues with compute capability support this function. + /// - This function must be called outside of a render pass. + /// - `count` must be less than or equal to `Limits::max_compute_work_group_count` + /// + /// TODO: + unsafe fn dispatch(&mut self, count: WorkGroupCount); + + /// Works similarly to `dispatch()` but reads parameters from the given + /// buffer during execution. + unsafe fn dispatch_indirect(&mut self, buffer: &B::Buffer, offset: buffer::Offset); + + /// Adds a command to copy regions from the source to destination buffer. + unsafe fn copy_buffer(&mut self, src: &B::Buffer, dst: &B::Buffer, regions: T) + where + T: IntoIterator, + T::Item: Borrow, + T::IntoIter: ExactSizeIterator; + + /// Copies regions from the source to the destination images, which + /// have the given layouts. No format conversion is done; the source and destination + /// `Layout`'s **must** have the same sized image formats (such as `Rgba8Unorm` and + /// `R32`, both of which are 32 bits). + unsafe fn copy_image( + &mut self, + src: &B::Image, + src_layout: Layout, + dst: &B::Image, + dst_layout: Layout, + regions: T, + ) where + T: IntoIterator, + T::Item: Borrow, + T::IntoIter: ExactSizeIterator; + + /// Copies regions from the source buffer to the destination image. + unsafe fn copy_buffer_to_image( + &mut self, + src: &B::Buffer, + dst: &B::Image, + dst_layout: Layout, + regions: T, + ) where + T: IntoIterator, + T::Item: Borrow, + T::IntoIter: ExactSizeIterator; + + /// Copies regions from the source image to the destination buffer. + unsafe fn copy_image_to_buffer( + &mut self, + src: &B::Image, + src_layout: Layout, + dst: &B::Buffer, + regions: T, + ) where + T: IntoIterator, + T::Item: Borrow, + T::IntoIter: ExactSizeIterator; + + // TODO: This explanation needs improvement. + /// Performs a non-indexed drawing operation, fetching vertex attributes + /// from the currently bound vertex buffers. It performs instanced + /// drawing, drawing `instances.len()` + /// times with an `instanceIndex` starting with the start of the range. + unsafe fn draw(&mut self, vertices: Range, instances: Range); + + /// Performs indexed drawing, drawing the range of indices + /// given by the current index buffer and any bound vertex buffers. + /// `base_vertex` specifies the vertex offset corresponding to index 0. + /// That is, the offset into the vertex buffer is `(current_index + base_vertex)` + /// + /// It also performs instanced drawing, identical to `draw()`. + unsafe fn draw_indexed( + &mut self, + indices: Range, + base_vertex: VertexOffset, + instances: Range, + ); + + /// Functions identically to `draw()`, except the parameters are read + /// from the given buffer, starting at `offset` and increasing `stride` + /// bytes with each successive draw. Performs `draw_count` draws total. + /// `draw_count` may be zero. + /// + /// Each draw command in the buffer is a series of 4 `u32` values specifying, + /// in order, the number of vertices to draw, the number of instances to draw, + /// the index of the first vertex to draw, and the instance ID of the first + /// instance to draw. + unsafe fn draw_indirect( + &mut self, + buffer: &B::Buffer, + offset: buffer::Offset, + draw_count: DrawCount, + stride: u32, + ); + + /// Like `draw_indirect()`, this does indexed drawing a la `draw_indexed()` but + /// reads the draw parameters out of the given buffer. + /// + /// Each draw command in the buffer is a series of 5 values specifying, + /// in order, the number of indices, the number of instances, the first index, + /// the vertex offset, and the first instance. All are `u32`'s except + /// the vertex offset, which is an `i32`. + unsafe fn draw_indexed_indirect( + &mut self, + buffer: &B::Buffer, + offset: buffer::Offset, + draw_count: DrawCount, + stride: u32, + ); + + /// Functions identically to `draw_indirect()`, except the amount of draw + /// calls are specified by the u32 in `count_buffer` at `count_buffer_offset`. + /// There is a limit of `max_draw_count` invocations. + /// + /// Each draw command in the buffer is a series of 4 `u32` values specifying, + /// in order, the number of vertices to draw, the number of instances to draw, + /// the index of the first vertex to draw, and the instance ID of the first + /// instance to draw. + unsafe fn draw_indirect_count( + &mut self, + _buffer: &B::Buffer, + _offset: buffer::Offset, + _count_buffer: &B::Buffer, + _count_buffer_offset: buffer::Offset, + _max_draw_count: u32, + _stride: u32, + ); + + /// Functions identically to `draw_indexed_indirect()`, except the amount of draw + /// calls are specified by the u32 in `count_buffer` at `count_buffer_offset`. + /// There is a limit of `max_draw_count` invocations. + /// + /// Each draw command in the buffer is a series of 5 values specifying, + /// in order, the number of indices, the number of instances, the first index, + /// the vertex offset, and the first instance. All are `u32`'s except + /// the vertex offset, which is an `i32`. + unsafe fn draw_indexed_indirect_count( + &mut self, + _buffer: &B::Buffer, + _offset: buffer::Offset, + _count_buffer: &B::Buffer, + _count_buffer_offset: buffer::Offset, + _max_draw_count: u32, + _stride: u32, + ); + + /// Dispatches `task_count` of threads. Similar to compute dispatch. + unsafe fn draw_mesh_tasks(&mut self, task_count: TaskCount, first_task: TaskCount); + + /// Indirect version of `draw_mesh_tasks`. Analogous to `draw_indirect`, but for mesh shaders. + unsafe fn draw_mesh_tasks_indirect( + &mut self, + buffer: &B::Buffer, + offset: buffer::Offset, + draw_count: DrawCount, + stride: u32, + ); + + /// Like `draw_mesh_tasks_indirect` except that the draw count is read by + /// the device from a buffer during execution. The command will read an + /// unsigned 32-bit integer from `count_buffer` located at `count_buffer_offset` + /// and use this as the draw count. + unsafe fn draw_mesh_tasks_indirect_count( + &mut self, + buffer: &B::Buffer, + offset: buffer::Offset, + count_buffer: &B::Buffer, + count_buffer_offset: buffer::Offset, + max_draw_count: DrawCount, + stride: u32, + ); + + /// Signals an event once all specified stages of the shader pipeline have completed. + unsafe fn set_event(&mut self, event: &B::Event, stages: pso::PipelineStage); + + /// Resets an event once all specified stages of the shader pipeline have completed. + unsafe fn reset_event(&mut self, event: &B::Event, stages: pso::PipelineStage); + + /// Waits at some shader stage(s) until all events have been signalled. + /// + /// - `src_stages` specifies the shader pipeline stages in which the events were signalled. + /// - `dst_stages` specifies the shader pipeline stages at which execution should wait. + /// - `barriers` specifies a series of memory barriers to be executed before pipeline execution + /// resumes. + unsafe fn wait_events<'a, I, J>( + &mut self, + events: I, + stages: Range, + barriers: J, + ) where + I: IntoIterator, + I::Item: Borrow, + I::IntoIter: ExactSizeIterator, + J: IntoIterator, + J::Item: Borrow>, + J::IntoIter: ExactSizeIterator; + + /// Begins a query operation. Queries count operations or record timestamps + /// resulting from commands that occur between the beginning and end of the query, + /// and save the results to the query pool. + unsafe fn begin_query(&mut self, query: query::Query, flags: query::ControlFlags); + + /// End a query. + unsafe fn end_query(&mut self, query: query::Query); + + /// Reset/clear the values in the given range of the query pool. + unsafe fn reset_query_pool(&mut self, pool: &B::QueryPool, queries: Range); + + /// Copy query results into a buffer. + unsafe fn copy_query_pool_results( + &mut self, + pool: &B::QueryPool, + queries: Range, + buffer: &B::Buffer, + offset: buffer::Offset, + stride: buffer::Offset, + flags: query::ResultFlags, + ); + + /// Requests a timestamp to be written. + unsafe fn write_timestamp(&mut self, stage: pso::PipelineStage, query: query::Query); + + /// Modify constant data in a graphics pipeline. Push constants are intended to modify data in a + /// pipeline more quickly than a updating the values inside a descriptor set. + /// + /// Push constants must be aligned to 4 bytes, and to guarantee alignment, this function takes a + /// `&[u32]` instead of a `&[u8]`. Note that the offset is still specified in units of bytes. + unsafe fn push_graphics_constants( + &mut self, + layout: &B::PipelineLayout, + stages: pso::ShaderStageFlags, + offset: u32, + constants: &[u32], + ); + + /// Modify constant data in a compute pipeline. Push constants are intended to modify data in a + /// pipeline more quickly than a updating the values inside a descriptor set. + /// + /// Push constants must be aligned to 4 bytes, and to guarantee alignment, this function takes a + /// `&[u32]` instead of a `&[u8]`. Note that the offset is still specified in units of bytes. + unsafe fn push_compute_constants( + &mut self, + layout: &B::PipelineLayout, + offset: u32, + constants: &[u32], + ); + + /// Execute the given secondary command buffers. + unsafe fn execute_commands<'a, T, I>(&mut self, cmd_buffers: I) + where + T: 'a + Borrow, + I: IntoIterator, + I::IntoIter: ExactSizeIterator; + + /// Debug mark the current spot in the command buffer. + unsafe fn insert_debug_marker(&mut self, name: &str, color: u32); + /// Start a debug marker at the current place in the command buffer. + unsafe fn begin_debug_marker(&mut self, name: &str, color: u32); + /// End the last started debug marker scope. + unsafe fn end_debug_marker(&mut self); +} diff --git a/third_party/rust/gfx-hal/src/command/structs.rs b/third_party/rust/gfx-hal/src/command/structs.rs new file mode 100644 index 0000000000..d3c412f8c4 --- /dev/null +++ b/third_party/rust/gfx-hal/src/command/structs.rs @@ -0,0 +1,86 @@ +use crate::{buffer, image}; + +use std::ops::Range; + +/// Specifies a source region and a destination +/// region in a buffer for copying. All values +/// are in units of bytes. +#[derive(Clone, Copy, Debug)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct BufferCopy { + /// Buffer region source offset. + pub src: buffer::Offset, + /// Buffer region destination offset. + pub dst: buffer::Offset, + /// Region size. + pub size: buffer::Offset, +} + +/// Bundles together all the parameters needed to copy data from one `Image` +/// to another. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct ImageCopy { + /// The image subresource to copy from. + pub src_subresource: image::SubresourceLayers, + /// The source offset. + pub src_offset: image::Offset, + /// The image subresource to copy to. + pub dst_subresource: image::SubresourceLayers, + /// The destination offset. + pub dst_offset: image::Offset, + /// The extent of the region to copy. + pub extent: image::Extent, +} + +/// Bundles together all the parameters needed to copy a buffer +/// to an image or vice-versa. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct BufferImageCopy { + /// Buffer offset in bytes. + pub buffer_offset: buffer::Offset, + /// Width of a buffer 'row' in texels. + pub buffer_width: u32, + /// Height of a buffer 'image slice' in texels. + pub buffer_height: u32, + /// The image subresource. + pub image_layers: image::SubresourceLayers, + /// The offset of the portion of the image to copy. + pub image_offset: image::Offset, + /// Size of the portion of the image to copy. + pub image_extent: image::Extent, +} + +/// Parameters for an image resolve operation, +/// where a multi-sampled image is copied into a single-sampled +/// image. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct ImageResolve { + /// Source image and layers. + pub src_subresource: image::SubresourceLayers, + /// Source image offset. + pub src_offset: image::Offset, + /// Destination image and layers. + pub dst_subresource: image::SubresourceLayers, + /// Destination image offset. + pub dst_offset: image::Offset, + /// Image extent. + pub extent: image::Extent, +} + +/// Parameters for an image blit operation, where a portion of one image +/// is copied into another, possibly with scaling and filtering. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct ImageBlit { + /// Source image and layers. + pub src_subresource: image::SubresourceLayers, + /// Source image bounds. + pub src_bounds: Range, + /// Destination image and layers. + pub dst_subresource: image::SubresourceLayers, + /// Destination image bounds. + pub dst_bounds: Range, +} diff --git a/third_party/rust/gfx-hal/src/device.rs b/third_party/rust/gfx-hal/src/device.rs new file mode 100644 index 0000000000..e8aa1ec5ae --- /dev/null +++ b/third_party/rust/gfx-hal/src/device.rs @@ -0,0 +1,997 @@ +//! Logical graphics device. +//! +//! # Device +//! +//! This module exposes the [`Device`][Device] trait, which provides methods for creating +//! and managing graphics resources such as buffers, images and memory. +//! +//! The `Adapter` and `Device` types are very similar to the Vulkan concept of +//! "physical devices" vs. "logical devices"; an `Adapter` is single GPU +//! (or CPU) that implements a backend, a `Device` is a +//! handle to that physical device that has the requested capabilities +//! and is used to actually do things. + +use std::any::Any; +use std::borrow::Borrow; +use std::ops::Range; +use std::{fmt, iter}; + +use crate::{ + buffer, format, image, + memory::{Requirements, Segment}, + pass, + pool::CommandPoolCreateFlags, + pso, + pso::DescriptorPoolCreateFlags, + query, + queue::QueueFamilyId, + Backend, MemoryTypeId, +}; + +/// Error occurred caused device to be lost. +#[derive(Clone, Debug, PartialEq)] +pub struct DeviceLost; + +impl std::fmt::Display for DeviceLost { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fmt.write_str("Device lost") + } +} + +impl std::error::Error for DeviceLost {} + +/// Error occurred caused surface to be lost. +#[derive(Clone, Debug, PartialEq)] +pub struct SurfaceLost; + +impl std::fmt::Display for SurfaceLost { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fmt.write_str("Surface lost") + } +} + +impl std::error::Error for SurfaceLost {} + +/// Native window is already in use by graphics API. +#[derive(Clone, Debug, PartialEq)] +pub struct WindowInUse; + +impl std::fmt::Display for WindowInUse { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fmt.write_str("Window is in use") + } +} + +impl std::error::Error for WindowInUse {} + +/// Error allocating memory. +#[derive(Clone, Debug, PartialEq)] +pub enum OutOfMemory { + /// Host memory exhausted. + Host, + /// Device memory exhausted. + Device, +} + +impl std::fmt::Display for OutOfMemory { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + OutOfMemory::Host => write!(fmt, "Out of host memory"), + OutOfMemory::Device => write!(fmt, "Out of device memory"), + } + } +} + +impl std::error::Error for OutOfMemory {} + +/// Error occurred caused device to be lost +/// or out of memory error. +#[derive(Clone, Debug, PartialEq)] +pub enum OomOrDeviceLost { + /// Out of either host or device memory. + OutOfMemory(OutOfMemory), + /// Device is lost + DeviceLost(DeviceLost), +} + +impl From for OomOrDeviceLost { + fn from(error: OutOfMemory) -> Self { + OomOrDeviceLost::OutOfMemory(error) + } +} + +impl From for OomOrDeviceLost { + fn from(error: DeviceLost) -> Self { + OomOrDeviceLost::DeviceLost(error) + } +} + +impl std::fmt::Display for OomOrDeviceLost { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + OomOrDeviceLost::DeviceLost(err) => write!(fmt, "Failed querying device: {}", err), + OomOrDeviceLost::OutOfMemory(err) => write!(fmt, "Failed querying device: {}", err), + } + } +} + +impl std::error::Error for OomOrDeviceLost { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + OomOrDeviceLost::DeviceLost(err) => Some(err), + OomOrDeviceLost::OutOfMemory(err) => Some(err), + } + } +} + +/// Possible cause of allocation failure. +#[derive(Clone, Debug, PartialEq)] +pub enum AllocationError { + /// Out of either host or device memory. + OutOfMemory(OutOfMemory), + + /// Cannot create any more objects. + TooManyObjects, +} + +impl From for AllocationError { + fn from(error: OutOfMemory) -> Self { + AllocationError::OutOfMemory(error) + } +} + +impl std::fmt::Display for AllocationError { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + AllocationError::OutOfMemory(err) => write!(fmt, "Failed to allocate object: {}", err), + AllocationError::TooManyObjects => { + write!(fmt, "Failed to allocate object: Too many objects") + } + } + } +} + +impl std::error::Error for AllocationError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + AllocationError::OutOfMemory(err) => Some(err), + _ => None, + } + } +} + +/// Device creation errors during `open`. +#[derive(Clone, Debug, PartialEq)] +pub enum CreationError { + /// Out of either host or device memory. + OutOfMemory(OutOfMemory), + /// Device initialization failed due to implementation specific errors. + InitializationFailed, + /// At least one of the user requested extensions if not supported by the + /// physical device. + MissingExtension, + /// At least one of the user requested features if not supported by the + /// physical device. + /// + /// Use [`features`](trait.PhysicalDevice.html#tymethod.features) + /// for checking the supported features. + MissingFeature, + /// Too many logical devices have been created from this physical device. + /// + /// The implementation may only support one logical device for each physical + /// device or lacks resources to allocate a new device. + TooManyObjects, + /// The logical or physical device are lost during the device creation + /// process. + /// + /// This may be caused by hardware failure, physical device removal, + /// power outage, etc. + DeviceLost, +} + +impl std::fmt::Display for CreationError { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + CreationError::OutOfMemory(err) => write!(fmt, "Failed to create device: {}", err), + CreationError::InitializationFailed => write!( + fmt, + "Failed to create device: Implementation specific error occurred" + ), + CreationError::MissingExtension => write!( + fmt, + "Failed to create device: Requested extension is missing" + ), + CreationError::MissingFeature => { + write!(fmt, "Failed to create device: Requested feature is missing") + } + CreationError::TooManyObjects => { + write!(fmt, "Failed to create device: Too many objects") + } + CreationError::DeviceLost => write!( + fmt, + "Failed to create device: Logical or Physical device was lost during creation" + ), + } + } +} + +impl std::error::Error for CreationError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + CreationError::OutOfMemory(err) => Some(err), + _ => None, + } + } +} + +/// Error accessing a mapping. +#[derive(Clone, Debug, PartialEq)] +pub enum MapError { + /// Out of either host or device memory. + OutOfMemory(OutOfMemory), + /// The requested mapping range is outside of the resource. + OutOfBounds, + /// Failed to allocate an appropriately sized contiguous virtual address range + MappingFailed, + /// Memory is not CPU visible + Access, +} + +impl From for MapError { + fn from(error: OutOfMemory) -> Self { + MapError::OutOfMemory(error) + } +} + +impl std::fmt::Display for MapError { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + MapError::OutOfMemory(err) => write!(fmt, "Failed to map memory: {}", err), + MapError::OutOfBounds => write!(fmt, "Failed to map memory: Requested range is outside the resource"), + MapError::MappingFailed => write!( + fmt, + "Failed to map memory: Unable to allocate an appropriately sized contiguous virtual address range" + ), + MapError::Access => write!(fmt, "Failed to map memory: Memory is not CPU visible"), + } + } +} + +impl std::error::Error for MapError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + MapError::OutOfMemory(err) => Some(err), + _ => None, + } + } +} + +/// Error binding a resource to memory allocation. +#[derive(Clone, Debug, PartialEq)] +pub enum BindError { + /// Out of either host or device memory. + OutOfMemory(OutOfMemory), + /// Requested binding to memory that doesn't support the required operations. + WrongMemory, + /// Requested binding to an invalid memory. + OutOfBounds, +} + +impl From for BindError { + fn from(error: OutOfMemory) -> Self { + BindError::OutOfMemory(error) + } +} + +impl std::fmt::Display for BindError { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + BindError::OutOfMemory(err) => { + write!(fmt, "Failed to bind object to memory range: {}", err) + } + BindError::OutOfBounds => write!( + fmt, + "Failed to bind object to memory range: Requested range is outside the resource" + ), + BindError::WrongMemory => { + write!(fmt, "Failed to bind object to memory range: Wrong memory") + } + } + } +} + +impl std::error::Error for BindError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + BindError::OutOfMemory(err) => Some(err), + _ => None, + } + } +} + +/// Specifies the waiting targets. +#[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum WaitFor { + /// Wait for any target. + Any, + /// Wait for all targets at once. + All, +} + +/// An error from creating a shader module. +#[derive(Clone, Debug, PartialEq)] +pub enum ShaderError { + /// The shader failed to compile. + CompilationFailed(String), + /// The shader is missing an entry point. + MissingEntryPoint(String), + /// The shader has a mismatch of interface (e.g missing push constants). + InterfaceMismatch(String), + /// The shader stage is not supported. + UnsupportedStage(pso::ShaderStageFlags), + /// Out of either host or device memory. + OutOfMemory(OutOfMemory), +} + +impl From for ShaderError { + fn from(error: OutOfMemory) -> Self { + ShaderError::OutOfMemory(error) + } +} + +impl std::fmt::Display for ShaderError { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ShaderError::OutOfMemory(err) => write!(fmt, "Shader error: {}", err), + ShaderError::CompilationFailed(string) => { + write!(fmt, "Shader error: Compilation failed: {}", string) + } + ShaderError::MissingEntryPoint(string) => { + write!(fmt, "Shader error: Missing entry point: {}", string) + } + ShaderError::InterfaceMismatch(string) => { + write!(fmt, "Shader error: Interface mismatch: {}", string) + } + ShaderError::UnsupportedStage(stage) => { + write!(fmt, "Shader error: Unsupported stage: {:?}", stage) + } + } + } +} + +impl std::error::Error for ShaderError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + ShaderError::OutOfMemory(err) => Some(err), + _ => None, + } + } +} + +/// Logical device handle, responsible for creating and managing resources +/// for the physical device it was created from. +/// +/// ## Resource Construction and Handling +/// +/// This device structure can then be used to create and manage different resources, +/// like [buffers][Device::create_buffer], [shader modules][Device::create_shader_module] +/// and [images][Device::create_image]. See the individual methods for more information. +/// +/// ## Mutability +/// +/// All the methods get `&self`. Any internal mutability of the `Device` is hidden from the user. +/// +/// ## Synchronization +/// +/// `Device` should be usable concurrently from multiple threads. The `Send` and `Sync` bounds +/// are not enforced at the HAL level due to OpenGL constraint (to be revised). Users can still +/// benefit from the backends that support synchronization of the `Device`. +/// +pub trait Device: fmt::Debug + Any + Send + Sync { + /// Allocates a memory segment of a specified type. + /// + /// There is only a limited amount of allocations allowed depending on the implementation! + /// + /// # Arguments + /// + /// * `memory_type` - Index of the memory type in the memory properties of the associated physical device. + /// * `size` - Size of the allocation. + unsafe fn allocate_memory( + &self, + memory_type: MemoryTypeId, + size: u64, + ) -> Result; + + /// Free device memory + unsafe fn free_memory(&self, memory: B::Memory); + + /// Create a new [command pool][crate::pool::CommandPool] for a given queue family. + /// + /// *Note*: the family has to be associated with one of [the queue groups + /// of this device][crate::adapter::Gpu::queue_groups]. + unsafe fn create_command_pool( + &self, + family: QueueFamilyId, + create_flags: CommandPoolCreateFlags, + ) -> Result; + + /// Destroy a command pool. + unsafe fn destroy_command_pool(&self, pool: B::CommandPool); + + /// Create a [render pass][crate::pass] with the given attachments and subpasses. + /// + /// The use of a render pass in a command buffer is a *render pass* instance. + /// + /// # Arguments + /// + /// * `attachments` - [image attachments][crate::pass::Attachment] to be used in + /// this render pass. Usually you need at least one attachment, to be used as output. + /// * `subpasses` - [subpasses][crate::pass::SubpassDesc] to use. + /// You need to use at least one subpass. + /// * `dependencies` - [dependencies between subpasses][crate::pass::SubpassDependency]. + /// Can be empty. + unsafe fn create_render_pass<'a, IA, IS, ID>( + &self, + attachments: IA, + subpasses: IS, + dependencies: ID, + ) -> Result + where + IA: IntoIterator, + IA::Item: Borrow, + IA::IntoIter: ExactSizeIterator, + IS: IntoIterator, + IS::Item: Borrow>, + IS::IntoIter: ExactSizeIterator, + ID: IntoIterator, + ID::Item: Borrow, + ID::IntoIter: ExactSizeIterator; + + /// Destroys a *render pass* created by this device. + unsafe fn destroy_render_pass(&self, rp: B::RenderPass); + + /// Create a new pipeline layout object. + /// + /// # Arguments + /// + /// * `set_layouts` - Descriptor set layouts + /// * `push_constants` - Ranges of push constants. A shader stage may only contain one push + /// constant block. The range is defined in units of bytes. + /// + /// # PipelineLayout + /// + /// Access to descriptor sets from a pipeline is accomplished through a *pipeline layout*. + /// Zero or more descriptor set layouts and zero or more push constant ranges are combined to + /// form a pipeline layout object which describes the complete set of resources that **can** be + /// accessed by a pipeline. The pipeline layout represents a sequence of descriptor sets with + /// each having a specific layout. This sequence of layouts is used to determine the interface + /// between shader stages and shader resources. Each pipeline is created using a pipeline layout. + unsafe fn create_pipeline_layout( + &self, + set_layouts: IS, + push_constant: IR, + ) -> Result + where + IS: IntoIterator, + IS::Item: Borrow, + IS::IntoIter: ExactSizeIterator, + IR: IntoIterator, + IR::Item: Borrow<(pso::ShaderStageFlags, Range)>, + IR::IntoIter: ExactSizeIterator; + + /// Destroy a pipeline layout object + unsafe fn destroy_pipeline_layout(&self, layout: B::PipelineLayout); + + /// Create a pipeline cache object. + unsafe fn create_pipeline_cache( + &self, + data: Option<&[u8]>, + ) -> Result; + + /// Retrieve data from pipeline cache object. + unsafe fn get_pipeline_cache_data( + &self, + cache: &B::PipelineCache, + ) -> Result, OutOfMemory>; + + /// Merge a number of source pipeline caches into the target one. + unsafe fn merge_pipeline_caches( + &self, + target: &B::PipelineCache, + sources: I, + ) -> Result<(), OutOfMemory> + where + I: IntoIterator, + I::Item: Borrow, + I::IntoIter: ExactSizeIterator; + + /// Destroy a pipeline cache object. + unsafe fn destroy_pipeline_cache(&self, cache: B::PipelineCache); + + /// Create a graphics pipeline. + /// + /// # Arguments + /// + /// * `desc` - the [description][crate::pso::GraphicsPipelineDesc] of + /// the graphics pipeline to create. + /// * `cache` - the pipeline cache, + /// [obtained from this device][Device::create_pipeline_cache], + /// used for faster PSO creation. + unsafe fn create_graphics_pipeline<'a>( + &self, + desc: &pso::GraphicsPipelineDesc<'a, B>, + cache: Option<&B::PipelineCache>, + ) -> Result; + + /// Create multiple graphics pipelines. + unsafe fn create_graphics_pipelines<'a, I>( + &self, + descs: I, + cache: Option<&B::PipelineCache>, + ) -> Vec> + where + I: IntoIterator, + I::Item: Borrow>, + { + descs + .into_iter() + .map(|desc| self.create_graphics_pipeline(desc.borrow(), cache)) + .collect() + } + + /// Destroy a graphics pipeline. + /// + /// The graphics pipeline shouldn't be destroyed before any submitted command buffer, + /// which references the graphics pipeline, has finished execution. + unsafe fn destroy_graphics_pipeline(&self, pipeline: B::GraphicsPipeline); + + /// Create a compute pipeline. + unsafe fn create_compute_pipeline<'a>( + &self, + desc: &pso::ComputePipelineDesc<'a, B>, + cache: Option<&B::PipelineCache>, + ) -> Result; + + /// Create compute pipelines. + unsafe fn create_compute_pipelines<'a, I>( + &self, + descs: I, + cache: Option<&B::PipelineCache>, + ) -> Vec> + where + I: IntoIterator, + I::Item: Borrow>, + { + descs + .into_iter() + .map(|desc| self.create_compute_pipeline(desc.borrow(), cache)) + .collect() + } + + /// Destroy a compute pipeline. + /// + /// The compute pipeline shouldn't be destroyed before any submitted command buffer, + /// which references the compute pipeline, has finished execution. + unsafe fn destroy_compute_pipeline(&self, pipeline: B::ComputePipeline); + + /// Create a new framebuffer object. + /// + /// # Safety + /// - `extent.width`, `extent.height` and `extent.depth` **must** be greater than `0`. + unsafe fn create_framebuffer( + &self, + pass: &B::RenderPass, + attachments: I, + extent: image::Extent, + ) -> Result + where + I: IntoIterator, + I::Item: Borrow; + + /// Destroy a framebuffer. + /// + /// The framebuffer shouldn't be destroy before any submitted command buffer, + /// which references the framebuffer, has finished execution. + unsafe fn destroy_framebuffer(&self, buf: B::Framebuffer); + + /// Create a new shader module object from the SPIR-V binary data. + /// + /// Once a shader module has been created, any [entry points][crate::pso::EntryPoint] + /// it contains can be used in pipeline shader stages of + /// [compute pipelines][crate::pso::ComputePipelineDesc] and + /// [graphics pipelines][crate::pso::GraphicsPipelineDesc]. + unsafe fn create_shader_module( + &self, + spirv_data: &[u32], + ) -> Result; + + /// Destroy a shader module module + /// + /// A shader module can be destroyed while pipelines created using its shaders are still in use. + unsafe fn destroy_shader_module(&self, shader: B::ShaderModule); + + /// Create a new buffer (unbound). + /// + /// The created buffer won't have associated memory until `bind_buffer_memory` is called. + unsafe fn create_buffer( + &self, + size: u64, + usage: buffer::Usage, + ) -> Result; + + /// Get memory requirements for the buffer + unsafe fn get_buffer_requirements(&self, buf: &B::Buffer) -> Requirements; + + /// Bind memory to a buffer. + /// + /// Be sure to check that there is enough memory available for the buffer. + /// Use `get_buffer_requirements` to acquire the memory requirements. + unsafe fn bind_buffer_memory( + &self, + memory: &B::Memory, + offset: u64, + buf: &mut B::Buffer, + ) -> Result<(), BindError>; + + /// Destroy a buffer. + /// + /// The buffer shouldn't be destroyed before any submitted command buffer, + /// which references the images, has finished execution. + unsafe fn destroy_buffer(&self, buffer: B::Buffer); + + /// Create a new buffer view object + unsafe fn create_buffer_view( + &self, + buf: &B::Buffer, + fmt: Option, + range: buffer::SubRange, + ) -> Result; + + /// Destroy a buffer view object + unsafe fn destroy_buffer_view(&self, view: B::BufferView); + + /// Create a new image object + unsafe fn create_image( + &self, + kind: image::Kind, + mip_levels: image::Level, + format: format::Format, + tiling: image::Tiling, + usage: image::Usage, + view_caps: image::ViewCapabilities, + ) -> Result; + + /// Get memory requirements for the Image + unsafe fn get_image_requirements(&self, image: &B::Image) -> Requirements; + + /// + unsafe fn get_image_subresource_footprint( + &self, + image: &B::Image, + subresource: image::Subresource, + ) -> image::SubresourceFootprint; + + /// Bind device memory to an image object + unsafe fn bind_image_memory( + &self, + memory: &B::Memory, + offset: u64, + image: &mut B::Image, + ) -> Result<(), BindError>; + + /// Destroy an image. + /// + /// The image shouldn't be destroyed before any submitted command buffer, + /// which references the images, has finished execution. + unsafe fn destroy_image(&self, image: B::Image); + + /// Create an image view from an existing image + unsafe fn create_image_view( + &self, + image: &B::Image, + view_kind: image::ViewKind, + format: format::Format, + swizzle: format::Swizzle, + range: image::SubresourceRange, + ) -> Result; + + /// Destroy an image view object + unsafe fn destroy_image_view(&self, view: B::ImageView); + + /// Create a new sampler object + unsafe fn create_sampler( + &self, + desc: &image::SamplerDesc, + ) -> Result; + + /// Destroy a sampler object + unsafe fn destroy_sampler(&self, sampler: B::Sampler); + + /// Create a descriptor pool. + /// + /// Descriptor pools allow allocation of descriptor sets. + /// The pool can't be modified directly, only through updating descriptor sets. + unsafe fn create_descriptor_pool( + &self, + max_sets: usize, + descriptor_ranges: I, + flags: DescriptorPoolCreateFlags, + ) -> Result + where + I: IntoIterator, + I::Item: Borrow, + I::IntoIter: ExactSizeIterator; + + /// Destroy a descriptor pool object + /// + /// When a pool is destroyed, all descriptor sets allocated from the pool are implicitly freed + /// and become invalid. Descriptor sets allocated from a given pool do not need to be freed + /// before destroying that descriptor pool. + unsafe fn destroy_descriptor_pool(&self, pool: B::DescriptorPool); + + /// Create a descriptor set layout. + /// + /// A descriptor set layout object is defined by an array of zero or more descriptor bindings. + /// Each individual descriptor binding is specified by a descriptor type, a count (array size) + /// of the number of descriptors in the binding, a set of shader stages that **can** access the + /// binding, and (if using immutable samplers) an array of sampler descriptors. + unsafe fn create_descriptor_set_layout( + &self, + bindings: I, + immutable_samplers: J, + ) -> Result + where + I: IntoIterator, + I::Item: Borrow, + J: IntoIterator, + J::Item: Borrow, + J::IntoIter: ExactSizeIterator; + + /// Destroy a descriptor set layout object + unsafe fn destroy_descriptor_set_layout(&self, layout: B::DescriptorSetLayout); + + /// Specifying the parameters of a descriptor set write operation + unsafe fn write_descriptor_sets<'a, I, J>(&self, write_iter: I) + where + I: IntoIterator>, + J: IntoIterator, + J::Item: Borrow>; + + /// Structure specifying a copy descriptor set operation + unsafe fn copy_descriptor_sets<'a, I>(&self, copy_iter: I) + where + I: IntoIterator, + I::Item: Borrow>, + I::IntoIter: ExactSizeIterator; + + /// Map a memory object into application address space + /// + /// Call `map_memory()` to retrieve a host virtual address pointer to a region of a mappable memory object + unsafe fn map_memory(&self, memory: &B::Memory, segment: Segment) -> Result<*mut u8, MapError>; + + /// Flush mapped memory ranges + unsafe fn flush_mapped_memory_ranges<'a, I>(&self, ranges: I) -> Result<(), OutOfMemory> + where + I: IntoIterator, + I::Item: Borrow<(&'a B::Memory, Segment)>; + + /// Invalidate ranges of non-coherent memory from the host caches + unsafe fn invalidate_mapped_memory_ranges<'a, I>(&self, ranges: I) -> Result<(), OutOfMemory> + where + I: IntoIterator, + I::Item: Borrow<(&'a B::Memory, Segment)>; + + /// Unmap a memory object once host access to it is no longer needed by the application + unsafe fn unmap_memory(&self, memory: &B::Memory); + + /// Create a new semaphore object. + fn create_semaphore(&self) -> Result; + + /// Destroy a semaphore object. + unsafe fn destroy_semaphore(&self, semaphore: B::Semaphore); + + /// Create a new fence object. + /// + /// Fences are a synchronization primitive that **can** be used to insert a dependency from + /// a queue to the host. + /// Fences have two states - signaled and unsignaled. + /// + /// A fence **can** be signaled as part of the execution of a + /// [queue submission][crate::queue::CommandQueue::submit] command. + /// + /// Fences **can** be unsignaled on the host with + /// [`reset_fences`][Device::reset_fences]. + /// + /// Fences **can** be waited on by the host with the + /// [`wait_for_fences`][Device::wait_for_fences] command. + /// + /// A fence's current state **can** be queried with + /// [`get_fence_status`][Device::get_fence_status]. + /// + /// # Arguments + /// + /// * `signaled` - the fence will be in its signaled state. + fn create_fence(&self, signaled: bool) -> Result; + + /// Resets a given fence to its original, unsignaled state. + unsafe fn reset_fence(&self, fence: &B::Fence) -> Result<(), OutOfMemory> { + self.reset_fences(iter::once(fence)) + } + + /// Resets multiple fences to their original states. + unsafe fn reset_fences(&self, fences: I) -> Result<(), OutOfMemory> + where + I: IntoIterator, + I::Item: Borrow, + I::IntoIter: ExactSizeIterator, + { + for fence in fences { + self.reset_fence(fence.borrow())?; + } + Ok(()) + } + + /// Blocks until the given fence is signaled. + /// Returns true if the fence was signaled before the timeout. + unsafe fn wait_for_fence( + &self, + fence: &B::Fence, + timeout_ns: u64, + ) -> Result { + self.wait_for_fences(iter::once(fence), WaitFor::All, timeout_ns) + } + + /// Blocks until all or one of the given fences are signaled. + /// Returns true if fences were signaled before the timeout. + unsafe fn wait_for_fences( + &self, + fences: I, + wait: WaitFor, + timeout_ns: u64, + ) -> Result + where + I: IntoIterator, + I::Item: Borrow, + I::IntoIter: ExactSizeIterator, + { + use std::{thread, time}; + fn to_ns(duration: time::Duration) -> u64 { + duration.as_secs() * 1_000_000_000 + duration.subsec_nanos() as u64 + } + + let start = time::Instant::now(); + match wait { + WaitFor::All => { + for fence in fences { + if !self.wait_for_fence(fence.borrow(), 0)? { + let elapsed_ns = to_ns(start.elapsed()); + if elapsed_ns > timeout_ns { + return Ok(false); + } + if !self.wait_for_fence(fence.borrow(), timeout_ns - elapsed_ns)? { + return Ok(false); + } + } + } + Ok(true) + } + WaitFor::Any => { + let fences: Vec<_> = fences.into_iter().collect(); + loop { + for fence in &fences { + if self.wait_for_fence(fence.borrow(), 0)? { + return Ok(true); + } + } + if to_ns(start.elapsed()) >= timeout_ns { + return Ok(false); + } + thread::sleep(time::Duration::from_millis(1)); + } + } + } + } + + /// true for signaled, false for not ready + unsafe fn get_fence_status(&self, fence: &B::Fence) -> Result; + + /// Destroy a fence object + unsafe fn destroy_fence(&self, fence: B::Fence); + + /// Create an event object. + fn create_event(&self) -> Result; + + /// Destroy an event object. + unsafe fn destroy_event(&self, event: B::Event); + + /// Query the status of an event. + /// + /// Returns `true` if the event is set, or `false` if it is reset. + unsafe fn get_event_status(&self, event: &B::Event) -> Result; + + /// Sets an event. + unsafe fn set_event(&self, event: &B::Event) -> Result<(), OutOfMemory>; + + /// Resets an event. + unsafe fn reset_event(&self, event: &B::Event) -> Result<(), OutOfMemory>; + + /// Create a new query pool object + /// + /// Queries are managed using query pool objects. Each query pool is a collection of a specific + /// number of queries of a particular type. + unsafe fn create_query_pool( + &self, + ty: query::Type, + count: query::Id, + ) -> Result; + + /// Destroy a query pool object + unsafe fn destroy_query_pool(&self, pool: B::QueryPool); + + /// Get query pool results into the specified CPU memory. + /// Returns `Ok(false)` if the results are not ready yet and neither of `WAIT` or `PARTIAL` flags are set. + unsafe fn get_query_pool_results( + &self, + pool: &B::QueryPool, + queries: Range, + data: &mut [u8], + stride: buffer::Offset, + flags: query::ResultFlags, + ) -> Result; + + /// Wait for all queues associated with this device to idle. + /// + /// Host access to all queues needs to be **externally** sycnhronized! + fn wait_idle(&self) -> Result<(), OutOfMemory>; + + /// Associate a name with an image, for easier debugging in external tools or with validation + /// layers that can print a friendly name when referring to objects in error messages + unsafe fn set_image_name(&self, image: &mut B::Image, name: &str); + /// Associate a name with a buffer, for easier debugging in external tools or with validation + /// layers that can print a friendly name when referring to objects in error messages + unsafe fn set_buffer_name(&self, buffer: &mut B::Buffer, name: &str); + /// Associate a name with a command buffer, for easier debugging in external tools or with + /// validation layers that can print a friendly name when referring to objects in error messages + unsafe fn set_command_buffer_name(&self, command_buffer: &mut B::CommandBuffer, name: &str); + /// Associate a name with a semaphore, for easier debugging in external tools or with validation + /// layers that can print a friendly name when referring to objects in error messages + unsafe fn set_semaphore_name(&self, semaphore: &mut B::Semaphore, name: &str); + /// Associate a name with a fence, for easier debugging in external tools or with validation + /// layers that can print a friendly name when referring to objects in error messages + unsafe fn set_fence_name(&self, fence: &mut B::Fence, name: &str); + /// Associate a name with a framebuffer, for easier debugging in external tools or with + /// validation layers that can print a friendly name when referring to objects in error messages + unsafe fn set_framebuffer_name(&self, framebuffer: &mut B::Framebuffer, name: &str); + /// Associate a name with a render pass, for easier debugging in external tools or with + /// validation layers that can print a friendly name when referring to objects in error messages + unsafe fn set_render_pass_name(&self, render_pass: &mut B::RenderPass, name: &str); + /// Associate a name with a descriptor set, for easier debugging in external tools or with + /// validation layers that can print a friendly name when referring to objects in error messages + unsafe fn set_descriptor_set_name(&self, descriptor_set: &mut B::DescriptorSet, name: &str); + /// Associate a name with a descriptor set layout, for easier debugging in external tools or + /// with validation layers that can print a friendly name when referring to objects in error + /// messages + unsafe fn set_descriptor_set_layout_name( + &self, + descriptor_set_layout: &mut B::DescriptorSetLayout, + name: &str, + ); + /// Associate a name with a pipeline layout, for easier debugging in external tools or with + /// validation layers that can print a friendly name when referring to objects in error messages + unsafe fn set_pipeline_layout_name(&self, pipeline_layout: &mut B::PipelineLayout, name: &str); + /// Associate a name with a compute pipeline, for easier debugging in external tools or with + /// validation layers that can print a friendly name when referring to objects in error messages + unsafe fn set_compute_pipeline_name( + &self, + compute_pipeline: &mut B::ComputePipeline, + name: &str, + ); + /// Associate a name with a graphics pipeline, for easier debugging in external tools or with + /// validation layers that can print a friendly name when referring to objects in error messages + unsafe fn set_graphics_pipeline_name( + &self, + graphics_pipeline: &mut B::GraphicsPipeline, + name: &str, + ); +} diff --git a/third_party/rust/gfx-hal/src/format.rs b/third_party/rust/gfx-hal/src/format.rs new file mode 100644 index 0000000000..2415b57cf2 --- /dev/null +++ b/third_party/rust/gfx-hal/src/format.rs @@ -0,0 +1,625 @@ +//! Universal format specification. +//! Applicable to textures, views, and vertex buffers. +//! +//! For a more detailed description of all the specific format specifiers, +//! please see [the official Vulkan documentation](https://www.khronos.org/registry/vulkan/specs/1.0/man/html/VkFormat.html) +//! +//! `gfx-rs` splits a `Format` into two sub-components, a `SurfaceType` and +//! a `ChannelType`. The `SurfaceType` specifies how the large the channels are, +//! for instance `R32_G32_B32_A32`. The `ChannelType` specifies how the +//! components are interpreted, for instance `Sfloat` or `Sint`. + +bitflags!( + /// Bitflags which describe what properties of an image + /// a format specifies or does not specify. For example, + /// the `Rgba8Unorm` format only specifies a `COLOR` aspect, + /// while `D32SfloatS8Uint` specifies both a depth and stencil + /// aspect but no color. + #[derive(Default)] + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + pub struct Aspects: u8 { + /// Color aspect. + const COLOR = 0x1; + /// Depth aspect. + const DEPTH = 0x2; + /// Stencil aspect. + const STENCIL = 0x4; + } +); + +/// Description of a format. +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct FormatDesc { + /// Total number of bits. + /// + /// * Depth/Stencil formats are opaque formats, where the total number of bits is unknown. + /// A dummy value is used for these formats instead (sum of depth and stencil bits). + /// For copy operations, the number of bits of the corresponding aspect should be used. + /// * The total number can be larger than the sum of individual format bits + /// (`color`, `alpha`, `depth` and `stencil`) for packed formats. + /// * For compressed formats, this denotes the number of bits per block. + pub bits: u16, + /// Dimensions (width, height) of the texel blocks. + pub dim: (u8, u8), + /// The format representation depends on the endianness of the platform. + /// + /// * On little-endian systems, the actual oreder of components is reverse of what + /// a surface type specifies. + pub packed: bool, + /// Format aspects + pub aspects: Aspects, +} + +impl FormatDesc { + /// Check if the format is compressed. + pub fn is_compressed(&self) -> bool { + self.dim != (1, 1) + } +} + +/// Description of the bits distribution of a format. +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct FormatBits { + /// Number of color bits (summed for R/G/B). + /// + /// For compressed formats, this value is 0. + pub color: u8, + /// Number of alpha bits. + /// + /// For compressed formats, this value is 0. + pub alpha: u8, + /// Number of depth bits + pub depth: u8, + /// Number of stencil bits + pub stencil: u8, +} + +/// Format bits configuration with no bits assigned. +pub const BITS_ZERO: FormatBits = FormatBits { + color: 0, + alpha: 0, + depth: 0, + stencil: 0, +}; + +/// Source channel in a swizzle configuration. Some may redirect onto +/// different physical channels, some may be hardcoded to 0 or 1. +#[repr(u8)] +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum Component { + //TODO: add `Identity = 0`? + /// Hardcoded zero + Zero = 1, + /// Hardcoded one + One = 2, + /// Red channel + R = 3, + /// Green channel + G = 4, + /// Blue channel + B = 5, + /// Alpha channel. + A = 6, +} + +/// Channel swizzle configuration for the resource views. +/// This specifies a "swizzle" operation which remaps the various +/// channels of a format into a different order. For example, +/// `Swizzle(Component::B, Component::G, Component::R, Component::A)` +/// will swap `RGBA` formats into `BGRA` formats and back. +/// +/// Note: It's not currently mirrored at compile-time, +/// thus providing less safety and convenience. +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct Swizzle(pub Component, pub Component, pub Component, pub Component); + +impl Swizzle { + /// A trivially non-swizzling configuration; performs no changes. + pub const NO: Swizzle = Swizzle(Component::R, Component::G, Component::B, Component::A); +} + +impl Default for Swizzle { + fn default() -> Self { + Self::NO + } +} + +/// Format properties of the physical device. +#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct Properties { + /// A bitmask of the features supported when an image with linear tiling is requested. + /// Linear tiling has a known layout in-memory so data can be copied to and from host + /// memory. + pub linear_tiling: ImageFeature, + /// A bitmask of the features supported when an image with optimal tiling is requested. + /// Optimal tiling is arranged however the GPU wants; its exact layout is undefined. + pub optimal_tiling: ImageFeature, + /// The features supported by buffers. + pub buffer_features: BufferFeature, +} + +bitflags!( + /// Image feature flags. + #[derive(Default)] + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + pub struct ImageFeature: u32 { + /// Image view can be sampled. + const SAMPLED = 0x1; + /// Image view can be used as storage image. + const STORAGE = 0x2; + /// Image view can be used as storage image (with atomics). + const STORAGE_ATOMIC = 0x4; + /// Image view can be used as color and input attachment. + const COLOR_ATTACHMENT = 0x80; + /// Image view can be used as color (with blending) and input attachment. + const COLOR_ATTACHMENT_BLEND = 0x100; + /// Image view can be used as depth-stencil and input attachment. + const DEPTH_STENCIL_ATTACHMENT = 0x200; + /// Image can be used as source for blit commands. + const BLIT_SRC = 0x400; + /// Image can be used as destination for blit commands. + const BLIT_DST = 0x800; + /// Image can be sampled with a (mipmap) linear sampler or as blit source + /// with linear sampling. + /// Requires `SAMPLED` or `BLIT_SRC` flag. + const SAMPLED_LINEAR = 0x1000; + } +); + +bitflags!( + /// Buffer feature flags. + #[derive(Default)] + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + pub struct BufferFeature: u32 { + /// Buffer view can be used as uniform texel buffer. + const UNIFORM_TEXEL = 0x8; + /// Buffer view can be used as storage texel buffer. + const STORAGE_TEXEL = 0x10; + /// Buffer view can be used as storage texel buffer (with atomics). + const STORAGE_TEXEL_ATOMIC = 0x20; + /// Image view can be used as vertex buffer. + const VERTEX = 0x40; + } +); + +/// Type of a surface channel. This is how we interpret the +/// storage allocated with `SurfaceType`. +#[repr(u8)] +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum ChannelType { + /// Unsigned normalized. + Unorm, + /// Signed normalized. + Snorm, + /// Unsigned integer. + Uint, + /// Signed integer. + Sint, + /// Unsigned floating-point. + Ufloat, + /// Signed floating-point. + Sfloat, + /// Unsigned scaled integer. + Uscaled, + /// Signed scaled integer. + Sscaled, + /// Unsigned normalized, SRGB non-linear encoded. + Srgb, +} + +macro_rules! surface_types { + { $($name:ident { $total:expr, $($aspect:ident)|*, $dim:expr $( ,$component:ident : $bits:expr )*} ,)* } => { + /// Type of the allocated texture surface. It is supposed to only + /// carry information about the number of bits per each channel. + /// The actual types are up to the views to decide and interpret. + /// The actual components are up to the swizzle to define. + #[repr(u8)] + #[allow(missing_docs, non_camel_case_types)] + #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + pub enum SurfaceType { + $( $name, )* + } + + impl SurfaceType { + /// Return the bits for this format. + pub fn describe_bits(&self) -> FormatBits { + match *self { + $( SurfaceType::$name => FormatBits { + $( $component: $bits, )* + .. BITS_ZERO + }, )* + } + } + + /// Return the format descriptor. + pub fn desc(&self) -> FormatDesc { + match *self { + $( SurfaceType::$name => FormatDesc { + bits: $total.min(!$total), + dim: $dim, + packed: $total > 0x1000, + aspects: $(Aspects::$aspect)|*, + }, )* + } + } + } + } +} + +// ident { num_bits, aspects, dim, (color, alpha, ..) } +// if the number of bits is given with exclamation (e.g. `!16`), the format is considered packed +surface_types! { + R4_G4 { !8, COLOR, (1, 1), color: 8 }, + R4_G4_B4_A4 { !16, COLOR, (1, 1), color: 12, alpha: 4 }, + B4_G4_R4_A4 { !16, COLOR, (1, 1), color: 12, alpha: 4 }, + R5_G6_B5 { !16, COLOR, (1, 1), color: 16 }, + B5_G6_R5 { !16, COLOR, (1, 1), color: 16 }, + R5_G5_B5_A1 { !16, COLOR, (1, 1), color: 15, alpha: 1 }, + B5_G5_R5_A1 { !16, COLOR, (1, 1), color: 15, alpha: 1 }, + A1_R5_G5_B5 { !16, COLOR, (1, 1), color: 15, alpha: 1 }, + R8 { 8, COLOR, (1, 1), color: 8 }, + R8_G8 { 16, COLOR, (1, 1), color: 16 }, + R8_G8_B8 { 24, COLOR, (1, 1), color: 24 }, + B8_G8_R8 { 24, COLOR, (1, 1), color: 24 }, + R8_G8_B8_A8 { 32, COLOR, (1, 1), color: 24, alpha: 8 }, + B8_G8_R8_A8 { 32, COLOR, (1, 1), color: 24, alpha: 8 }, + A8_B8_G8_R8 { !32, COLOR, (1, 1), color: 24, alpha: 8 }, + A2_R10_G10_B10 { !32, COLOR, (1, 1), color: 30, alpha: 2 }, + A2_B10_G10_R10 { !32, COLOR, (1, 1), color: 30, alpha: 2 }, + R16 { 16, COLOR, (1, 1), color: 16 }, + R16_G16 { 32, COLOR, (1, 1), color: 32 }, + R16_G16_B16 { 48, COLOR, (1, 1), color: 48 }, + R16_G16_B16_A16 { 64, COLOR, (1, 1), color: 48, alpha: 16 }, + R32 { 32, COLOR, (1, 1), color: 32 }, + R32_G32 { 64, COLOR, (1, 1), color: 64 }, + R32_G32_B32 { 96, COLOR, (1, 1), color: 96 }, + R32_G32_B32_A32 { 128, COLOR, (1, 1), color: 96, alpha: 32 }, + R64 { 64, COLOR, (1, 1), color: 64 }, + R64_G64 { 128, COLOR, (1, 1), color: 128 }, + R64_G64_B64 { 192, COLOR, (1, 1), color: 192 }, + R64_G64_B64_A64 { 256, COLOR, (1, 1), color: 192, alpha: 64 }, + B10_G11_R11 { !32, COLOR, (1, 1), color: 32 }, + E5_B9_G9_R9 { !32, COLOR, (1, 1), color: 27 }, + D16 { 16, DEPTH, (1, 1), depth: 16 }, + X8D24 { !32, DEPTH, (1, 1), depth: 24 }, + D32 { 32, DEPTH, (1, 1), depth: 32 }, + S8 { 8, STENCIL, (1, 1), stencil: 8 }, + D16_S8 { 24, DEPTH | STENCIL, (1, 1), depth: 16, stencil: 8 }, + D24_S8 { 32, DEPTH | STENCIL, (1, 1), depth: 24, stencil: 8 }, + D32_S8 { 40, DEPTH | STENCIL, (1, 1), depth: 32, stencil: 8 }, + BC1_RGB { 64, COLOR, (4, 4) }, + BC1_RGBA { 64, COLOR, (4, 4) }, + BC2 { 128, COLOR, (4, 4) }, + BC3 { 128, COLOR, (4, 4) }, + BC4 { 64, COLOR, (4, 4) }, + BC5 { 128, COLOR, (4, 4) }, + BC6 { 128, COLOR, (4, 4) }, + BC7 { 128, COLOR, (4, 4) }, + ETC2_R8_G8_B8 { 64, COLOR, (4, 4) }, + ETC2_R8_G8_B8_A1 { 64, COLOR, (4, 4) }, + ETC2_R8_G8_B8_A8 { 128, COLOR, (4, 4) }, + EAC_R11 { 64, COLOR, (4, 4) }, + EAC_R11_G11 { 128, COLOR, (4, 4) }, + ASTC_4x4 { 128, COLOR, (4, 4) }, + ASTC_5x4 { 128, COLOR, (5, 4) }, + ASTC_5x5 { 128, COLOR, (5, 5) }, + ASTC_6x5 { 128, COLOR, (6, 5) }, + ASTC_6x6 { 128, COLOR, (6, 6) }, + ASTC_8x5 { 128, COLOR, (8, 5) }, + ASTC_8x6 { 128, COLOR, (8, 6) }, + ASTC_8x8 { 128, COLOR, (8, 8) }, + ASTC_10x5 { 128, COLOR, (10, 5) }, + ASTC_10x6 { 128, COLOR, (10, 6) }, + ASTC_10x8 { 128, COLOR, (10, 8) }, + ASTC_10x10 { 128, COLOR, (10, 10) }, + ASTC_12x10 { 128, COLOR, (12, 10) }, + ASTC_12x12 { 128, COLOR, (12, 12) }, +} + +/// Generic run-time base format. +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct BaseFormat(pub SurfaceType, pub ChannelType); + +/// Conversion trait into `Format`; +pub trait AsFormat { + /// Associated format. + const SELF: Format; +} + +macro_rules! formats { + { + $name:ident = ($surface:ident, $channel:ident), + $($name_tail:ident = ($surface_tail:ident, $channel_tail:ident),)* + } => { + /// A format descriptor that describes the channels present in a + /// texture or view, how they are laid out, what size they are, + /// and how the elements of the channels are interpreted (integer, + /// float, etc.) + #[allow(missing_docs)] + #[repr(u32)] + #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + pub enum Format { + $name = 1, + $( $name_tail, )* + + // This serves as safety net for conversion from Vulkan -> HAL, + // in case Vulkan adds new formats: + // 1. We can check if a format is out of range + // 2. We 'ensure' that backend implementations do non-exhaustive matching + #[doc(hidden)] + __NumFormats, + } + + /// Number of formats. + pub const NUM_FORMATS: usize = Format::__NumFormats as _; + + /// Conversion table from `Format` to `BaseFormat`, excluding `Undefined`. + pub const BASE_FORMATS: [BaseFormat; NUM_FORMATS-1] = [ + BaseFormat(SurfaceType::$surface, ChannelType::$channel), + $(BaseFormat(SurfaceType::$surface_tail, ChannelType::$channel_tail), )* + ]; + + /// A struct equivalent to the matching `Format` enum member, which allows + /// an API to be strongly typed on particular formats. + #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + pub struct $name; + + impl AsFormat for $name { + const SELF: Format = Format::$name; + } + + $( + /// A struct equivalent to the matching `Format` enum member, which allows + /// an API to be strongly typed on particular formats. + #[allow(missing_docs)] + #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + pub struct $name_tail; + + impl AsFormat for $name_tail { + const SELF: Format = Format::$name_tail; + } + + )* + } +} + +// Format order has to match the order exposed by the Vulkan API. +formats! { + Rg4Unorm = (R4_G4, Unorm), + Rgba4Unorm = (R4_G4_B4_A4, Unorm), + Bgra4Unorm = (B4_G4_R4_A4, Unorm), + R5g6b5Unorm = (R5_G6_B5, Unorm), + B5g6r5Unorm = (B5_G6_R5, Unorm), + R5g5b5a1Unorm = (R5_G5_B5_A1, Unorm), + B5g5r5a1Unorm = (B5_G5_R5_A1, Unorm), + A1r5g5b5Unorm = (A1_R5_G5_B5, Unorm), + R8Unorm = (R8, Unorm), + R8Snorm = (R8, Snorm), + R8Uscaled = (R8, Uscaled), + R8Sscaled = (R8, Sscaled), + R8Uint = (R8, Uint), + R8Sint = (R8, Sint), + R8Srgb = (R8, Srgb), + Rg8Unorm = (R8_G8, Unorm), + Rg8Snorm = (R8_G8, Snorm), + Rg8Uscaled = (R8_G8, Uscaled), + Rg8Sscaled = (R8_G8, Sscaled), + Rg8Uint = (R8_G8, Uint), + Rg8Sint = (R8_G8, Sint), + Rg8Srgb = (R8_G8, Srgb), + Rgb8Unorm = (R8_G8_B8, Unorm), + Rgb8Snorm = (R8_G8_B8, Snorm), + Rgb8Uscaled = (R8_G8_B8, Uscaled), + Rgb8Sscaled = (R8_G8_B8, Sscaled), + Rgb8Uint = (R8_G8_B8, Uint), + Rgb8Sint = (R8_G8_B8, Sint), + Rgb8Srgb = (R8_G8_B8, Srgb), + Bgr8Unorm = (B8_G8_R8, Unorm), + Bgr8Snorm = (B8_G8_R8, Snorm), + Bgr8Uscaled = (B8_G8_R8, Uscaled), + Bgr8Sscaled = (B8_G8_R8, Sscaled), + Bgr8Uint = (B8_G8_R8, Uint), + Bgr8Sint = (B8_G8_R8, Sint), + Bgr8Srgb = (B8_G8_R8, Srgb), + Rgba8Unorm = (R8_G8_B8_A8, Unorm), + Rgba8Snorm = (R8_G8_B8_A8, Snorm), + Rgba8Uscaled = (R8_G8_B8_A8, Uscaled), + Rgba8Sscaled = (R8_G8_B8_A8, Sscaled), + Rgba8Uint = (R8_G8_B8_A8, Uint), + Rgba8Sint = (R8_G8_B8_A8, Sint), + Rgba8Srgb = (R8_G8_B8_A8, Srgb), + Bgra8Unorm = (B8_G8_R8_A8, Unorm), + Bgra8Snorm = (B8_G8_R8_A8, Snorm), + Bgra8Uscaled = (B8_G8_R8_A8, Uscaled), + Bgra8Sscaled = (B8_G8_R8_A8, Sscaled), + Bgra8Uint = (B8_G8_R8_A8, Uint), + Bgra8Sint = (B8_G8_R8_A8, Sint), + Bgra8Srgb = (B8_G8_R8_A8, Srgb), + Abgr8Unorm = (A8_B8_G8_R8, Unorm), + Abgr8Snorm = (A8_B8_G8_R8, Snorm), + Abgr8Uscaled = (A8_B8_G8_R8, Uscaled), + Abgr8Sscaled = (A8_B8_G8_R8, Sscaled), + Abgr8Uint = (A8_B8_G8_R8, Uint), + Abgr8Sint = (A8_B8_G8_R8, Sint), + Abgr8Srgb = (A8_B8_G8_R8, Srgb), + A2r10g10b10Unorm = (A2_R10_G10_B10, Unorm), + A2r10g10b10Snorm = (A2_R10_G10_B10, Snorm), + A2r10g10b10Uscaled = (A2_R10_G10_B10, Uscaled), + A2r10g10b10Sscaled = (A2_R10_G10_B10, Sscaled), + A2r10g10b10Uint = (A2_R10_G10_B10, Uint), + A2r10g10b10Sint = (A2_R10_G10_B10, Sint), + A2b10g10r10Unorm = (A2_B10_G10_R10, Unorm), + A2b10g10r10Snorm = (A2_B10_G10_R10, Snorm), + A2b10g10r10Uscaled = (A2_B10_G10_R10, Uscaled), + A2b10g10r10Sscaled = (A2_B10_G10_R10, Sscaled), + A2b10g10r10Uint = (A2_B10_G10_R10, Uint), + A2b10g10r10Sint = (A2_B10_G10_R10, Sint), + R16Unorm = (R16, Unorm), + R16Snorm = (R16, Snorm), + R16Uscaled = (R16, Uscaled), + R16Sscaled = (R16, Sscaled), + R16Uint = (R16, Uint), + R16Sint = (R16, Sint), + R16Sfloat = (R16, Sfloat), + Rg16Unorm = (R16_G16, Unorm), + Rg16Snorm = (R16_G16, Snorm), + Rg16Uscaled = (R16_G16, Uscaled), + Rg16Sscaled = (R16_G16, Sscaled), + Rg16Uint = (R16_G16, Uint), + Rg16Sint = (R16_G16, Sint), + Rg16Sfloat = (R16_G16, Sfloat), + Rgb16Unorm = (R16_G16_B16, Unorm), + Rgb16Snorm = (R16_G16_B16, Snorm), + Rgb16Uscaled = (R16_G16_B16, Uscaled), + Rgb16Sscaled = (R16_G16_B16, Sscaled), + Rgb16Uint = (R16_G16_B16, Uint), + Rgb16Sint = (R16_G16_B16, Sint), + Rgb16Sfloat = (R16_G16_B16, Sfloat), + Rgba16Unorm = (R16_G16_B16_A16, Unorm), + Rgba16Snorm = (R16_G16_B16_A16, Snorm), + Rgba16Uscaled = (R16_G16_B16_A16, Uscaled), + Rgba16Sscaled = (R16_G16_B16_A16, Sscaled), + Rgba16Uint = (R16_G16_B16_A16, Uint), + Rgba16Sint = (R16_G16_B16_A16, Sint), + Rgba16Sfloat = (R16_G16_B16_A16, Sfloat), + R32Uint = (R32, Uint), + R32Sint = (R32, Sint), + R32Sfloat = (R32, Sfloat), + Rg32Uint = (R32_G32, Uint), + Rg32Sint = (R32_G32, Sint), + Rg32Sfloat = (R32_G32, Sfloat), + Rgb32Uint = (R32_G32_B32, Uint), + Rgb32Sint = (R32_G32_B32, Sint), + Rgb32Sfloat = (R32_G32_B32, Sfloat), + Rgba32Uint = (R32_G32_B32_A32, Uint), + Rgba32Sint = (R32_G32_B32_A32, Sint), + Rgba32Sfloat = (R32_G32_B32_A32, Sfloat), + R64Uint = (R64, Uint), + R64Sint = (R64, Sint), + R64Sfloat = (R64, Sfloat), + Rg64Uint = (R64_G64, Uint), + Rg64Sint = (R64_G64, Sint), + Rg64Sfloat = (R64_G64, Sfloat), + Rgb64Uint = (R64_G64_B64, Uint), + Rgb64Sint = (R64_G64_B64, Sint), + Rgb64Sfloat = (R64_G64_B64, Sfloat), + Rgba64Uint = (R64_G64_B64_A64, Uint), + Rgba64Sint = (R64_G64_B64_A64, Sint), + Rgba64Sfloat = (R64_G64_B64_A64, Sfloat), + B10g11r11Ufloat = (B10_G11_R11, Ufloat), + E5b9g9r9Ufloat = (E5_B9_G9_R9, Ufloat), + D16Unorm = (D16, Unorm), + X8D24Unorm = (X8D24, Unorm), + D32Sfloat = (D32, Sfloat), + S8Uint = (S8, Uint), + D16UnormS8Uint = (D16_S8, Unorm), + D24UnormS8Uint = (D24_S8, Unorm), + D32SfloatS8Uint = (D32_S8, Sfloat), + Bc1RgbUnorm = (BC1_RGB, Unorm), + Bc1RgbSrgb = (BC1_RGB, Srgb), + Bc1RgbaUnorm = (BC1_RGBA, Unorm), + Bc1RgbaSrgb = (BC1_RGBA, Srgb), + Bc2Unorm = (BC2, Unorm), + Bc2Srgb = (BC2, Srgb), + Bc3Unorm = (BC3, Unorm), + Bc3Srgb = (BC3, Srgb), + Bc4Unorm = (BC4, Unorm), + Bc4Snorm = (BC4, Snorm), + Bc5Unorm = (BC5, Unorm), + Bc5Snorm = (BC5, Snorm), + Bc6hUfloat = (BC6, Ufloat), + Bc6hSfloat = (BC6, Sfloat), + Bc7Unorm = (BC7, Unorm), + Bc7Srgb = (BC7, Srgb), + Etc2R8g8b8Unorm = (ETC2_R8_G8_B8, Unorm), + Etc2R8g8b8Srgb = (ETC2_R8_G8_B8, Srgb), + Etc2R8g8b8a1Unorm = (ETC2_R8_G8_B8_A1, Unorm), + Etc2R8g8b8a1Srgb = (ETC2_R8_G8_B8_A1, Srgb), + Etc2R8g8b8a8Unorm = (ETC2_R8_G8_B8_A8, Unorm), + Etc2R8g8b8a8Srgb = (ETC2_R8_G8_B8_A8, Srgb), + EacR11Unorm = (EAC_R11, Unorm), + EacR11Snorm = (EAC_R11, Snorm), + EacR11g11Unorm = (EAC_R11_G11, Unorm), + EacR11g11Snorm = (EAC_R11_G11, Snorm), + Astc4x4Unorm = (ASTC_4x4, Unorm), + Astc4x4Srgb = (ASTC_4x4, Srgb), + Astc5x4Unorm = (ASTC_5x4, Unorm), + Astc5x4Srgb = (ASTC_5x4, Srgb), + Astc5x5Unorm = (ASTC_5x5, Unorm), + Astc5x5Srgb = (ASTC_5x5, Srgb), + Astc6x5Unorm = (ASTC_6x5, Unorm), + Astc6x5Srgb = (ASTC_6x5, Srgb), + Astc6x6Unorm = (ASTC_6x6, Unorm), + Astc6x6Srgb = (ASTC_6x6, Srgb), + Astc8x5Unorm = (ASTC_8x5, Unorm), + Astc8x5Srgb = (ASTC_8x5, Srgb), + Astc8x6Unorm = (ASTC_8x6, Unorm), + Astc8x6Srgb = (ASTC_8x6, Srgb), + Astc8x8Unorm = (ASTC_8x8, Unorm), + Astc8x8Srgb = (ASTC_8x8, Srgb), + Astc10x5Unorm = (ASTC_10x5, Unorm), + Astc10x5Srgb = (ASTC_10x5, Srgb), + Astc10x6Unorm = (ASTC_10x6, Unorm), + Astc10x6Srgb = (ASTC_10x6, Srgb), + Astc10x8Unorm = (ASTC_10x8, Unorm), + Astc10x8Srgb = (ASTC_10x8, Srgb), + Astc10x10Unorm = (ASTC_10x10, Unorm), + Astc10x10Srgb = (ASTC_10x10, Srgb), + Astc12x10Unorm = (ASTC_12x10, Unorm), + Astc12x10Srgb = (ASTC_12x10, Srgb), + Astc12x12Unorm = (ASTC_12x12, Unorm), + Astc12x12Srgb = (ASTC_12x12, Srgb), +} + +impl Format { + /// Get base format. + /// + /// Returns `None` if format is `Undefined`. + pub fn base_format(self) -> BaseFormat { + assert!(self as usize != 0 && NUM_FORMATS > self as usize); + BASE_FORMATS[self as usize - 1] + } + + /// A shortcut to obtain surface format description. + pub fn surface_desc(&self) -> FormatDesc { + self.base_format().0.desc() + } + + /// Returns if the format has a color aspect. + pub fn is_color(self) -> bool { + self.surface_desc().aspects.contains(Aspects::COLOR) + } + + /// Returns if the format has a depth aspect. + pub fn is_depth(self) -> bool { + self.surface_desc().aspects.contains(Aspects::DEPTH) + } + + /// Returns if the format has a stencil aspect. + pub fn is_stencil(self) -> bool { + self.surface_desc().aspects.contains(Aspects::STENCIL) + } +} + +// Common vertex attribute formats +impl AsFormat for f32 { + const SELF: Format = Format::R32Sfloat; +} +impl AsFormat for [f32; 2] { + const SELF: Format = Format::Rg32Sfloat; +} +impl AsFormat for [f32; 3] { + const SELF: Format = Format::Rgb32Sfloat; +} +impl AsFormat for [f32; 4] { + const SELF: Format = Format::Rgba32Sfloat; +} diff --git a/third_party/rust/gfx-hal/src/image.rs b/third_party/rust/gfx-hal/src/image.rs new file mode 100644 index 0000000000..16a3f90d7e --- /dev/null +++ b/third_party/rust/gfx-hal/src/image.rs @@ -0,0 +1,753 @@ +//! Image related structures. +//! +//! An image is a block of GPU memory representing a grid of texels. + +use crate::{ + buffer::Offset as RawOffset, + device, format, + pso::{Comparison, Rect}, +}; +use std::{f32, hash, ops::Range}; + +/// Dimension size. +pub type Size = u32; +/// Number of MSAA samples. +pub type NumSamples = u8; +/// Image layer. +pub type Layer = u16; +/// Image mipmap level. +pub type Level = u8; +/// Maximum accessible mipmap level of an image. +pub const MAX_LEVEL: Level = 15; +/// A texel coordinate in an image. +pub type TexelCoordinate = i32; + +/// Describes the size of an image, which may be up to three dimensional. +#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct Extent { + /// Image width + pub width: Size, + /// Image height + pub height: Size, + /// Image depth. + pub depth: Size, +} + +impl Extent { + /// Return true if one of the dimensions is zero. + pub fn is_empty(&self) -> bool { + self.width == 0 || self.height == 0 || self.depth == 0 + } + /// Get the extent at a particular mipmap level. + pub fn at_level(&self, level: Level) -> Self { + Extent { + width: 1.max(self.width >> level), + height: 1.max(self.height >> level), + depth: 1.max(self.depth >> level), + } + } + /// Get a rectangle for the full area of extent. + pub fn rect(&self) -> Rect { + Rect { + x: 0, + y: 0, + w: self.width as i16, + h: self.height as i16, + } + } +} + +/// An offset into an `Image` used for image-to-image +/// copy operations. All offsets are in texels, and +/// specifying offsets other than 0 for dimensions +/// that do not exist is undefined behavior -- for +/// example, specifying a `z` offset of `1` in a +/// two-dimensional image. +#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct Offset { + /// X offset. + pub x: TexelCoordinate, + /// Y offset. + pub y: TexelCoordinate, + /// Z offset. + pub z: TexelCoordinate, +} + +impl Offset { + /// Zero offset shortcut. + pub const ZERO: Self = Offset { x: 0, y: 0, z: 0 }; + + /// Convert the offset into 2-sided bounds given the extent. + pub fn into_bounds(self, extent: &Extent) -> Range { + let end = Offset { + x: self.x + extent.width as i32, + y: self.y + extent.height as i32, + z: self.z + extent.depth as i32, + }; + self..end + } +} + +/// Image tiling modes. +#[repr(u32)] +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum Tiling { + /// Optimal tiling for GPU memory access. Implementation-dependent. + Optimal = 0, + /// Optimal for CPU read/write. Texels are laid out in row-major order, + /// possibly with some padding on each row. + Linear = 1, +} + +/// Pure image object creation error. +#[derive(Clone, Debug, PartialEq)] +pub enum CreationError { + /// Out of either host or device memory. + OutOfMemory(device::OutOfMemory), + /// The format is not supported by the device. + Format(format::Format), + /// The kind doesn't support a particular operation. + Kind, + /// Failed to map a given multisampled kind to the device. + Samples(NumSamples), + /// Unsupported size in one of the dimensions. + Size(Size), + /// The given data has a different size than the target image slice. + Data(usize), + /// The mentioned usage mode is not supported + Usage(Usage), +} + +impl From for CreationError { + fn from(error: device::OutOfMemory) -> Self { + CreationError::OutOfMemory(error) + } +} + +impl std::fmt::Display for CreationError { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + CreationError::OutOfMemory(err) => write!(fmt, "Failed to create image: {}", err), + CreationError::Format(format) => write!(fmt, "Failed to create image: Unsupported format: {:?}", format), + CreationError::Kind => write!(fmt, "Failed to create image: Specified kind doesn't support particular operation"), // Room for improvement. + CreationError::Samples(samples) => write!(fmt, "Failed to create image: Specified format doesn't support specified sampling {}", samples), + CreationError::Size(size) => write!(fmt, "Failed to create image: Unsupported size in one of the dimensions {}", size), + CreationError::Data(data) => write!(fmt, "Failed to create image: The given data has a different size {{{}}} than the target image slice", data), // Actually nothing emits this. + CreationError::Usage(usage) => write!(fmt, "Failed to create image: Unsupported usage: {:?}", usage), + } + } +} + +impl std::error::Error for CreationError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + CreationError::OutOfMemory(err) => Some(err), + _ => None, + } + } +} + +/// Error creating an `ImageView`. +#[derive(Clone, Debug, PartialEq)] +pub enum ViewCreationError { + /// The required usage flag is not present in the image. + Usage(Usage), + /// Selected mip level doesn't exist. + Level(Level), + /// Selected array layer doesn't exist. + Layer(LayerError), + /// An incompatible format was requested for the view. + BadFormat(format::Format), + /// An incompatible view kind was requested for the view. + BadKind(ViewKind), + /// Out of either Host or Device memory + OutOfMemory(device::OutOfMemory), + /// The backend refused for some reason. + Unsupported, +} + +impl From for ViewCreationError { + fn from(error: device::OutOfMemory) -> Self { + ViewCreationError::OutOfMemory(error) + } +} + +impl std::fmt::Display for ViewCreationError { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ViewCreationError::Usage(usage) => write!(fmt, "Failed to create image view: Specified usage flags are not present in the image {:?}", usage), + ViewCreationError::Level(level) => write!(fmt, "Failed to create image view: Selected level doesn't exist in the image {}", level), + ViewCreationError::Layer(err) => write!(fmt, "Failed to create image view: {}", err), + ViewCreationError::BadFormat(format) => write!(fmt, "Failed to create image view: Incompatible format {:?}", format), + ViewCreationError::BadKind(kind) => write!(fmt, "Failed to create image view: Incompatible kind {:?}", kind), + ViewCreationError::OutOfMemory(err) => write!(fmt, "Failed to create image view: {}", err), + ViewCreationError::Unsupported => write!(fmt, "Failed to create image view: Implementation specific error occurred"), + } + } +} + +impl std::error::Error for ViewCreationError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + ViewCreationError::OutOfMemory(err) => Some(err), + _ => None, + } + } +} + +/// An error associated with selected image layer. +#[derive(Clone, Debug, PartialEq)] +pub enum LayerError { + /// The source image kind doesn't support array slices. + NotExpected(Kind), + /// Selected layers are outside of the provided range. + OutOfBounds, +} + +impl std::fmt::Display for LayerError { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + LayerError::NotExpected(kind) => { + write!(fmt, "Kind {{{:?}}} does not support arrays", kind) + } + LayerError::OutOfBounds => write!(fmt, "Out of bounds layers"), + } + } +} + +/// How to [filter](https://en.wikipedia.org/wiki/Texture_filtering) the +/// image when sampling. They correspond to increasing levels of quality, +/// but also cost. +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum Filter { + /// Selects a single texel from the current mip level and uses its value. + /// + /// Mip filtering selects the filtered value from one level. + Nearest, + /// Selects multiple texels and calculates the value via multivariate interpolation. + /// * 1D: Linear interpolation + /// * 2D/Cube: Bilinear interpolation + /// * 3D: Trilinear interpolation + Linear, +} + +/// The face of a cube image to do an operation on. +#[allow(missing_docs)] +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[repr(u8)] +pub enum CubeFace { + PosX, + NegX, + PosY, + NegY, + PosZ, + NegZ, +} + +/// A constant array of cube faces in the order they map to the hardware. +pub const CUBE_FACES: [CubeFace; 6] = [ + CubeFace::PosX, + CubeFace::NegX, + CubeFace::PosY, + CubeFace::NegY, + CubeFace::PosZ, + CubeFace::NegZ, +]; + +/// Specifies the dimensionality of an image to be allocated, +/// along with the number of mipmap layers and MSAA samples +/// if applicable. +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum Kind { + /// A single one-dimensional row of texels. + D1(Size, Layer), + /// Two-dimensional image. + D2(Size, Size, Layer, NumSamples), + /// Volumetric image. + D3(Size, Size, Size), +} + +impl Kind { + /// Get the image extent. + pub fn extent(&self) -> Extent { + match *self { + Kind::D1(width, _) => Extent { + width, + height: 1, + depth: 1, + }, + Kind::D2(width, height, _, _) => Extent { + width, + height, + depth: 1, + }, + Kind::D3(width, height, depth) => Extent { + width, + height, + depth, + }, + } + } + + /// Get the extent of a particular mipmap level. + pub fn level_extent(&self, level: Level) -> Extent { + use std::cmp::{max, min}; + // must be at least 1 + let map = |val| max(min(val, 1), val >> min(level, MAX_LEVEL)); + match *self { + Kind::D1(w, _) => Extent { + width: map(w), + height: 1, + depth: 1, + }, + Kind::D2(w, h, _, _) => Extent { + width: map(w), + height: map(h), + depth: 1, + }, + Kind::D3(w, h, d) => Extent { + width: map(w), + height: map(h), + depth: map(d), + }, + } + } + + /// Count the number of mipmap levels. + pub fn compute_num_levels(&self) -> Level { + use std::cmp::max; + match *self { + Kind::D2(_, _, _, s) if s > 1 => { + // anti-aliased images can't have mipmaps + 1 + } + _ => { + let extent = self.extent(); + let dominant = max(max(extent.width, extent.height), extent.depth); + (1..).find(|level| dominant >> level == 0).unwrap() + } + } + } + + /// Return the number of layers in an array type. + /// + /// Each cube face counts as separate layer. + pub fn num_layers(&self) -> Layer { + match *self { + Kind::D1(_, a) | Kind::D2(_, _, a, _) => a, + Kind::D3(..) => 1, + } + } + + /// Return the number of MSAA samples for the kind. + pub fn num_samples(&self) -> NumSamples { + match *self { + Kind::D1(..) => 1, + Kind::D2(_, _, _, s) => s, + Kind::D3(..) => 1, + } + } +} + +/// Specifies the kind/dimensionality of an image view. +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum ViewKind { + /// A single one-dimensional row of texels. + D1, + /// An array of rows of texels. Equivalent to `D2` except that texels + /// in different rows are not sampled, so filtering will be constrained + /// to a single row of texels at a time. + D1Array, + /// A traditional 2D image, with rows arranged contiguously. + D2, + /// An array of 2D images. Equivalent to `D3` except that texels in + /// a different depth level are not sampled. + D2Array, + /// A volume image, with each 2D layer arranged contiguously. + D3, + /// A set of 6 2D images, one for each face of a cube. + Cube, + /// An array of Cube images. + CubeArray, +} + +bitflags!( + /// Capabilities to create views into an image. + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + pub struct ViewCapabilities: u32 { + /// Support creation of views with different formats. + const MUTABLE_FORMAT = 0x0000_0008; + /// Support creation of `Cube` and `CubeArray` kinds of views. + const KIND_CUBE = 0x0000_0010; + /// Support creation of `D2Array` kind of view. + const KIND_2D_ARRAY = 0x0000_0020; + } +); + +bitflags!( + /// TODO: Find out if TRANSIENT_ATTACHMENT + INPUT_ATTACHMENT + /// are applicable on backends other than Vulkan. --AP + /// Image usage flags + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + pub struct Usage: u32 { + /// The image is used as a transfer source. + const TRANSFER_SRC = 0x1; + /// The image is used as a transfer destination. + const TRANSFER_DST = 0x2; + /// The image is a [sampled image](https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#descriptorsets-sampledimage) + const SAMPLED = 0x4; + /// The image is a [storage image](https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#descriptorsets-storageimage) + const STORAGE = 0x8; + /// The image is used as a color attachment -- that is, color input to a rendering pass. + const COLOR_ATTACHMENT = 0x10; + /// The image is used as a depth attachment. + const DEPTH_STENCIL_ATTACHMENT = 0x20; + /// + const TRANSIENT_ATTACHMENT = 0x40; + /// + const INPUT_ATTACHMENT = 0x80; + + } +); + +impl Usage { + /// Returns true if this image can be used in transfer operations. + pub fn can_transfer(&self) -> bool { + self.intersects(Usage::TRANSFER_SRC | Usage::TRANSFER_DST) + } + + /// Returns true if this image can be used as a target. + pub fn can_target(&self) -> bool { + self.intersects(Usage::COLOR_ATTACHMENT | Usage::DEPTH_STENCIL_ATTACHMENT) + } +} + +/// Specifies how image coordinates outside the range `[0, 1]` are handled. +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum WrapMode { + /// Tile the image, that is, sample the coordinate modulo `1.0`, so + /// addressing the image beyond an edge will "wrap" back from the + /// other edge. + Tile, + /// Mirror the image. Like tile, but uses abs(coord) before the modulo. + Mirror, + /// Clamp the image to the value at `0.0` or `1.0` respectively. + Clamp, + /// Use border color. + Border, + /// Mirror once and clamp to edge otherwise. + /// + /// Only valid if `Features::SAMPLER_MIRROR_CLAMP_EDGE` is enabled. + MirrorClamp, +} + +/// A wrapper for the LOD level of an image. Needed so that we can +/// implement Eq and Hash for it. +#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct Lod(pub f32); + +impl Lod { + /// Possible LOD range. + pub const RANGE: Range = Lod(f32::MIN)..Lod(f32::MAX); +} + +impl Eq for Lod {} +impl hash::Hash for Lod { + fn hash(&self, state: &mut H) { + self.0.to_bits().hash(state) + } +} + +/// A wrapper for an RGBA color with 8 bits per texel, encoded as a u32. +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct PackedColor(pub u32); + +impl From<[f32; 4]> for PackedColor { + fn from(c: [f32; 4]) -> PackedColor { + PackedColor( + c.iter() + .rev() + .fold(0, |u, &c| (u << 8) + (c * 255.0) as u32), + ) + } +} + +impl Into<[f32; 4]> for PackedColor { + fn into(self) -> [f32; 4] { + let mut out = [0.0; 4]; + for (i, channel) in out.iter_mut().enumerate() { + let byte = (self.0 >> (i << 3)) & 0xFF; + *channel = byte as f32 / 255.0; + } + out + } +} + +/// The border color for `WrapMode::Border` wrap mode. +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum BorderColor { + /// + TransparentBlack, + /// + OpaqueBlack, + /// + OpaqueWhite, +} + +impl Into<[f32; 4]> for BorderColor { + fn into(self) -> [f32; 4] { + match self { + BorderColor::TransparentBlack => [0.0, 0.0, 0.0, 0.0], + BorderColor::OpaqueBlack => [0.0, 0.0, 0.0, 1.0], + BorderColor::OpaqueWhite => [1.0, 1.0, 1.0, 1.0], + } + } +} + +/// Specifies how to sample from an image. These are all the parameters +/// available that alter how the GPU goes from a coordinate in an image +/// to producing an actual value from the texture, including filtering/ +/// scaling, wrap mode, etc. +// TODO: document the details of sampling. +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct SamplerDesc { + /// Minification filter method to use. + pub min_filter: Filter, + /// Magnification filter method to use. + pub mag_filter: Filter, + /// Mip filter method to use. + pub mip_filter: Filter, + /// Wrapping mode for each of the U, V, and W axis (S, T, and R in OpenGL + /// speak). + pub wrap_mode: (WrapMode, WrapMode, WrapMode), + /// This bias is added to every computed mipmap level (N + lod_bias). For + /// example, if it would select mipmap level 2 and lod_bias is 1, it will + /// use mipmap level 3. + pub lod_bias: Lod, + /// This range is used to clamp LOD level used for sampling. + pub lod_range: Range, + /// Comparison mode, used primary for a shadow map. + pub comparison: Option, + /// Border color is used when one of the wrap modes is set to border. + pub border: BorderColor, + /// Specifies whether the texture coordinates are normalized. + pub normalized: bool, + /// Anisotropic filtering. + /// + /// Can be `Some(_)` only if `Features::SAMPLER_ANISOTROPY` is enabled. + pub anisotropy_clamp: Option, +} + +impl SamplerDesc { + /// Create a new sampler description with a given filter method for all filtering operations + /// and a wrapping mode, using no LOD modifications. + pub fn new(filter: Filter, wrap: WrapMode) -> Self { + SamplerDesc { + min_filter: filter, + mag_filter: filter, + mip_filter: filter, + wrap_mode: (wrap, wrap, wrap), + lod_bias: Lod(0.0), + lod_range: Lod::RANGE.clone(), + comparison: None, + border: BorderColor::TransparentBlack, + normalized: true, + anisotropy_clamp: None, + } + } +} + +/// Specifies options for how memory for an image is arranged. +/// These are hints to the GPU driver and may or may not have actual +/// performance effects, but describe constraints on how the data +/// may be used that a program *must* obey. They do not specify +/// how channel values or such are laid out in memory; the actual +/// image data is considered opaque. +/// +/// Details may be found in [the Vulkan spec](https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#resources-image-layouts) +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum Layout { + /// General purpose, no restrictions on usage. + General, + /// Must only be used as a color attachment in a framebuffer. + ColorAttachmentOptimal, + /// Must only be used as a depth attachment in a framebuffer. + DepthStencilAttachmentOptimal, + /// Must only be used as a depth attachment in a framebuffer, + /// or as a read-only depth or stencil buffer in a shader. + DepthStencilReadOnlyOptimal, + /// Must only be used as a read-only image in a shader. + ShaderReadOnlyOptimal, + /// Must only be used as the source for a transfer command. + TransferSrcOptimal, + /// Must only be used as the destination for a transfer command. + TransferDstOptimal, + /// No layout, does not support device access. Only valid as a + /// source layout when transforming data to a specific destination + /// layout or initializing data. Does NOT guarentee that the contents + /// of the source buffer are preserved. + Undefined, + /// Like `Undefined`, but does guarentee that the contents of the source + /// buffer are preserved. + Preinitialized, + /// The layout that an image must be in to be presented to the display. + Present, +} + +impl Default for Layout { + fn default() -> Self { + Self::General + } +} + +bitflags!( + /// Bitflags to describe how memory in an image or buffer can be accessed. + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + pub struct Access: u32 { + /// Read access to an input attachment from within a fragment shader. + const INPUT_ATTACHMENT_READ = 0x10; + /// Read-only state for SRV access, or combine with `SHADER_WRITE` to have r/w access to UAV. + const SHADER_READ = 0x20; + /// Writeable state for UAV access. + /// Combine with `SHADER_READ` to have r/w access to UAV. + const SHADER_WRITE = 0x40; + /// Read state but can only be combined with `COLOR_ATTACHMENT_WRITE`. + const COLOR_ATTACHMENT_READ = 0x80; + /// Write-only state but can be combined with `COLOR_ATTACHMENT_READ`. + const COLOR_ATTACHMENT_WRITE = 0x100; + /// Read access to a depth/stencil attachment in a depth or stencil operation. + const DEPTH_STENCIL_ATTACHMENT_READ = 0x200; + /// Write access to a depth/stencil attachment in a depth or stencil operation. + const DEPTH_STENCIL_ATTACHMENT_WRITE = 0x400; + /// Read access to the buffer in a copy operation. + const TRANSFER_READ = 0x800; + /// Write access to the buffer in a copy operation. + const TRANSFER_WRITE = 0x1000; + /// Read access for raw memory to be accessed by the host system (ie, CPU). + const HOST_READ = 0x2000; + /// Write access for raw memory to be accessed by the host system. + const HOST_WRITE = 0x4000; + /// Read access for memory to be accessed by a non-specific entity. This may + /// be the host system, or it may be something undefined or specified by an + /// extension. + const MEMORY_READ = 0x8000; + /// Write access for memory to be accessed by a non-specific entity. + const MEMORY_WRITE = 0x10000; + } +); + +/// Image state, combining access methods and the image's layout. +pub type State = (Access, Layout); + +/// Selector of a concrete subresource in an image. +#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct Subresource { + /// Included aspects: color/depth/stencil + pub aspects: format::Aspects, + /// Selected mipmap level + pub level: Level, + /// Selected array level + pub layer: Layer, +} + +/// A subset of resource layers contained within an image's level. +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct SubresourceLayers { + /// Included aspects: color/depth/stencil + pub aspects: format::Aspects, + /// Selected mipmap level + pub level: Level, + /// Included array levels + pub layers: Range, +} + +/// A subset of resources contained within an image. +#[derive(Clone, Debug, Default, Hash, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct SubresourceRange { + /// Included aspects: color/depth/stencil + pub aspects: format::Aspects, + /// First mipmap level in this subresource + pub level_start: Level, + /// Number of sequential levels in this subresource. + /// + /// A value of `None` indicates the subresource contains + /// all of the remaining levels. + pub level_count: Option, + /// First layer in this subresource + pub layer_start: Layer, + /// Number of sequential layers in this subresource. + /// + /// A value of `None` indicates the subresource contains + /// all of the remaining layers. + pub layer_count: Option, +} + +impl From for SubresourceRange { + fn from(sub: SubresourceLayers) -> Self { + SubresourceRange { + aspects: sub.aspects, + level_start: sub.level, + level_count: Some(1), + layer_start: sub.layers.start, + layer_count: Some(sub.layers.end - sub.layers.start), + } + } +} + +impl SubresourceRange { + /// Resolve the concrete level count based on the total number of layers in an image. + pub fn resolve_level_count(&self, total: Level) -> Level { + self.level_count.unwrap_or(total - self.level_start) + } + + /// Resolve the concrete layer count based on the total number of layer in an image. + pub fn resolve_layer_count(&self, total: Layer) -> Layer { + self.layer_count.unwrap_or(total - self.layer_start) + } +} + +/// Image format properties. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct FormatProperties { + /// Maximum extent. + pub max_extent: Extent, + /// Max number of mipmap levels. + pub max_levels: Level, + /// Max number of array layers. + pub max_layers: Layer, + /// Bit mask of supported sample counts. + pub sample_count_mask: NumSamples, + /// Maximum size of the resource in bytes. + pub max_resource_size: usize, +} + +/// Footprint of a subresource in memory. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct SubresourceFootprint { + /// Byte slice occupied by the subresource. + pub slice: Range, + /// Byte distance between rows. + pub row_pitch: RawOffset, + /// Byte distance between array layers. + pub array_pitch: RawOffset, + /// Byte distance between depth slices. + pub depth_pitch: RawOffset, +} diff --git a/third_party/rust/gfx-hal/src/lib.rs b/third_party/rust/gfx-hal/src/lib.rs new file mode 100644 index 0000000000..eb5948c054 --- /dev/null +++ b/third_party/rust/gfx-hal/src/lib.rs @@ -0,0 +1,643 @@ +#![warn( + trivial_casts, + trivial_numeric_casts, + unused_extern_crates, + unused_import_braces, + unused_qualifications +)] +#![deny( + broken_intra_doc_links, + missing_debug_implementations, + missing_docs, + unused +)] + +//! Low-level graphics abstraction for Rust. Mostly operates on data, not types. +//! Designed for use by libraries and higher-level abstractions only. +//! +//! This crate provides a [hardware abstraction layer][hal] for [graphics adapters][gpus], +//! for both compute and graphics operations. The API design is heavily inspired by +//! the [Vulkan API](https://www.khronos.org/vulkan/), and borrows some of the terminology. +//! +//! [hal]: https://en.wikipedia.org/wiki/Hardware_abstraction +//! [gpus]: https://en.wikipedia.org/wiki/Video_card +//! +//! # Usage +//! +//! Most of the functionality is implemented in separate crates, one for each backend. +//! This crate only exposes a few generic traits and structures. You can import +//! all the necessary traits through the [`prelude`][prelude] module. +//! +//! The first step to using `gfx-hal` is to initialize one of the available +//! [backends][Backend], by creating an [`Instance`][Instance]. Then proceed by +//! [enumerating][Instance::enumerate_adapters] the available +//! [graphics adapters][adapter::Adapter] and querying their available +//! [features][Features] and [queues][queue::family::QueueFamily]. +//! +//! You can use the [`open`][adapter::PhysicalDevice::open] method on a +//! [`PhysicalDevice`][adapter::PhysicalDevice] to get a [logical device +//! handle][device::Device], from which you can manage all the other device-specific +//! resources. + +#[macro_use] +extern crate bitflags; + +#[cfg(feature = "serde")] +#[macro_use] +extern crate serde; + +use std::any::Any; +use std::fmt; +use std::hash::Hash; + +pub mod adapter; +pub mod buffer; +pub mod command; +pub mod device; +pub mod format; +pub mod image; +pub mod memory; +pub mod pass; +pub mod pool; +pub mod pso; +pub mod query; +pub mod queue; +pub mod window; + +/// Prelude module re-exports all the traits necessary to use `gfx-hal`. +pub mod prelude { + pub use crate::{ + adapter::PhysicalDevice, + command::CommandBuffer, + device::Device, + pool::CommandPool, + pso::DescriptorPool, + queue::{CommandQueue, QueueFamily}, + window::{PresentationSurface, Surface}, + Instance, + }; +} + +/// Draw vertex count. +pub type VertexCount = u32; +/// Draw vertex base offset. +pub type VertexOffset = i32; +/// Draw number of indices. +pub type IndexCount = u32; +/// Draw number of instances. +pub type InstanceCount = u32; +/// Indirect draw calls count. +pub type DrawCount = u32; +/// Number of work groups. +pub type WorkGroupCount = [u32; 3]; +/// Number of tasks. +pub type TaskCount = u32; + +bitflags! { + //TODO: add a feature for non-normalized samplers + //TODO: add a feature for mutable comparison samplers + /// Features that the device supports. + /// + /// These only include features of the core interface and not API extensions. + /// + /// Can be obtained from a [physical device][adapter::PhysicalDevice] by calling + /// [`features`][adapter::PhysicalDevice::features]. + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + pub struct Features: u128 { + /// Bit mask of Vulkan Core/Extension features. + const CORE_MASK = 0xFFFF_FFFF_FFFF_FFFF; + /// Bit mask of Vulkan Portability features. + const PORTABILITY_MASK = 0x0000_FFFF_0000_0000_0000_0000; + /// Bit mask for extra WebGPU features. + const WEBGPU_MASK = 0xFFFF_0000_0000_0000_0000_0000; + /// Bit mask for all extensions. + const EXTENSIONS_MASK = 0xFFFF_FFFF_0000_0000_0000_0000_0000_0000; + + /// Support for robust buffer access. + /// Buffer access by SPIR-V shaders is checked against the buffer/image boundaries. + const ROBUST_BUFFER_ACCESS = 0x0000_0000_0000_0001; + /// Support the full 32-bit range of indexed for draw calls. + /// If not supported, the maximum index value is determined by `Limits::max_draw_index_value`. + const FULL_DRAW_INDEX_U32 = 0x0000_0000_0000_0002; + /// Support cube array image views. + const IMAGE_CUBE_ARRAY = 0x0000_0000_0000_0004; + /// Support different color blending settings per attachments on graphics pipeline creation. + const INDEPENDENT_BLENDING = 0x0000_0000_0000_0008; + /// Support geometry shader. + const GEOMETRY_SHADER = 0x0000_0000_0000_0010; + /// Support tessellation shaders. + const TESSELLATION_SHADER = 0x0000_0000_0000_0020; + /// Support per-sample shading and multisample interpolation. + const SAMPLE_RATE_SHADING = 0x0000_0000_0000_0040; + /// Support dual source blending. + const DUAL_SRC_BLENDING = 0x0000_0000_0000_0080; + /// Support logic operations. + const LOGIC_OP = 0x0000_0000_0000_0100; + /// Support multiple draws per indirect call. + const MULTI_DRAW_INDIRECT = 0x0000_0000_0000_0200; + /// Support indirect drawing with first instance value. + /// If not supported the first instance value **must** be 0. + const DRAW_INDIRECT_FIRST_INSTANCE = 0x0000_0000_0000_0400; + /// Support depth clamping. + const DEPTH_CLAMP = 0x0000_0000_0000_0800; + /// Support depth bias clamping. + const DEPTH_BIAS_CLAMP = 0x0000_0000_0000_1000; + /// Support non-fill polygon modes. + const NON_FILL_POLYGON_MODE = 0x0000_0000_0000_2000; + /// Support depth bounds test. + const DEPTH_BOUNDS = 0x0000_0000_0000_4000; + /// Support lines with width other than 1.0. + const LINE_WIDTH = 0x0000_0000_0000_8000; + /// Support points with size greater than 1.0. + const POINT_SIZE = 0x0000_0000_0001_0000; + /// Support replacing alpha values with 1.0. + const ALPHA_TO_ONE = 0x0000_0000_0002_0000; + /// Support multiple viewports and scissors. + const MULTI_VIEWPORTS = 0x0000_0000_0004_0000; + /// Support anisotropic filtering. + const SAMPLER_ANISOTROPY = 0x0000_0000_0008_0000; + /// Support ETC2 texture compression formats. + const FORMAT_ETC2 = 0x0000_0000_0010_0000; + /// Support ASTC (LDR) texture compression formats. + const FORMAT_ASTC_LDR = 0x0000_0000_0020_0000; + /// Support BC texture compression formats. + const FORMAT_BC = 0x0000_0000_0040_0000; + /// Support precise occlusion queries, returning the actual number of samples. + /// If not supported, queries return a non-zero value when at least **one** sample passes. + const PRECISE_OCCLUSION_QUERY = 0x0000_0000_0080_0000; + /// Support query of pipeline statistics. + const PIPELINE_STATISTICS_QUERY = 0x0000_0000_0100_0000; + /// Support unordered access stores and atomic ops in the vertex, geometry + /// and tessellation shader stage. + /// If not supported, the shader resources **must** be annotated as read-only. + const VERTEX_STORES_AND_ATOMICS = 0x0000_0000_0200_0000; + /// Support unordered access stores and atomic ops in the fragment shader stage + /// If not supported, the shader resources **must** be annotated as read-only. + const FRAGMENT_STORES_AND_ATOMICS = 0x0000_0000_0400_0000; + /// + const SHADER_TESSELLATION_AND_GEOMETRY_POINT_SIZE = 0x0000_0000_0800_0000; + /// + const SHADER_IMAGE_GATHER_EXTENDED = 0x0000_0000_1000_0000; + /// + const SHADER_STORAGE_IMAGE_EXTENDED_FORMATS = 0x0000_0000_2000_0000; + /// + const SHADER_STORAGE_IMAGE_MULTISAMPLE = 0x0000_0000_4000_0000; + /// + const SHADER_STORAGE_IMAGE_READ_WITHOUT_FORMAT = 0x0000_0000_8000_0000; + /// + const SHADER_STORAGE_IMAGE_WRITE_WITHOUT_FORMAT = 0x0000_0001_0000_0000; + /// + const SHADER_UNIFORM_BUFFER_ARRAY_DYNAMIC_INDEXING = 0x0000_0002_0000_0000; + /// + const SHADER_SAMPLED_IMAGE_ARRAY_DYNAMIC_INDEXING = 0x0000_0004_0000_0000; + /// + const SHADER_STORAGE_BUFFER_ARRAY_DYNAMIC_INDEXING = 0x0000_0008_0000_0000; + /// + const SHADER_STORAGE_IMAGE_ARRAY_DYNAMIC_INDEXING = 0x0000_0010_0000_0000; + /// + const SHADER_CLIP_DISTANCE = 0x0000_0020_0000_0000; + /// + const SHADER_CULL_DISTANCE = 0x0000_0040_0000_0000; + /// + const SHADER_FLOAT64 = 0x0000_0080_0000_0000; + /// + const SHADER_INT64 = 0x0000_0100_0000_0000; + /// + const SHADER_INT16 = 0x0000_0200_0000_0000; + /// + const SHADER_RESOURCE_RESIDENCY = 0x0000_0400_0000_0000; + /// + const SHADER_RESOURCE_MIN_LOD = 0x0000_0800_0000_0000; + /// + const SPARSE_BINDING = 0x0000_1000_0000_0000; + /// + const SPARSE_RESIDENCY_BUFFER = 0x0000_2000_0000_0000; + /// + const SPARSE_RESIDENCY_IMAGE_2D = 0x0000_4000_0000_0000; + /// + const SPARSE_RESIDENCY_IMAGE_3D = 0x0000_8000_0000_0000; + /// + const SPARSE_RESIDENCY_2_SAMPLES = 0x0001_0000_0000_0000; + /// + const SPARSE_RESIDENCY_4_SAMPLES = 0x0002_0000_0000_0000; + /// + const SPARSE_RESIDENCY_8_SAMPLES = 0x0004_0000_0000_0000; + /// + const SPARSE_RESIDENCY_16_SAMPLES = 0x0008_0000_0000_0000; + /// + const SPARSE_RESIDENCY_ALIASED = 0x0010_0000_0000_0000; + /// + const VARIABLE_MULTISAMPLE_RATE = 0x0020_0000_0000_0000; + /// + const INHERITED_QUERIES = 0x0040_0000_0000_0000; + /// Support for arrays of texture descriptors + const TEXTURE_DESCRIPTOR_ARRAY = 0x0080_0000_0000_0000; + /// Support for + const SAMPLER_MIRROR_CLAMP_EDGE = 0x0100_0000_0000_0000; + /// Allow indexing sampled texture descriptor arrays with dynamically non-uniform data + const SAMPLED_TEXTURE_DESCRIPTOR_INDEXING = 0x0200_0000_0000_0000; + /// Allow indexing storage texture descriptor arrays with dynamically non-uniform data + const STORAGE_TEXTURE_DESCRIPTOR_INDEXING = 0x0400_0000_0000_0000; + /// Allow descriptor arrays to be unsized in shaders + const UNSIZED_DESCRIPTOR_ARRAY = 0x0800_0000_0000_0000; + /// Enable draw_indirect_count and draw_indexed_indirect_count + const DRAW_INDIRECT_COUNT = 0x1000_0000_0000_0000; + + /// Support triangle fan primitive topology. + const TRIANGLE_FAN = 0x0001 << 64; + /// Support separate stencil reference values for front and back sides. + const SEPARATE_STENCIL_REF_VALUES = 0x0002 << 64; + /// Support manually specified vertex attribute rates (divisors). + const INSTANCE_RATE = 0x0004 << 64; + /// Support non-zero mipmap bias on samplers. + const SAMPLER_MIP_LOD_BIAS = 0x0008 << 64; + /// Support sampler wrap mode that clamps to border. + const SAMPLER_BORDER_COLOR = 0x0010 << 64; + /// Can create comparison samplers in regular descriptor sets. + const MUTABLE_COMPARISON_SAMPLER = 0x0020 << 64; + + /// Make the NDC coordinate system pointing Y up, to match D3D and Metal. + const NDC_Y_UP = 0x0001 << 80; + + /// Supports task shader stage. + const TASK_SHADER = 0x0001 << 96; + /// Supports mesh shader stage. + const MESH_SHADER = 0x0002 << 96; + } +} + +bitflags! { + /// Features that the device supports natively, but is able to emulate. + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + pub struct Hints: u32 { + /// Support indexed, instanced drawing with base vertex and instance. + const BASE_VERTEX_INSTANCE_DRAWING = 0x0001; + } +} + +/// Resource limits of a particular graphics device. +#[derive(Clone, Copy, Debug, Default, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct Limits { + /// Maximum supported image 1D size. + pub max_image_1d_size: image::Size, + /// Maximum supported image 2D size. + pub max_image_2d_size: image::Size, + /// Maximum supported image 3D size. + pub max_image_3d_size: image::Size, + /// Maximum supported image cube size. + pub max_image_cube_size: image::Size, + /// Maximum supporter image array size. + pub max_image_array_layers: image::Layer, + /// Maximum number of elements for the BufferView to see. + pub max_texel_elements: usize, + /// + pub max_uniform_buffer_range: buffer::Offset, + /// + pub max_storage_buffer_range: buffer::Offset, + /// + pub max_push_constants_size: usize, + /// + pub max_memory_allocation_count: usize, + /// + pub max_sampler_allocation_count: usize, + /// + pub max_bound_descriptor_sets: pso::DescriptorSetIndex, + /// + pub max_framebuffer_layers: usize, + /// + pub max_per_stage_descriptor_samplers: usize, + /// + pub max_per_stage_descriptor_uniform_buffers: usize, + /// + pub max_per_stage_descriptor_storage_buffers: usize, + /// + pub max_per_stage_descriptor_sampled_images: usize, + /// + pub max_per_stage_descriptor_storage_images: usize, + /// + pub max_per_stage_descriptor_input_attachments: usize, + /// + pub max_per_stage_resources: usize, + + /// + pub max_descriptor_set_samplers: usize, + /// + pub max_descriptor_set_uniform_buffers: usize, + /// + pub max_descriptor_set_uniform_buffers_dynamic: usize, + /// + pub max_descriptor_set_storage_buffers: usize, + /// + pub max_descriptor_set_storage_buffers_dynamic: usize, + /// + pub max_descriptor_set_sampled_images: usize, + /// + pub max_descriptor_set_storage_images: usize, + /// + pub max_descriptor_set_input_attachments: usize, + + /// Maximum number of vertex input attributes that can be specified for a graphics pipeline. + pub max_vertex_input_attributes: usize, + /// Maximum number of vertex buffers that can be specified for providing vertex attributes to a graphics pipeline. + pub max_vertex_input_bindings: usize, + /// Maximum vertex input attribute offset that can be added to the vertex input binding stride. + pub max_vertex_input_attribute_offset: usize, + /// Maximum vertex input binding stride that can be specified in a vertex input binding. + pub max_vertex_input_binding_stride: usize, + /// Maximum number of components of output variables which can be output by a vertex shader. + pub max_vertex_output_components: usize, + + /// Maximum number of vertices for each patch. + pub max_patch_size: pso::PatchSize, + /// + pub max_geometry_shader_invocations: usize, + /// + pub max_geometry_input_components: usize, + /// + pub max_geometry_output_components: usize, + /// + pub max_geometry_output_vertices: usize, + /// + pub max_geometry_total_output_components: usize, + /// + pub max_fragment_input_components: usize, + /// + pub max_fragment_output_attachments: usize, + /// + pub max_fragment_dual_source_attachments: usize, + /// + pub max_fragment_combined_output_resources: usize, + + /// + pub max_compute_shared_memory_size: usize, + /// + pub max_compute_work_group_count: WorkGroupCount, + /// + pub max_compute_work_group_invocations: usize, + /// + pub max_compute_work_group_size: [u32; 3], + + /// + pub max_draw_indexed_index_value: IndexCount, + /// + pub max_draw_indirect_count: InstanceCount, + + /// + pub max_sampler_lod_bias: f32, + /// Maximum degree of sampler anisotropy. + pub max_sampler_anisotropy: f32, + + /// Maximum number of viewports. + pub max_viewports: usize, + /// + pub max_viewport_dimensions: [image::Size; 2], + /// + pub max_framebuffer_extent: image::Extent, + + /// + pub min_memory_map_alignment: usize, + /// + pub buffer_image_granularity: buffer::Offset, + /// The alignment of the start of buffer used as a texel buffer, in bytes, non-zero. + pub min_texel_buffer_offset_alignment: buffer::Offset, + /// The alignment of the start of buffer used for uniform buffer updates, in bytes, non-zero. + pub min_uniform_buffer_offset_alignment: buffer::Offset, + /// The alignment of the start of buffer used as a storage buffer, in bytes, non-zero. + pub min_storage_buffer_offset_alignment: buffer::Offset, + /// Number of samples supported for color attachments of framebuffers (floating/fixed point). + pub framebuffer_color_sample_counts: image::NumSamples, + /// Number of samples supported for depth attachments of framebuffers. + pub framebuffer_depth_sample_counts: image::NumSamples, + /// Number of samples supported for stencil attachments of framebuffers. + pub framebuffer_stencil_sample_counts: image::NumSamples, + /// Maximum number of color attachments that can be used by a subpass in a render pass. + pub max_color_attachments: usize, + /// + pub standard_sample_locations: bool, + /// The alignment of the start of the buffer used as a GPU copy source, in bytes, non-zero. + pub optimal_buffer_copy_offset_alignment: buffer::Offset, + /// The alignment of the row pitch of the texture data stored in a buffer that is + /// used in a GPU copy operation, in bytes, non-zero. + pub optimal_buffer_copy_pitch_alignment: buffer::Offset, + /// Size and alignment in bytes that bounds concurrent access to host-mapped device memory. + pub non_coherent_atom_size: usize, + + /// The alignment of the vertex buffer stride. + pub min_vertex_input_binding_stride_alignment: buffer::Offset, + + /// The maximum number of local workgroups that can be launched by a single draw mesh tasks command + pub max_draw_mesh_tasks_count: u32, + /// The maximum total number of task shader invocations in a single local workgroup. The product of the X, Y, and + /// Z sizes, as specified by the LocalSize execution mode in shader modules or by the object decorated by the + /// WorkgroupSize decoration, must be less than or equal to this limit. + pub max_task_work_group_invocations: u32, + /// The maximum size of a local task workgroup. These three values represent the maximum local workgroup size in + /// the X, Y, and Z dimensions, respectively. The x, y, and z sizes, as specified by the LocalSize execution mode + /// or by the object decorated by the WorkgroupSize decoration in shader modules, must be less than or equal to + /// the corresponding limit. + pub max_task_work_group_size: [u32; 3], + /// The maximum number of bytes that the task shader can use in total for shared and output memory combined. + pub max_task_total_memory_size: u32, + /// The maximum number of output tasks a single task shader workgroup can emit. + pub max_task_output_count: u32, + /// The maximum total number of mesh shader invocations in a single local workgroup. The product of the X, Y, and + /// Z sizes, as specified by the LocalSize execution mode in shader modules or by the object decorated by the + /// WorkgroupSize decoration, must be less than or equal to this limit. + pub max_mesh_work_group_invocations: u32, + /// The maximum size of a local mesh workgroup. These three values represent the maximum local workgroup size in + /// the X, Y, and Z dimensions, respectively. The x, y, and z sizes, as specified by the LocalSize execution mode + /// or by the object decorated by the WorkgroupSize decoration in shader modules, must be less than or equal to the + /// corresponding limit. + pub max_mesh_work_group_size: [u32; 3], + /// The maximum number of bytes that the mesh shader can use in total for shared and output memory combined. + pub max_mesh_total_memory_size: u32, + /// The maximum number of vertices a mesh shader output can store. + pub max_mesh_output_vertices: u32, + /// The maximum number of primitives a mesh shader output can store. + pub max_mesh_output_primitives: u32, + /// The maximum number of multi-view views a mesh shader can use. + pub max_mesh_multiview_view_count: u32, + /// The granularity with which mesh vertex outputs are allocated. The value can be used to compute the memory size + /// used by the mesh shader, which must be less than or equal to maxMeshTotalMemorySize. + pub mesh_output_per_vertex_granularity: u32, + /// The granularity with which mesh outputs qualified as per-primitive are allocated. The value can be used to + /// compute the memory size used by the mesh shader, which must be less than or equal to + pub mesh_output_per_primitive_granularity: u32, +} + +/// An enum describing the type of an index value in a slice's index buffer +#[allow(missing_docs)] +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[repr(u8)] +pub enum IndexType { + U16, + U32, +} + +/// Error creating an instance of a backend on the platform that +/// doesn't support this backend. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct UnsupportedBackend; + +impl fmt::Display for UnsupportedBackend { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "UnsupportedBackend") + } +} + +impl std::error::Error for UnsupportedBackend {} + +/// An instantiated backend. +/// +/// Any startup the backend needs to perform will be done when creating the type that implements +/// `Instance`. +/// +/// # Examples +/// +/// ```rust +/// # extern crate gfx_backend_empty; +/// # extern crate gfx_hal; +/// use gfx_backend_empty as backend; +/// use gfx_hal::Instance; +/// +/// // Create a concrete instance of our backend (this is backend-dependent and may be more +/// // complicated for some backends). +/// let instance = backend::Instance::create("My App", 1).unwrap(); +/// // We can get a list of the available adapters, which are either physical graphics +/// // devices, or virtual adapters. Because we are using the dummy `empty` backend, +/// // there will be nothing in this list. +/// for (idx, adapter) in instance.enumerate_adapters().iter().enumerate() { +/// println!("Adapter {}: {:?}", idx, adapter.info); +/// } +/// ``` +pub trait Instance: Any + Send + Sync + Sized { + /// Create a new instance. + /// + /// # Arguments + /// + /// * `name` - name of the application using the API. + /// * `version` - free form representation of the application's version. + /// + /// This metadata is passed further down the graphics stack. + /// + /// # Errors + /// + /// Returns an `Err` variant if the requested backend [is not supported + /// on the current platform][UnsupportedBackend]. + fn create(name: &str, version: u32) -> Result; + + /// Return all available [graphics adapters][adapter::Adapter]. + fn enumerate_adapters(&self) -> Vec>; + + /// Create a new [surface][window::Surface]. + /// + /// Surfaces can be used to render to windows. + /// + /// # Safety + /// + /// This method can cause undefined behavior if `raw_window_handle` isn't + /// a handle to a valid window for the current platform. + unsafe fn create_surface( + &self, + raw_window_handle: &impl raw_window_handle::HasRawWindowHandle, + ) -> Result; + + /// Destroy a surface, freeing the resources associated with it and + /// releasing it from this graphics API. + /// + /// # Safety + /// + unsafe fn destroy_surface(&self, surface: B::Surface); +} + +/// A strongly-typed index to a particular `MemoryType`. +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct MemoryTypeId(pub usize); + +impl From for MemoryTypeId { + fn from(id: usize) -> Self { + MemoryTypeId(id) + } +} + +struct PseudoVec(Option); + +impl Extend for PseudoVec { + fn extend>(&mut self, iter: I) { + let mut iter = iter.into_iter(); + self.0 = iter.next(); + assert!(iter.next().is_none()); + } +} + +/// Wraps together all the types needed for a graphics backend. +/// +/// Each backend module, such as OpenGL or Metal, will implement this trait +/// with its own concrete types. +pub trait Backend: 'static + Sized + Eq + Clone + Hash + fmt::Debug + Any + Send + Sync { + /// The corresponding [instance][Instance] type for this backend. + type Instance: Instance; + /// The corresponding [physical device][adapter::PhysicalDevice] type for this backend. + type PhysicalDevice: adapter::PhysicalDevice; + /// The corresponding [logical device][device::Device] type for this backend. + type Device: device::Device; + /// The corresponding [surface][window::PresentationSurface] type for this backend. + type Surface: window::PresentationSurface; + + /// The corresponding [queue family][queue::QueueFamily] type for this backend. + type QueueFamily: queue::QueueFamily; + /// The corresponding [command queue][queue::CommandQueue] type for this backend. + type CommandQueue: queue::CommandQueue; + /// The corresponding [command buffer][command::CommandBuffer] type for this backend. + type CommandBuffer: command::CommandBuffer; + + /// The corresponding shader module type for this backend. + type ShaderModule: fmt::Debug + Any + Send + Sync; + /// The corresponding render pass type for this backend. + type RenderPass: fmt::Debug + Any + Send + Sync; + /// The corresponding framebuffer type for this backend. + type Framebuffer: fmt::Debug + Any + Send + Sync; + + /// The corresponding memory type for this backend. + type Memory: fmt::Debug + Any + Send + Sync; + /// The corresponding [command pool][pool::CommandPool] type for this backend. + type CommandPool: pool::CommandPool; + + /// The corresponding buffer type for this backend. + type Buffer: fmt::Debug + Any + Send + Sync; + /// The corresponding buffer view type for this backend. + type BufferView: fmt::Debug + Any + Send + Sync; + /// The corresponding image type for this backend. + type Image: fmt::Debug + Any + Send + Sync; + /// The corresponding image view type for this backend. + type ImageView: fmt::Debug + Any + Send + Sync; + /// The corresponding sampler type for this backend. + type Sampler: fmt::Debug + Any + Send + Sync; + + /// The corresponding compute pipeline type for this backend. + type ComputePipeline: fmt::Debug + Any + Send + Sync; + /// The corresponding graphics pipeline type for this backend. + type GraphicsPipeline: fmt::Debug + Any + Send + Sync; + /// The corresponding pipeline cache type for this backend. + type PipelineCache: fmt::Debug + Any + Send + Sync; + /// The corresponding pipeline layout type for this backend. + type PipelineLayout: fmt::Debug + Any + Send + Sync; + /// The corresponding [descriptor pool][pso::DescriptorPool] type for this backend. + type DescriptorPool: pso::DescriptorPool; + /// The corresponding descriptor set type for this backend. + type DescriptorSet: fmt::Debug + Any + Send + Sync; + /// The corresponding descriptor set layout type for this backend. + type DescriptorSetLayout: fmt::Debug + Any + Send + Sync; + + /// The corresponding fence type for this backend. + type Fence: fmt::Debug + Any + Send + Sync; + /// The corresponding semaphore type for this backend. + type Semaphore: fmt::Debug + Any + Send + Sync; + /// The corresponding event type for this backend. + type Event: fmt::Debug + Any + Send + Sync; + /// The corresponding query pool type for this backend. + type QueryPool: fmt::Debug + Any + Send + Sync; +} diff --git a/third_party/rust/gfx-hal/src/memory.rs b/third_party/rust/gfx-hal/src/memory.rs new file mode 100644 index 0000000000..a4a200e90d --- /dev/null +++ b/third_party/rust/gfx-hal/src/memory.rs @@ -0,0 +1,130 @@ +//! Types to describe the properties of memory allocated for graphics resources. + +use crate::{buffer, image, queue, Backend}; +use std::ops::Range; + +bitflags!( + /// Memory property flags. + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + pub struct Properties: u16 { + /// Device local memory on the GPU. + const DEVICE_LOCAL = 0x1; + + /// Host visible memory can be accessed by the CPU. + /// + /// Backends must provide at least one cpu visible memory. + const CPU_VISIBLE = 0x2; + + /// CPU-GPU coherent. + /// + /// Non-coherent memory requires explicit flushing. + const COHERENT = 0x4; + + /// Cached memory by the CPU + const CPU_CACHED = 0x8; + + /// Memory that may be lazily allocated as needed on the GPU + /// and *must not* be visible to the CPU. + const LAZILY_ALLOCATED = 0x10; + } +); + +bitflags!( + /// Memory heap flags. + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + pub struct HeapFlags: u16 { + /// Device local memory on the GPU. + const DEVICE_LOCAL = 0x1; + } +); + +bitflags!( + /// Barrier dependency flags. + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + pub struct Dependencies: u32 { + /// Specifies the memory dependency to be framebuffer-local. + const BY_REGION = 0x1; + /// + const VIEW_LOCAL = 0x2; + /// + const DEVICE_GROUP = 0x4; + } +); + +// DOC TODO: Could be better, but I don't know how to do this without +// trying to explain the whole synchronization model. +/// A [memory barrier](https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#synchronization-memory-barriers) +/// type for either buffers or images. +#[allow(missing_docs)] +#[derive(Clone, Debug)] +pub enum Barrier<'a, B: Backend> { + /// Applies the given access flags to all buffers in the range. + AllBuffers(Range), + /// Applies the given access flags to all images in the range. + AllImages(Range), + /// A memory barrier that defines access to a buffer. + Buffer { + /// The access flags controlling the buffer. + states: Range, + /// The buffer the barrier controls. + target: &'a B::Buffer, + /// Subrange of the buffer the barrier applies to. + range: buffer::SubRange, + /// The source and destination Queue family IDs, for a [queue family ownership transfer](https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#synchronization-queue-transfers) + /// Can be `None` to indicate no ownership transfer. + families: Option>, + }, + /// A memory barrier that defines access to (a subset of) an image. + Image { + /// The access flags controlling the image. + states: Range, + /// The image the barrier controls. + target: &'a B::Image, + /// A `SubresourceRange` that defines which section of an image the barrier applies to. + range: image::SubresourceRange, + /// The source and destination Queue family IDs, for a [queue family ownership transfer](https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#synchronization-queue-transfers) + /// Can be `None` to indicate no ownership transfer. + families: Option>, + }, +} + +impl<'a, B: Backend> Barrier<'a, B> { + /// Create a barrier for the whole buffer between the given states. + pub fn whole_buffer(target: &'a B::Buffer, states: Range) -> Self { + Barrier::Buffer { + states, + target, + families: None, + range: buffer::SubRange::WHOLE, + } + } +} + +/// Memory requirements for a certain resource (buffer/image). +#[derive(Clone, Copy, Debug)] +pub struct Requirements { + /// Size in the memory. + pub size: u64, + /// Memory alignment. + pub alignment: u64, + /// Supported memory types. + pub type_mask: u32, +} + +/// A linear segment within a memory block. +#[derive(Clone, Debug, Default, Hash, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct Segment { + /// Offset to the segment. + pub offset: u64, + /// Size of the segment, or None if unbound. + pub size: Option, +} + +impl Segment { + /// All the memory available. + pub const ALL: Self = Segment { + offset: 0, + size: None, + }; +} diff --git a/third_party/rust/gfx-hal/src/pass.rs b/third_party/rust/gfx-hal/src/pass.rs new file mode 100644 index 0000000000..c6f2d9045b --- /dev/null +++ b/third_party/rust/gfx-hal/src/pass.rs @@ -0,0 +1,197 @@ +//! Render pass handling. +//! +//! A *render pass* represents a collection of +//! +//! - [attachments][crate::pass::Attachment] +//! - [subpasses][crate::pass::SubpassDesc] +//! - [dependencies][crate::pass::SubpassDependency] between the subpasses +//! +//! and describes how the attachments are used over the course of the subpasses. + +use crate::{format::Format, image, memory::Dependencies, pso::PipelineStage, Backend}; +use std::ops::Range; + +/// Specifies the operation to be used when reading data from a subpass attachment. +#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum AttachmentLoadOp { + /// Preserve existing content in the attachment. + Load, + /// Clear the attachment. + Clear, + /// Attachment content will be undefined. + DontCare, +} + +/// Specifies the operation to be used when writing data to a subpass attachment. +#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum AttachmentStoreOp { + /// Content written to the attachment will be preserved. + Store, + /// Attachment content will be undefined. + DontCare, +} + +/// Image layout of an attachment. +pub type AttachmentLayout = image::Layout; + +/// Attachment operations. +#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct AttachmentOps { + /// Indicates how the data of the attachment will be loaded at first usage at + /// the beginning of the subpass. + pub load: AttachmentLoadOp, + /// Whether or not data from the store operation will be preserved after the subpass. + pub store: AttachmentStoreOp, +} + +impl AttachmentOps { + /// Specifies `DontCare` for both load and store op. + pub const DONT_CARE: Self = AttachmentOps { + load: AttachmentLoadOp::DontCare, + store: AttachmentStoreOp::DontCare, + }; + + /// Specifies `Clear` for load op and `Store` for store op. + pub const INIT: Self = AttachmentOps { + load: AttachmentLoadOp::Clear, + store: AttachmentStoreOp::Store, + }; + + /// Specifies `Load` for load op and `Store` for store op. + pub const PRESERVE: Self = AttachmentOps { + load: AttachmentLoadOp::Load, + store: AttachmentStoreOp::Store, + }; + + /// Convenience function to create a new `AttachmentOps`. + pub fn new(load: AttachmentLoadOp, store: AttachmentStoreOp) -> Self { + AttachmentOps { load, store } + } + + /// A method to provide `AttachmentOps::DONT_CARE` to things that expect + /// a default function rather than a value. + #[cfg(feature = "serde")] + fn whatever() -> Self { + Self::DONT_CARE + } +} + +/// An attachment is a description of a resource provided to a render subpass. +/// +/// It includes things such as render targets, images that were produced from +/// previous subpasses, etc. +#[derive(Clone, Debug, Hash, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct Attachment { + /// Format of this attachment. + /// + /// In the most cases `format` is not `None`. It should be only used for + /// creating dummy renderpasses, which are used as placeholder for compatible + /// renderpasses. + pub format: Option, + /// Number of samples to use when loading from this attachment. + /// + /// If greater than 1, [multisampling](https://en.wikipedia.org/wiki/Multisample_anti-aliasing) + /// will take effect. + pub samples: image::NumSamples, + /// Load and store operations of the attachment. + pub ops: AttachmentOps, + /// Load and store operations of the stencil aspect, if any. + #[cfg_attr(feature = "serde", serde(default = "AttachmentOps::whatever"))] + pub stencil_ops: AttachmentOps, + /// Initial and final image layouts of the renderpass. + pub layouts: Range, +} + +impl Attachment { + /// Returns true if this attachment has some clear operations. + /// + /// Useful when starting a render pass, since there has to be a clear value provided. + pub fn has_clears(&self) -> bool { + self.ops.load == AttachmentLoadOp::Clear || self.stencil_ops.load == AttachmentLoadOp::Clear + } +} + +/// Index of an attachment within a framebuffer/renderpass, +pub type AttachmentId = usize; +/// Reference to an attachment by index and expected image layout. +pub type AttachmentRef = (AttachmentId, AttachmentLayout); +/// An AttachmentId that can be used instead of providing an attachment. +pub const ATTACHMENT_UNUSED: AttachmentId = !0; + +/// Index of a subpass. +pub type SubpassId = u8; + +/// Expresses a dependency between multiple [subpasses][SubpassDesc]. +/// +/// This is used both to describe a source or destination subpass; +/// data either explicitly passes from this subpass to the next or from another +/// subpass into this one. +#[derive(Clone, Debug, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct SubpassDependency { + /// Other subpasses this one depends on. + /// + /// If one of the range sides is `None`, it refers to the external + /// scope either before or after the whole render pass. + pub passes: Range>, + /// Other pipeline stages this subpass depends on. + pub stages: Range, + /// Resource accesses this subpass depends on. + pub accesses: Range, + /// Dependency flags. + pub flags: Dependencies, +} + +/// Description of a subpass for render pass creation. +#[derive(Clone, Debug)] +pub struct SubpassDesc<'a> { + /// Which attachments will be used as color buffers. + pub colors: &'a [AttachmentRef], + /// Which attachments will be used as depth/stencil buffers. + pub depth_stencil: Option<&'a AttachmentRef>, + /// Which attachments will be used as input attachments. + pub inputs: &'a [AttachmentRef], + /// Which attachments will be used as resolve destinations. + /// + /// The number of resolve attachments may be zero or equal to the number of color attachments. + /// + /// At the end of a subpass the color attachment will be resolved to the corresponding + /// resolve attachment. + /// + /// The resolve attachment must not be multisampled. + pub resolves: &'a [AttachmentRef], + /// Attachments that are not used by the subpass but must be preserved to be + /// passed on to subsequent passes. + pub preserves: &'a [AttachmentId], +} + +/// A sub-pass borrow of a pass. +#[derive(Debug)] +pub struct Subpass<'a, B: Backend> { + /// Index of the subpass + pub index: SubpassId, + /// Main pass borrow. + pub main_pass: &'a B::RenderPass, +} + +impl<'a, B: Backend> Clone for Subpass<'a, B> { + fn clone(&self) -> Self { + Subpass { + index: self.index, + main_pass: self.main_pass, + } + } +} + +impl<'a, B: Backend> PartialEq for Subpass<'a, B> { + fn eq(&self, other: &Self) -> bool { + self.index == other.index && std::ptr::eq(self.main_pass, other.main_pass) + } +} + +impl<'a, B: Backend> Copy for Subpass<'a, B> {} +impl<'a, B: Backend> Eq for Subpass<'a, B> {} diff --git a/third_party/rust/gfx-hal/src/pool.rs b/third_party/rust/gfx-hal/src/pool.rs new file mode 100644 index 0000000000..f194feb3a0 --- /dev/null +++ b/third_party/rust/gfx-hal/src/pool.rs @@ -0,0 +1,65 @@ +//! Command pools + +use crate::command::Level; +use crate::{Backend, PseudoVec}; + +use std::any::Any; +use std::fmt; + +bitflags!( + /// Command pool creation flags. + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + pub struct CommandPoolCreateFlags: u8 { + /// Indicates short-lived command buffers. + /// Memory optimization hint for implementations. + const TRANSIENT = 0x1; + /// Allow command buffers to be reset individually. + const RESET_INDIVIDUAL = 0x2; + } +); + +/// The allocated command buffers are associated with the creating command queue. +pub trait CommandPool: fmt::Debug + Any + Send + Sync { + /// Reset the command pool and the corresponding command buffers. + /// + /// # Arguments + /// + /// * `release_resources` - if `true`, this command pool will recycle all the + /// resources it own and give them back to the system. + /// + /// # Synchronization + /// + /// You may _not_ free the pool if a command buffer allocated from it + /// is still in use (pool memory still in use). + unsafe fn reset(&mut self, release_resources: bool); + + /// Allocate a single [command buffer][crate::command::CommandBuffer] from the pool. + /// + /// # Arguments + /// + /// * `level` - whether this command buffer is primary or secondary. + unsafe fn allocate_one(&mut self, level: Level) -> B::CommandBuffer { + let mut result = PseudoVec(None); + self.allocate(1, level, &mut result); + result.0.unwrap() + } + + /// Allocate new [command buffers][crate::command::CommandBuffer] from the pool. + /// + /// # Arguments + /// + /// * `num` - how many buffers to return + /// * `level` - whether to allocate primary or secondary command buffers. + /// * `list` - an extendable list of command buffers into which to allocate. + unsafe fn allocate(&mut self, num: usize, level: Level, list: &mut E) + where + E: Extend, + { + list.extend((0..num).map(|_| self.allocate_one(level))); + } + + /// Free [command buffers][crate::command::CommandBuffer] allocated from this pool. + unsafe fn free(&mut self, buffers: I) + where + I: IntoIterator; +} diff --git a/third_party/rust/gfx-hal/src/pso/compute.rs b/third_party/rust/gfx-hal/src/pso/compute.rs new file mode 100644 index 0000000000..8757e9a4e1 --- /dev/null +++ b/third_party/rust/gfx-hal/src/pso/compute.rs @@ -0,0 +1,31 @@ +//! Compute pipeline descriptor. + +use crate::{ + pso::{BasePipeline, EntryPoint, PipelineCreationFlags}, + Backend, +}; + +/// A description of the data needed to construct a compute pipeline. +#[derive(Debug)] +pub struct ComputePipelineDesc<'a, B: Backend> { + /// The shader entry point that performs the computation. + pub shader: EntryPoint<'a, B>, + /// Pipeline layout. + pub layout: &'a B::PipelineLayout, + /// Any flags necessary for the pipeline creation. + pub flags: PipelineCreationFlags, + /// The parent pipeline to this one, if any. + pub parent: BasePipeline<'a, B::ComputePipeline>, +} + +impl<'a, B: Backend> ComputePipelineDesc<'a, B> { + /// Create a new empty PSO descriptor. + pub fn new(shader: EntryPoint<'a, B>, layout: &'a B::PipelineLayout) -> Self { + ComputePipelineDesc { + shader, + layout, + flags: PipelineCreationFlags::empty(), + parent: BasePipeline::None, + } + } +} diff --git a/third_party/rust/gfx-hal/src/pso/descriptor.rs b/third_party/rust/gfx-hal/src/pso/descriptor.rs new file mode 100644 index 0000000000..c1d3a71d9e --- /dev/null +++ b/third_party/rust/gfx-hal/src/pso/descriptor.rs @@ -0,0 +1,316 @@ +//! Descriptor sets and layouts. +//! +//! A [`Descriptor`] is an object that describes the connection between a resource, such as +//! an `Image` or `Buffer`, and a variable in a shader. Descriptors are organized into +//! `DescriptorSet`s, each of which contains multiple descriptors that are bound and unbound to +//! shaders as a single unit. The contents of each descriptor in a set is defined by a +//! `DescriptorSetLayout` which is in turn built of [`DescriptorSetLayoutBinding`]s. A `DescriptorSet` +//! is then allocated from a [`DescriptorPool`] using the `DescriptorSetLayout`, and specific [`Descriptor`]s are +//! then bound to each binding point in the set using a [`DescriptorSetWrite`] and/or [`DescriptorSetCopy`]. +//! Each descriptor set may contain descriptors to multiple different sorts of resources, and a shader may +//! use multiple descriptor sets at a time. +//! +//! [`Descriptor`]: enum.Descriptor.html +//! [`DescriptorSetLayoutBinding`]: struct.DescriptorSetLayoutBinding.html +//! [`DescriptorPool`]: trait.DescriptorPool.html +//! [`DescriptorSetWrite`]: struct.DescriptorSetWrite.html +//! [`DescriptorSetCopy`]: struct.DescriptorSetWrite.html + +use std::{borrow::Borrow, fmt, iter}; + +use crate::{ + buffer::SubRange, device::OutOfMemory, image::Layout, pso::ShaderStageFlags, Backend, PseudoVec, +}; + +/// +pub type DescriptorSetIndex = u16; +/// +pub type DescriptorBinding = u32; +/// +pub type DescriptorArrayIndex = usize; + +/// Specific type of a buffer. +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum BufferDescriptorType { + /// Storage buffers allow load, store, and atomic operations. + Storage { + /// If true, store operations are not permitted on this buffer. + read_only: bool, + }, + /// Uniform buffers provide constant data to be accessed in a shader. + Uniform, +} + +/// Format of a buffer. +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum BufferDescriptorFormat { + /// The buffer is interpreted as a structure defined in a shader. + Structured { + /// If true, the buffer is accessed by an additional offset specified in + /// the `offsets` parameter of `CommandBuffer::bind_*_descriptor_sets`. + dynamic_offset: bool, + }, + /// The buffer is interpreted as a 1-D array of texels, which undergo format + /// conversion when loaded in a shader. + Texel, +} + +/// Specific type of an image descriptor. +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum ImageDescriptorType { + /// A sampled image allows sampling operations. + Sampled { + /// If true, this descriptor corresponds to both a sampled image and a + /// sampler to be used with that image. + with_sampler: bool, + }, + /// A storage image allows load, store and atomic operations. + Storage { + /// If true, store operations are not permitted on this image. + read_only: bool, + }, +} + +/// The type of a descriptor. +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum DescriptorType { + /// A descriptor associated with sampler. + Sampler, + /// A descriptor associated with an image. + Image { + /// The specific type of this image descriptor. + ty: ImageDescriptorType, + }, + /// A descriptor associated with a buffer. + Buffer { + /// The type of this buffer descriptor. + ty: BufferDescriptorType, + /// The format of this buffer descriptor. + format: BufferDescriptorFormat, + }, + /// A descriptor associated with an input attachment. + InputAttachment, +} + +/// Information about the contents of and in which stages descriptors may be bound to a descriptor +/// set at a certain binding point. Multiple `DescriptorSetLayoutBinding`s are assembled into +/// a `DescriptorSetLayout`, which is then allocated into a `DescriptorSet` using a +/// [`DescriptorPool`]. +/// +/// A descriptor set consists of multiple binding points. +/// Each binding point contains one or multiple descriptors of a certain type. +/// The binding point is only valid for the pipelines stages specified. +/// +/// The binding _must_ match with the corresponding shader interface. +/// +/// [`DescriptorPool`]: trait.DescriptorPool.html +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct DescriptorSetLayoutBinding { + /// Descriptor bindings range. + pub binding: DescriptorBinding, + /// Type of the bound descriptors. + pub ty: DescriptorType, + /// Number of descriptors in the array. + /// + /// *Note*: If count is zero, the binding point is reserved + /// and can't be accessed from any shader stages. + pub count: DescriptorArrayIndex, + /// Valid shader stages. + pub stage_flags: ShaderStageFlags, + /// Use the associated list of immutable samplers. + pub immutable_samplers: bool, +} + +/// Set of descriptors of a specific type. +#[derive(Clone, Copy, Debug)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct DescriptorRangeDesc { + /// Type of the stored descriptors. + pub ty: DescriptorType, + /// Amount of space. + pub count: usize, +} + +/// An error allocating descriptor sets from a pool. +#[derive(Clone, Debug, PartialEq)] +pub enum AllocationError { + /// OutOfMemory::Host: Memory allocation on the host side failed. + /// OutOfMemory::Device: Memory allocation on the device side failed. + /// This could be caused by a lack of memory or pool fragmentation. + OutOfMemory(OutOfMemory), + /// Memory allocation failed as there is not enough in the pool. + /// This could be caused by too many descriptor sets being created. + OutOfPoolMemory, + /// Memory allocation failed due to pool fragmentation. + FragmentedPool, + /// Descriptor set allocation failed as the layout is incompatible with the pool. + IncompatibleLayout, +} + +impl std::fmt::Display for AllocationError { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + AllocationError::OutOfMemory(OutOfMemory::Host) => { + write!(fmt, "Failed to allocate descriptor set: Out of host memory") + } + AllocationError::OutOfMemory(OutOfMemory::Device) => write!( + fmt, + "Failed to allocate descriptor set: Out of device memory" + ), + AllocationError::OutOfPoolMemory => { + write!(fmt, "Failed to allocate descriptor set: Out of pool memory") + } + AllocationError::FragmentedPool => { + write!(fmt, "Failed to allocate descriptor set: Pool is fragmented") + } + AllocationError::IncompatibleLayout => write!( + fmt, + "Failed to allocate descriptor set: Incompatible layout" + ), + } + } +} + +impl std::error::Error for AllocationError {} + +/// A descriptor pool is a collection of memory from which descriptor sets are allocated. +pub trait DescriptorPool: Send + Sync + fmt::Debug { + /// Allocate a descriptor set from the pool. + /// + /// The descriptor set will be allocated from the pool according to the corresponding set layout. However, + /// specific descriptors must still be written to the set before use using a [`DescriptorSetWrite`] or + /// [`DescriptorSetCopy`]. + /// + /// Descriptors will become invalid once the pool is reset. Usage of invalidated descriptor sets results + /// in undefined behavior. + /// + /// [`DescriptorSetWrite`]: struct.DescriptorSetWrite.html + /// [`DescriptorSetCopy`]: struct.DescriptorSetCopy.html + unsafe fn allocate_set( + &mut self, + layout: &B::DescriptorSetLayout, + ) -> Result { + let mut result = PseudoVec(None); + self.allocate(iter::once(layout), &mut result)?; + Ok(result.0.unwrap()) + } + + /// Allocate multiple descriptor sets from the pool. + /// + /// The descriptor set will be allocated from the pool according to the corresponding set layout. However, + /// specific descriptors must still be written to the set before use using a [`DescriptorSetWrite`] or + /// [`DescriptorSetCopy`]. + /// + /// Each descriptor set will be allocated from the pool according to the corresponding set layout. + /// Descriptors will become invalid once the pool is reset. Usage of invalidated descriptor sets results + /// in undefined behavior. + /// + /// [`DescriptorSetWrite`]: struct.DescriptorSetWrite.html + /// [`DescriptorSetCopy`]: struct.DescriptorSetCopy.html + unsafe fn allocate(&mut self, layouts: I, list: &mut E) -> Result<(), AllocationError> + where + I: IntoIterator, + I::Item: Borrow, + E: Extend, + { + for layout in layouts { + let set = self.allocate_set(layout.borrow())?; + list.extend(iter::once(set)); + } + Ok(()) + } + + /// Free the given descriptor sets provided as an iterator. + unsafe fn free(&mut self, descriptor_sets: I) + where + I: IntoIterator; + + /// Resets a descriptor pool, releasing all resources from all the descriptor sets + /// allocated from it and freeing the descriptor sets. Invalidates all descriptor + /// sets allocated from the pool; trying to use one after the pool has been reset + /// is undefined behavior. + unsafe fn reset(&mut self); +} + +/// Writes the actual descriptors to be bound into a descriptor set. +/// +/// Should be provided to the `write_descriptor_sets` method of a `Device`. +#[derive(Debug)] +pub struct DescriptorSetWrite<'a, B: Backend, WI> +where + WI: IntoIterator, + WI::Item: Borrow>, +{ + /// The descriptor set to modify. + pub set: &'a B::DescriptorSet, + /// Binding index to start writing at. + /// + /// *Note*: when there are more descriptors provided than + /// array elements left in the specified binding starting + /// at the specified offset, the updates are spilled onto + /// the next binding (starting with offset 0), and so on. + pub binding: DescriptorBinding, + /// Offset into the array to copy to. + pub array_offset: DescriptorArrayIndex, + /// Descriptors to write to the set. + pub descriptors: WI, +} + +/// A handle to a specific shader resource that can be bound for use in a `DescriptorSet`. +/// Usually provided in a [`DescriptorSetWrite`] +/// +/// [`DescriptorSetWrite`]: struct.DescriptorSetWrite.html +#[allow(missing_docs)] +#[derive(Clone, Debug)] +pub enum Descriptor<'a, B: Backend> { + Sampler(&'a B::Sampler), + Image(&'a B::ImageView, Layout), + CombinedImageSampler(&'a B::ImageView, Layout, &'a B::Sampler), + Buffer(&'a B::Buffer, SubRange), + TexelBuffer(&'a B::BufferView), +} + +/// Copies a range of descriptors to be bound from one descriptor set to another. +/// +/// Should be provided to the `copy_descriptor_sets` method of a `Device`. +#[derive(Clone, Copy, Debug)] +pub struct DescriptorSetCopy<'a, B: Backend> { + /// Descriptor set to copy from. + pub src_set: &'a B::DescriptorSet, + /// Binding to copy from. + /// + /// *Note*: when there are more descriptors required than + /// array elements left in the specified binding starting + /// at the specified offset, the updates are taken from + /// the next binding (starting with offset 0), and so on. + pub src_binding: DescriptorBinding, + /// Offset into the descriptor array to start copying from. + pub src_array_offset: DescriptorArrayIndex, + /// Descriptor set to copy to. + pub dst_set: &'a B::DescriptorSet, + /// Binding to copy to. + /// + /// *Note*: when there are more descriptors provided than + /// array elements left in the specified binding starting + /// at the specified offset, the updates are spilled onto + /// the next binding (starting with offset 0), and so on. + pub dst_binding: DescriptorBinding, + /// Offset into the descriptor array to copy to. + pub dst_array_offset: DescriptorArrayIndex, + /// How many descriptors to copy. + pub count: usize, +} + +bitflags! { + /// Descriptor pool creation flags. + pub struct DescriptorPoolCreateFlags: u32 { + /// Specifies that descriptor sets are allowed to be freed from the pool + /// individually. + const FREE_DESCRIPTOR_SET = 0x1; + } +} diff --git a/third_party/rust/gfx-hal/src/pso/graphics.rs b/third_party/rust/gfx-hal/src/pso/graphics.rs new file mode 100644 index 0000000000..b647b1d611 --- /dev/null +++ b/third_party/rust/gfx-hal/src/pso/graphics.rs @@ -0,0 +1,298 @@ +//! Graphics pipeline descriptor. + +use crate::{ + image, pass, + pso::{ + input_assembler::{AttributeDesc, InputAssemblerDesc, VertexBufferDesc}, + output_merger::{ColorBlendDesc, DepthStencilDesc, Face}, + BasePipeline, EntryPoint, PipelineCreationFlags, State, + }, + Backend, +}; + +use std::ops::Range; + +/// A simple struct describing a rect with integer coordinates. +#[derive(Clone, Copy, Debug, Hash, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct Rect { + /// X position. + pub x: i16, + /// Y position. + pub y: i16, + /// Width. + pub w: i16, + /// Height. + pub h: i16, +} + +/// A simple struct describing a rect with integer coordinates. +#[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct ClearRect { + /// 2D region. + pub rect: Rect, + /// Layer range. + pub layers: Range, +} + +/// A viewport, generally equating to a window on a display. +#[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct Viewport { + /// The viewport boundaries. + pub rect: Rect, + /// The viewport depth limits. + pub depth: Range, +} + +/// A single RGBA float color. +pub type ColorValue = [f32; 4]; +/// A single depth value from a depth buffer. +pub type DepthValue = f32; +/// A single value from a stencil buffer. +pub type StencilValue = u32; +/// Baked-in pipeline states. +#[derive(Clone, Debug, Default, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct BakedStates { + /// Static viewport. TODO: multiple viewports + pub viewport: Option, + /// Static scissor. TODO: multiple scissors + pub scissor: Option, + /// Static blend constant color. + pub blend_color: Option, + /// Static depth bounds. + pub depth_bounds: Option>, +} +#[derive(Debug)] +/// Primitive Assembler describes how input data are fetched in the pipeline and formed into primitives before being sent into the fragment shader. +pub enum PrimitiveAssemblerDesc<'a, B: Backend> { + /// Vertex based pipeline + Vertex { + /// Vertex buffers (IA) + buffers: &'a [VertexBufferDesc], + /// Vertex attributes (IA) + attributes: &'a [AttributeDesc], + /// Input assembler attributes, describes how + /// vertices are assembled into primitives (such as triangles). + input_assembler: InputAssemblerDesc, + /// A shader that outputs a vertex in a model. + vertex: EntryPoint<'a, B>, + /// Tesselation shaders consisting of: + /// + /// 1. Hull shader: takes in an input patch (values representing + /// a small portion of a shape, which may be actual geometry or may + /// be parameters for creating geometry) and produces one or more + /// output patches. + /// + /// 2. Domain shader: takes in domains produced from a hull shader's output + /// patches and computes actual vertex positions. + tessellation: Option<(EntryPoint<'a, B>, EntryPoint<'a, B>)>, + /// A shader that takes given input vertexes and outputs zero + /// or more output vertexes. + geometry: Option>, + }, + /// Mesh shading pipeline + Mesh { + /// A shader that creates a variable amount of mesh shader + /// invocations. + task: Option>, + /// A shader of which each workgroup emits zero or + /// more output primitives and the group of vertices and their + /// associated data required for each output primitive. + mesh: EntryPoint<'a, B>, + }, +} +/// A description of all the settings that can be altered +/// when creating a graphics pipeline. +#[derive(Debug)] +pub struct GraphicsPipelineDesc<'a, B: Backend> { + /// Primitive assembler + pub primitive_assembler: PrimitiveAssemblerDesc<'a, B>, + /// Rasterizer setup + pub rasterizer: Rasterizer, + /// A shader that outputs a value for a fragment. + /// Usually this value is a color that is then displayed as a + /// pixel on a screen. + /// + /// If a fragment shader is omitted, the results of fragment + /// processing are undefined. Specifically, any fragment color + /// outputs are considered to have undefined values, and the + /// fragment depth is considered to be unmodified. This can + /// be useful for depth-only rendering. + pub fragment: Option>, + /// Description of how blend operations should be performed. + pub blender: BlendDesc, + /// Depth stencil (DSV) + pub depth_stencil: DepthStencilDesc, + /// Multisampling. + pub multisampling: Option, + /// Static pipeline states. + pub baked_states: BakedStates, + /// Pipeline layout. + pub layout: &'a B::PipelineLayout, + /// Subpass in which the pipeline can be executed. + pub subpass: pass::Subpass<'a, B>, + /// Options that may be set to alter pipeline properties. + pub flags: PipelineCreationFlags, + /// The parent pipeline, which may be + /// `BasePipeline::None`. + pub parent: BasePipeline<'a, B::GraphicsPipeline>, +} + +impl<'a, B: Backend> GraphicsPipelineDesc<'a, B> { + /// Create a new empty PSO descriptor. + pub fn new( + primitive_assembler: PrimitiveAssemblerDesc<'a, B>, + rasterizer: Rasterizer, + fragment: Option>, + layout: &'a B::PipelineLayout, + subpass: pass::Subpass<'a, B>, + ) -> Self { + GraphicsPipelineDesc { + primitive_assembler, + rasterizer, + fragment, + blender: BlendDesc::default(), + depth_stencil: DepthStencilDesc::default(), + multisampling: None, + baked_states: BakedStates::default(), + layout, + subpass, + flags: PipelineCreationFlags::empty(), + parent: BasePipeline::None, + } + } +} + +/// Methods for rasterizing polygons, ie, turning the mesh +/// into a raster image. +#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum PolygonMode { + /// Rasterize as a point. + Point, + /// Rasterize as a line with the given width. + Line, + /// Rasterize as a face. + Fill, +} + +/// The front face winding order of a set of vertices. This is +/// the order of vertexes that define which side of a face is +/// the "front". +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum FrontFace { + /// Clockwise winding order. + Clockwise, + /// Counter-clockwise winding order. + CounterClockwise, +} + +/// A depth bias allows changing the produced depth values +/// for fragments slightly but consistently. This permits +/// drawing of multiple polygons in the same plane without +/// Z-fighting, such as when trying to draw shadows on a wall. +/// +/// For details of the algorithm and equations, see +/// [the Vulkan spec](https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#primsrast-depthbias). +#[derive(Copy, Clone, Debug, Default, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct DepthBias { + /// A constant depth value added to each fragment. + pub const_factor: f32, + /// The minimum or maximum depth bias of a fragment. + pub clamp: f32, + /// A constant bias applied to the fragment's slope. + pub slope_factor: f32, +} + +/// Rasterization state. +#[derive(Copy, Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct Rasterizer { + /// How to rasterize this primitive. + pub polygon_mode: PolygonMode, + /// Which face should be culled. + pub cull_face: Face, + /// Which vertex winding is considered to be the front face for culling. + pub front_face: FrontFace, + /// Whether or not to enable depth clamping; when enabled, instead of + /// fragments being omitted when they are outside the bounds of the z-plane, + /// they will be clamped to the min or max z value. + pub depth_clamping: bool, + /// What depth bias, if any, to use for the drawn primitives. + pub depth_bias: Option>, + /// Controls how triangles will be rasterized depending on their overlap with pixels. + pub conservative: bool, + /// Controls width of rasterized line segments. + pub line_width: State, +} + +impl Rasterizer { + /// Simple polygon-filling rasterizer state + pub const FILL: Self = Rasterizer { + polygon_mode: PolygonMode::Fill, + cull_face: Face::NONE, + front_face: FrontFace::CounterClockwise, + depth_clamping: false, + depth_bias: None, + conservative: false, + line_width: State::Static(1.0), + }; +} + +/// A description of an equation for how to blend transparent, overlapping fragments. +#[derive(Clone, Debug, Default, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct BlendDesc { + /// The logic operation to apply to the blending equation, if any. + pub logic_op: Option, + /// Which color targets to apply the blending operation to. + pub targets: Vec, +} + +/// Logic operations used for specifying blend equations. +#[derive(Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[allow(missing_docs)] +pub enum LogicOp { + Clear = 0, + And = 1, + AndReverse = 2, + Copy = 3, + AndInverted = 4, + NoOp = 5, + Xor = 6, + Or = 7, + Nor = 8, + Equivalent = 9, + Invert = 10, + OrReverse = 11, + CopyInverted = 12, + OrInverted = 13, + Nand = 14, + Set = 15, +} + +/// +pub type SampleMask = u64; + +/// +#[derive(Clone, Debug, PartialEq)] +pub struct Multisampling { + /// + pub rasterization_samples: image::NumSamples, + /// + pub sample_shading: Option, + /// + pub sample_mask: SampleMask, + /// Toggles alpha-to-coverage multisampling, which can produce nicer edges + /// when many partially-transparent polygons are overlapping. + /// See [here]( https://msdn.microsoft.com/en-us/library/windows/desktop/bb205072(v=vs.85).aspx#Alpha_To_Coverage) for a full description. + pub alpha_coverage: bool, + /// + pub alpha_to_one: bool, +} diff --git a/third_party/rust/gfx-hal/src/pso/input_assembler.rs b/third_party/rust/gfx-hal/src/pso/input_assembler.rs new file mode 100644 index 0000000000..55ba01bae7 --- /dev/null +++ b/third_party/rust/gfx-hal/src/pso/input_assembler.rs @@ -0,0 +1,145 @@ +//! Input Assembler (IA) stage description. +//! The input assembler collects raw vertex and index data. + +use crate::{format, IndexType}; + +/// Shader binding location. +pub type Location = u32; +/// Index of a vertex buffer. +pub type BufferIndex = u32; +/// Offset of an attribute from the start of the buffer, in bytes +pub type ElemOffset = u32; +/// Offset between attribute values, in bytes +pub type ElemStride = u32; +/// Number of instances between each advancement of the vertex buffer. +pub type InstanceRate = u8; +/// Number of vertices in a patch +pub type PatchSize = u8; + +/// The rate at which to advance input data to shaders for the given buffer +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum VertexInputRate { + /// Advance the buffer after every vertex + Vertex, + /// Advance the buffer after every instance + Instance(InstanceRate), +} + +impl VertexInputRate { + /// Get the numeric representation of the rate + pub fn as_uint(&self) -> u8 { + match *self { + VertexInputRate::Vertex => 0, + VertexInputRate::Instance(divisor) => divisor, + } + } +} + +/// A struct element descriptor. +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct Element { + /// Element format + pub format: F, + /// Offset from the beginning of the container, in bytes + pub offset: ElemOffset, +} + +/// Vertex buffer description. Notably, completely separate from resource `Descriptor`s +/// used in `DescriptorSet`s. +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct VertexBufferDesc { + /// Binding number of this vertex buffer. This binding number is + /// used only for vertex buffers, and is completely separate from + /// `Descriptor` and `DescriptorSet` bind points. + pub binding: BufferIndex, + /// Total container size, in bytes. + /// Specifies the byte distance between two consecutive elements. + pub stride: ElemStride, + /// The rate at which to advance data for the given buffer + /// + /// i.e. the rate at which data passed to shaders will get advanced by + /// `stride` bytes + pub rate: VertexInputRate, +} + +/// Vertex attribute description. Notably, completely separate from resource `Descriptor`s +/// used in `DescriptorSet`s. +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct AttributeDesc { + /// Attribute binding location in the shader. Attribute locations are + /// shared between all vertex buffers in a pipeline, meaning that even if the + /// data for this attribute comes from a different vertex buffer, it still cannot + /// share the same location with another attribute. + pub location: Location, + /// Binding number of the associated vertex buffer. + pub binding: BufferIndex, + /// Attribute element description. + pub element: Element, +} + +/// Describes the type of geometric primitives, +/// created from vertex data. +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[repr(u8)] +pub enum Primitive { + /// Each vertex represents a single point. + PointList, + /// Each pair of vertices represent a single line segment. For example, with `[a, b, c, d, + /// e]`, `a` and `b` form a line, `c` and `d` form a line, and `e` is discarded. + LineList, + /// Every two consecutive vertices represent a single line segment. Visually forms a "path" of + /// lines, as they are all connected. For example, with `[a, b, c]`, `a` and `b` form a line + /// line, and `b` and `c` form a line. + LineStrip, + /// Each triplet of vertices represent a single triangle. For example, with `[a, b, c, d, e]`, + /// `a`, `b`, and `c` form a triangle, `d` and `e` are discarded. + TriangleList, + /// Every three consecutive vertices represent a single triangle. For example, with `[a, b, c, + /// d]`, `a`, `b`, and `c` form a triangle, and `b`, `c`, and `d` form a triangle. + TriangleStrip, + /// Patch list, + /// used with shaders capable of producing primitives on their own (tessellation) + PatchList(PatchSize), +} + +/// All the information needed to create an input assembler. +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct InputAssemblerDesc { + /// Type of the primitive + pub primitive: Primitive, + /// When adjacency information is enabled, every even-numbered vertex + /// (every other starting from the first) represents an additional + /// vertex for the primitive, while odd-numbered vertices (every other starting from the + /// second) represent adjacent vertices. + /// + /// For example, with `[a, b, c, d, e, f, g, h]`, `[a, c, + /// e, g]` form a triangle strip, and `[b, d, f, h]` are the adjacent vertices, where `b`, `d`, + /// and `f` are adjacent to the first triangle in the strip, and `d`, `f`, and `h` are adjacent + /// to the second. + pub with_adjacency: bool, + /// Describes whether or not primitive restart is supported for + /// an input assembler. Primitive restart is a feature that + /// allows a mark to be placed in an index buffer where it is + /// is "broken" into multiple pieces of geometry. + /// + /// See + /// for more detail. + pub restart_index: Option, +} + +impl InputAssemblerDesc { + /// Create a new IA descriptor without primitive restart or adjucency. + pub fn new(primitive: Primitive) -> Self { + InputAssemblerDesc { + primitive, + with_adjacency: false, + restart_index: None, + } + } +} diff --git a/third_party/rust/gfx-hal/src/pso/mod.rs b/third_party/rust/gfx-hal/src/pso/mod.rs new file mode 100644 index 0000000000..981605baac --- /dev/null +++ b/third_party/rust/gfx-hal/src/pso/mod.rs @@ -0,0 +1,220 @@ +//! Raw Pipeline State Objects +//! +//! This module contains items used to create and manage Pipelines. + +use crate::{device, pass, Backend}; + +mod compute; +mod descriptor; +mod graphics; +mod input_assembler; +mod output_merger; +mod specialization; + +pub use self::{ + compute::*, descriptor::*, graphics::*, input_assembler::*, output_merger::*, specialization::*, +}; + +/// Error types happening upon PSO creation on the device side. +#[derive(Clone, Debug, PartialEq)] +pub enum CreationError { + /// Unknown other error. + Other, + /// Unsupported pipeline on hardware or implementation. Example: mesh shaders on DirectX 11. + UnsupportedPipeline, + /// Invalid subpass (not part of renderpass). + InvalidSubpass(pass::SubpassId), + /// Shader compilation error. + Shader(device::ShaderError), + /// Out of either host or device memory. + OutOfMemory(device::OutOfMemory), +} + +impl From for CreationError { + fn from(err: device::OutOfMemory) -> Self { + CreationError::OutOfMemory(err) + } +} + +impl std::fmt::Display for CreationError { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + CreationError::OutOfMemory(err) => write!(fmt, "Failed to create pipeline: {}", err), + CreationError::Other => write!(fmt, "Failed to create pipeline: Unsupported usage: Implementation specific error occurred"), + CreationError::UnsupportedPipeline => write!(fmt, "Failed to create pipeline: pipeline type is not supported"), + CreationError::InvalidSubpass(subpass) => write!(fmt, "Failed to create pipeline: Invalid subpass: {}", subpass), + CreationError::Shader(err) => write!(fmt, "Failed to create pipeline: {}", err), + } + } +} + +impl std::error::Error for CreationError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + CreationError::OutOfMemory(err) => Some(err), + CreationError::Shader(err) => Some(err), + CreationError::InvalidSubpass(_) => None, + CreationError::Other => None, + CreationError::UnsupportedPipeline => None, + } + } +} + +bitflags!( + /// Stages of the logical pipeline. + /// + /// The pipeline is structured by the ordering of the flags. + /// Some stages are queue type dependent. + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + pub struct PipelineStage: u32 { + /// Beginning of the command queue. + const TOP_OF_PIPE = 0x1; + /// Indirect data consumption. + const DRAW_INDIRECT = 0x2; + /// Vertex data consumption. + const VERTEX_INPUT = 0x4; + /// Vertex shader execution. + const VERTEX_SHADER = 0x8; + /// Hull shader execution. + const HULL_SHADER = 0x10; + /// Domain shader execution. + const DOMAIN_SHADER = 0x20; + /// Geometry shader execution. + const GEOMETRY_SHADER = 0x40; + /// Fragment shader execution. + const FRAGMENT_SHADER = 0x80; + /// Stage of early depth and stencil test. + const EARLY_FRAGMENT_TESTS = 0x100; + /// Stage of late depth and stencil test. + const LATE_FRAGMENT_TESTS = 0x200; + /// Stage of final color value calculation. + const COLOR_ATTACHMENT_OUTPUT = 0x400; + /// Compute shader execution, + const COMPUTE_SHADER = 0x800; + /// Copy/Transfer command execution. + const TRANSFER = 0x1000; + /// End of the command queue. + const BOTTOM_OF_PIPE = 0x2000; + /// Read/Write access from host. + /// (Not a real pipeline stage) + const HOST = 0x4000; + /// Task shader stage. + const TASK_SHADER = 0x80000; + /// Mesh shader stage. + const MESH_SHADER = 0x100000; + } +); + +bitflags!( + /// Combination of different shader pipeline stages. + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + #[derive(Default)] + pub struct ShaderStageFlags: u32 { + /// Vertex shader stage. + const VERTEX = 0x1; + /// Hull (tessellation) shader stage. + const HULL = 0x2; + /// Domain (tessellation) shader stage. + const DOMAIN = 0x4; + /// Geometry shader stage. + const GEOMETRY = 0x8; + /// Fragment shader stage. + const FRAGMENT = 0x10; + /// Compute shader stage. + const COMPUTE = 0x20; + /// Task shader stage. + const TASK = 0x40; + /// Mesh shader stage. + const MESH = 0x80; + /// All graphics pipeline shader stages. + const GRAPHICS = Self::VERTEX.bits | Self::HULL.bits | + Self::DOMAIN.bits | Self::GEOMETRY.bits | Self::FRAGMENT.bits; + /// All shader stages (matches Vulkan). + const ALL = 0x7FFFFFFF; + } +); + +/// Shader entry point. +#[derive(Debug)] +pub struct EntryPoint<'a, B: Backend> { + /// Entry point name. + pub entry: &'a str, + /// Reference to the shader module containing this entry point. + pub module: &'a B::ShaderModule, + /// Specialization constants to be used when creating the pipeline. + pub specialization: Specialization<'a>, +} + +impl<'a, B: Backend> Clone for EntryPoint<'a, B> { + fn clone(&self) -> Self { + EntryPoint { + entry: self.entry, + module: self.module, + specialization: self.specialization.clone(), + } + } +} + +bitflags!( + /// Pipeline creation flags. + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + pub struct PipelineCreationFlags: u32 { + /// Disable pipeline optimizations. + /// + /// May speedup pipeline creation. + const DISABLE_OPTIMIZATION = 0x1; + /// Allow derivatives (children) of the pipeline. + /// + /// Must be set when pipelines set the pipeline as base. + const ALLOW_DERIVATIVES = 0x2; + } +); + +/// A reference to a parent pipeline. The assumption is that +/// a parent and derivative/child pipeline have most settings +/// in common, and one may be switched for another more quickly +/// than entirely unrelated pipelines would be. +#[derive(Debug)] +pub enum BasePipeline<'a, P: 'a> { + /// Referencing an existing pipeline as parent. + Pipeline(&'a P), + /// A pipeline in the same create pipelines call. + /// + /// The index of the parent must be lower than the index of the child. + Index(usize), + /// No parent pipeline exists. + None, +} + +/// Pipeline state which may be static or dynamic. +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum State { + /// Static state that cannot be altered. + Static(T), + /// Dynamic state set through a command buffer. + Dynamic, +} + +impl State { + /// Returns the static value or a default. + pub fn static_or(self, default: T) -> T { + match self { + State::Static(v) => v, + State::Dynamic => default, + } + } + + /// Whether the state is static. + pub fn is_static(self) -> bool { + match self { + State::Static(_) => true, + State::Dynamic => false, + } + } + + /// Whether the state is dynamic. + pub fn is_dynamic(self) -> bool { + !self.is_static() + } +} diff --git a/third_party/rust/gfx-hal/src/pso/output_merger.rs b/third_party/rust/gfx-hal/src/pso/output_merger.rs new file mode 100644 index 0000000000..88fbc8a8b6 --- /dev/null +++ b/third_party/rust/gfx-hal/src/pso/output_merger.rs @@ -0,0 +1,359 @@ +//! Output Merger (OM) stage description. +//! The final stage in a pipeline that creates pixel colors from +//! the input shader results, depth/stencil information, etc. + +use crate::pso::{graphics::StencilValue, State}; + +/// A pixel-wise comparison function. +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum Comparison { + /// `false` + Never = 0, + /// `x < y` + Less = 1, + /// `x == y` + Equal = 2, + /// `x <= y` + LessEqual = 3, + /// `x > y` + Greater = 4, + /// `x != y` + NotEqual = 5, + /// `x >= y` + GreaterEqual = 6, + /// `true` + Always = 7, +} + +bitflags!( + /// Target output color mask. + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + pub struct ColorMask: u8 { + /// Red mask + const RED = 0x1; + /// Green mask + const GREEN = 0x2; + /// Blue mask + const BLUE = 0x4; + /// Alpha channel mask + const ALPHA = 0x8; + /// Mask for RGB channels + const COLOR = 0x7; + /// Mask all channels + const ALL = 0xF; + /// Mask no channels. + const NONE = 0x0; + } +); + +impl Default for ColorMask { + fn default() -> Self { + Self::ALL + } +} + +/// Defines the possible blending factors. +/// During blending, the source or destination fragment may be +/// multiplied by a factor to produce the final result. +#[allow(missing_docs)] +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum Factor { + Zero = 0, + One = 1, + SrcColor = 2, + OneMinusSrcColor = 3, + DstColor = 4, + OneMinusDstColor = 5, + SrcAlpha = 6, + OneMinusSrcAlpha = 7, + DstAlpha = 8, + OneMinusDstAlpha = 9, + ConstColor = 10, + OneMinusConstColor = 11, + ConstAlpha = 12, + OneMinusConstAlpha = 13, + SrcAlphaSaturate = 14, + Src1Color = 15, + OneMinusSrc1Color = 16, + Src1Alpha = 17, + OneMinusSrc1Alpha = 18, +} + +/// Blending operations. +#[allow(missing_docs)] +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum BlendOp { + /// Adds source and destination. + /// Source and destination are multiplied by factors before addition. + Add { src: Factor, dst: Factor }, + /// Subtracts destination from source. + /// Source and destination are multiplied by factors before subtraction. + Sub { src: Factor, dst: Factor }, + /// Subtracts source from destination. + /// Source and destination are multiplied by factors before subtraction. + RevSub { src: Factor, dst: Factor }, + /// Component-wise minimum value of source and destination. + Min, + /// Component-wise maximum value of source and destination. + Max, +} + +impl BlendOp { + /// Replace the destination value with the source. + pub const REPLACE: Self = BlendOp::Add { + src: Factor::One, + dst: Factor::Zero, + }; + /// Add the source and destination together. + pub const ADD: Self = BlendOp::Add { + src: Factor::One, + dst: Factor::One, + }; + /// Alpha blend the source and destination together. + pub const ALPHA: Self = BlendOp::Add { + src: Factor::SrcAlpha, + dst: Factor::OneMinusSrcAlpha, + }; + /// Alpha blend a premultiplied-alpha source with the destination. + pub const PREMULTIPLIED_ALPHA: Self = BlendOp::Add { + src: Factor::One, + dst: Factor::OneMinusSrcAlpha, + }; +} + +/// Specifies whether to use blending, and if so, +/// which operations to use for color and alpha channels. +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct BlendState { + /// The blend operation to use for the color channels. + pub color: BlendOp, + /// The blend operation to use for the alpha channel. + pub alpha: BlendOp, +} + +impl BlendState { + /// Replace the color. + pub const REPLACE: Self = BlendState { + color: BlendOp::REPLACE, + alpha: BlendOp::REPLACE, + }; + /// Additive blending. + pub const ADD: Self = BlendState { + color: BlendOp::ADD, + alpha: BlendOp::ADD, + }; + /// Multiplicative blending. + pub const MULTIPLY: Self = BlendState { + color: BlendOp::Add { + src: Factor::Zero, + dst: Factor::SrcColor, + }, + alpha: BlendOp::Add { + src: Factor::Zero, + dst: Factor::SrcAlpha, + }, + }; + /// Alpha blending. + pub const ALPHA: Self = BlendState { + color: BlendOp::ALPHA, + alpha: BlendOp::PREMULTIPLIED_ALPHA, + }; + /// Pre-multiplied alpha blending. + pub const PREMULTIPLIED_ALPHA: Self = BlendState { + color: BlendOp::PREMULTIPLIED_ALPHA, + alpha: BlendOp::PREMULTIPLIED_ALPHA, + }; +} + +/// PSO color target descriptor. +#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct ColorBlendDesc { + /// Color write mask. + pub mask: ColorMask, + /// Blend state, if enabled. + pub blend: Option, +} + +impl ColorBlendDesc { + /// Empty blend descriptor just writes out the color without blending. + // this can be used because `Default::default()` isn't a const function... + pub const EMPTY: Self = ColorBlendDesc { + mask: ColorMask::ALL, + blend: None, + }; +} + +/// Depth test state. +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct DepthTest { + /// Comparison function to use. + pub fun: Comparison, + /// Specify whether to write to the depth buffer or not. + pub write: bool, +} + +impl DepthTest { + /// A depth test that always fails. + pub const FAIL: Self = DepthTest { + fun: Comparison::Never, + write: false, + }; + /// A depth test that always succeeds but doesn't + /// write to the depth buffer + // DOC TODO: Not a terribly helpful description there... + pub const PASS_TEST: Self = DepthTest { + fun: Comparison::Always, + write: false, + }; + /// A depth test that always succeeds and writes its result + /// to the depth buffer. + pub const PASS_WRITE: Self = DepthTest { + fun: Comparison::Always, + write: true, + }; +} + +/// The operation to use for stencil masking. +#[repr(u8)] +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum StencilOp { + /// Keep the current value in the stencil buffer (no change). + Keep = 0, + /// Set the value in the stencil buffer to zero. + Zero = 1, + /// Set the stencil buffer value to `reference` from `StencilFace`. + Replace = 2, + /// Increment the stencil buffer value, clamping to its maximum value. + IncrementClamp = 3, + /// Decrement the stencil buffer value, clamping to its minimum value. + DecrementClamp = 4, + /// Bitwise invert the current value in the stencil buffer. + Invert = 5, + /// Increment the stencil buffer value, wrapping around to 0 on overflow. + IncrementWrap = 6, + /// Decrement the stencil buffer value, wrapping around to the maximum value on overflow. + DecrementWrap = 7, +} + +/// Complete stencil state for a given side of a face. +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct StencilFace { + /// Comparison function to use to determine if the stencil test passes. + pub fun: Comparison, + /// What operation to do if the stencil test fails. + pub op_fail: StencilOp, + /// What operation to do if the stencil test passes but the depth test fails. + pub op_depth_fail: StencilOp, + /// What operation to do if both the depth and stencil test pass. + pub op_pass: StencilOp, +} + +impl Default for StencilFace { + fn default() -> StencilFace { + StencilFace { + fun: Comparison::Never, + op_fail: StencilOp::Keep, + op_depth_fail: StencilOp::Keep, + op_pass: StencilOp::Keep, + } + } +} + +/// A generic struct holding the properties of two sides of a polygon. +#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct Sided { + /// Information about the front face. + pub front: T, + /// Information about the back face. + pub back: T, +} + +impl Sided { + /// Create a new `Sided` structure with both `front` and `back` holding + /// the same value. + pub fn new(value: T) -> Self { + Sided { + front: value, + back: value, + } + } +} + +/// Pair of stencil values that could be either baked into a graphics pipeline +/// or provided dynamically. +pub type StencilValues = State>; + +/// Defines a stencil test. Stencil testing is an operation +/// performed to cull fragments; +/// the new fragment is tested against the value held in the +/// stencil buffer, and if the test fails the fragment is +/// discarded. +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct StencilTest { + /// Operations for stencil faces. + pub faces: Sided, + /// Masks that are ANDd with both the stencil buffer value and the reference value when they + /// are read before doing the stencil test. + pub read_masks: StencilValues, + /// Mask that are ANDd with the stencil value before writing to the stencil buffer. + pub write_masks: StencilValues, + /// The reference values used for stencil tests. + pub reference_values: StencilValues, +} + +impl Default for StencilTest { + fn default() -> Self { + StencilTest { + faces: Sided::default(), + read_masks: State::Static(Sided::new(!0)), + write_masks: State::Static(Sided::new(!0)), + reference_values: State::Static(Sided::new(0)), + } + } +} + +/// PSO depth-stencil target descriptor. +#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct DepthStencilDesc { + /// Optional depth testing/writing. + pub depth: Option, + /// Enable depth bounds testing. + pub depth_bounds: bool, + /// Stencil test/write. + pub stencil: Option, +} + +impl DepthStencilDesc { + /// Returns true if the descriptor assumes the depth attachment. + pub fn uses_depth(&self) -> bool { + self.depth.is_some() || self.depth_bounds + } + /// Returns true if the descriptor assumes the stencil attachment. + pub fn uses_stencil(&self) -> bool { + self.stencil.is_some() + } +} + +bitflags!( + /// Face. + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + pub struct Face: u32 { + /// Empty face. TODO: remove when constexpr are stabilized to use empty() + const NONE = 0x0; + /// Front face. + const FRONT = 0x1; + /// Back face. + const BACK = 0x2; + } +); diff --git a/third_party/rust/gfx-hal/src/pso/specialization.rs b/third_party/rust/gfx-hal/src/pso/specialization.rs new file mode 100644 index 0000000000..d4c15c3d25 --- /dev/null +++ b/third_party/rust/gfx-hal/src/pso/specialization.rs @@ -0,0 +1,134 @@ +//! Pipeline specialization types. + +use std::{borrow::Cow, ops::Range, slice}; + +/// Description of a specialization constant for the pipeline. +#[derive(Debug, Clone, Hash, PartialEq)] +pub struct SpecializationConstant { + /// Constant identifier in shader source. + pub id: u32, + /// Value to override specialization constant. + pub range: Range, +} + +/// Information required for pipeline specialization. +/// +/// Specialization allows for easy configuration of multiple similar pipelines. +/// For example, there may be a boolean exposed to the shader that switches +/// the [specularity](https://en.wikipedia.org/wiki/Specularity) on/off, +/// provided via a [specialization constant][SpecializationConstant]. +/// +/// That would produce separate PSO's for the "on" and "off" states +/// but they share most of the internal stuff and are fast to produce. +/// More importantly, they are fast to execute, since the driver +/// can optimize out the branch on that other PSO creation. +#[derive(Debug, Clone)] +pub struct Specialization<'a> { + /// Array of descriptors of specialization constants to override. + pub constants: Cow<'a, [SpecializationConstant]>, + /// Raw data of the specialization constants + pub data: Cow<'a, [u8]>, +} + +impl Specialization<'_> { + /// Empty specialization instance. + pub const EMPTY: Self = Specialization { + constants: Cow::Borrowed(&[]), + data: Cow::Borrowed(&[]), + }; +} + +impl Default for Specialization<'_> { + fn default() -> Self { + Specialization::EMPTY + } +} + +#[doc(hidden)] +#[derive(Debug, Default)] +pub struct SpecializationStorage { + constants: Vec, + data: Vec, +} + +/// List of specialization constants. +#[doc(hidden)] +pub trait SpecConstList: Sized { + fn fold(self, storage: &mut SpecializationStorage); +} + +impl From for Specialization<'_> +where + T: SpecConstList, +{ + fn from(list: T) -> Self { + let mut storage = SpecializationStorage::default(); + list.fold(&mut storage); + Specialization { + data: Cow::Owned(storage.data), + constants: Cow::Owned(storage.constants), + } + } +} + +#[doc(hidden)] +#[derive(Debug)] +pub struct SpecConstListNil; + +#[doc(hidden)] +#[derive(Debug)] +pub struct SpecConstListCons { + pub head: (u32, H), + pub tail: T, +} + +impl SpecConstList for SpecConstListNil { + fn fold(self, _storage: &mut SpecializationStorage) {} +} + +impl SpecConstList for SpecConstListCons +where + T: SpecConstList, +{ + fn fold(self, storage: &mut SpecializationStorage) { + let size = std::mem::size_of::(); + assert!(storage.data.len() + size <= u16::max_value() as usize); + let offset = storage.data.len() as u16; + storage.data.extend_from_slice(unsafe { + // Inspecting bytes is always safe. + let head_ptr: *const H = &self.head.1; + slice::from_raw_parts(head_ptr as *const u8, size) + }); + storage.constants.push(SpecializationConstant { + id: self.head.0, + range: offset..offset + size as u16, + }); + self.tail.fold(storage) + } +} + +/// Macro for specifying list of specialization constants for `EntryPoint`. +#[macro_export] +macro_rules! spec_const_list { + (@ $(,)?) => { + $crate::pso::SpecConstListNil + }; + + (@ $head_id:expr => $head_constant:expr $(,$tail_id:expr => $tail_constant:expr)* $(,)?) => { + $crate::pso::SpecConstListCons { + head: ($head_id, $head_constant), + tail: $crate::spec_const_list!(@ $($tail_id => $tail_constant),*), + } + }; + + ($($id:expr => $constant:expr),* $(,)?) => { + $crate::spec_const_list!(@ $($id => $constant),*).into() + }; + + ($($constant:expr),* $(,)?) => { + { + let mut counter = 0; + $crate::spec_const_list!(@ $({ counter += 1; counter - 1 } => $constant),*).into() + } + }; +} diff --git a/third_party/rust/gfx-hal/src/query.rs b/third_party/rust/gfx-hal/src/query.rs new file mode 100644 index 0000000000..66023361b4 --- /dev/null +++ b/third_party/rust/gfx-hal/src/query.rs @@ -0,0 +1,119 @@ +//! Commands that can be used to record statistics or other useful values +//! as the command buffer is running. +//! +//! They are often intended for profiling or other introspection, +//! providing a mechanism for the command buffer to record data about its operation +//! as it is running. + +use crate::device::OutOfMemory; +use crate::Backend; + +/// A query identifier. +pub type Id = u32; + +/// Query creation error. +#[derive(Clone, Debug, PartialEq)] +pub enum CreationError { + /// Out of either host or device memory. + OutOfMemory(OutOfMemory), + + /// Query type unsupported. + Unsupported(Type), +} + +impl From for CreationError { + fn from(error: OutOfMemory) -> Self { + CreationError::OutOfMemory(error) + } +} + +impl std::fmt::Display for CreationError { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + CreationError::OutOfMemory(err) => write!(fmt, "Failed to create query: {}", err), + CreationError::Unsupported(ty) => { + write!(fmt, "Failed to create query: Unsupported type: {:?}", ty) + } + } + } +} + +/// A `Query` object has a particular identifier and saves its results to a given `QueryPool`. +/// It is passed as a parameter to the command buffer's query methods. +#[derive(Debug)] +pub struct Query<'a, B: Backend> { + /// + pub pool: &'a B::QueryPool, + /// + pub id: Id, +} + +bitflags!( + /// Query control flags. + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + pub struct ControlFlags: u32 { + /// Occlusion queries **must** return the exact sampler number. + /// + /// Requires `precise_occlusion_query` device feature. + const PRECISE = 0x1; + } +); + +bitflags!( + /// Query result flags. + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + pub struct ResultFlags: u32 { + /// Results will be written as an array of 64-bit unsigned integer values. + const BITS_64 = 0x1; + /// Wait for each query’s status to become available before retrieving its results. + const WAIT = 0x2; + /// Availability status accompanies the results. + const WITH_AVAILABILITY = 0x4; + /// Returning partial results is acceptable. + const PARTIAL = 0x8; + } +); + +/// Type of queries in a query pool. +#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] +pub enum Type { + /// Occlusion query. Count the number of drawn samples between + /// the start and end of the query command. + Occlusion, + /// Pipeline statistic query. Counts the number of pipeline stage + /// invocations of the given types between the start and end of + /// the query command. + PipelineStatistics(PipelineStatistic), + /// Timestamp query. Timestamps can be recorded to the + /// query pool by calling `write_timestamp()`. + Timestamp, +} + +bitflags!( + /// Pipeline statistic flags + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + pub struct PipelineStatistic: u32 { + /// + const INPUT_ASSEMBLY_VERTICES = 0x1; + /// + const INPUT_ASSEMBLY_PRIMITIVES = 0x2; + /// + const VERTEX_SHADER_INVOCATIONS = 0x4; + /// + const GEOMETRY_SHADER_INVOCATIONS = 0x8; + /// + const GEOMETRY_SHADER_PRIMITIVES = 0x10; + /// + const CLIPPING_INVOCATIONS = 0x20; + /// + const CLIPPING_PRIMITIVES = 0x40; + /// + const FRAGMENT_SHADER_INVOCATIONS = 0x80; + /// + const HULL_SHADER_PATCHES = 0x100; + /// + const DOMAIN_SHADER_INVOCATIONS = 0x200; + /// + const COMPUTE_SHADER_INVOCATIONS = 0x400; + } +); diff --git a/third_party/rust/gfx-hal/src/queue/family.rs b/third_party/rust/gfx-hal/src/queue/family.rs new file mode 100644 index 0000000000..765e54d64c --- /dev/null +++ b/third_party/rust/gfx-hal/src/queue/family.rs @@ -0,0 +1,55 @@ +//! Queue family and groups. + +use crate::queue::QueueType; +use crate::Backend; + +use std::any::Any; +use std::fmt::Debug; + +/// General information about a queue family, available upon adapter discovery. +/// +/// Note that a backend can expose multiple queue families with the same properties. +/// +/// Can be obtained from an [adapter][crate::adapter::Adapter] through its +/// [`queue_families`][crate::adapter::Adapter::queue_families] field. +pub trait QueueFamily: Debug + Any + Send + Sync { + /// Returns the type of queues. + fn queue_type(&self) -> QueueType; + /// Returns maximum number of queues created from this family. + fn max_queues(&self) -> usize; + /// Returns the queue family ID. + fn id(&self) -> QueueFamilyId; +} + +/// Identifier for a queue family of a physical device. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct QueueFamilyId(pub usize); + +/// Bare-metal queue group. +/// +/// Denotes all queues created from one queue family. +#[derive(Debug)] +pub struct QueueGroup { + /// Family index for the queues in this group. + pub family: QueueFamilyId, + /// List of queues. + pub queues: Vec, +} + +impl QueueGroup { + /// Create a new, empty queue group for a queue family. + pub fn new(family: QueueFamilyId) -> Self { + QueueGroup { + family, + queues: Vec::new(), + } + } + + /// Add a command queue to the group. + /// + /// The queue needs to be created from this queue family. + pub fn add_queue(&mut self, queue: B::CommandQueue) { + self.queues.push(queue); + } +} diff --git a/third_party/rust/gfx-hal/src/queue/mod.rs b/third_party/rust/gfx-hal/src/queue/mod.rs new file mode 100644 index 0000000000..f913664b5f --- /dev/null +++ b/third_party/rust/gfx-hal/src/queue/mod.rs @@ -0,0 +1,139 @@ +//! Command queues. +//! +//! Queues are the execution paths of the graphical processing units. These process +//! submitted commands buffers. +//! +//! There are different types of queues, which can only handle associated command buffers. +//! `CommandQueue` has the capability defined by `C`: graphics, compute and transfer. + +pub mod family; + +use crate::{ + device::OutOfMemory, + pso, + window::{PresentError, PresentationSurface, Suboptimal}, + Backend, +}; +use std::{any::Any, borrow::Borrow, fmt, iter}; + +pub use self::family::{QueueFamily, QueueFamilyId, QueueGroup}; + +/// The type of the queue, an enum encompassing `queue::Capability` +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum QueueType { + /// Supports all operations. + General, + /// Only supports graphics and transfer operations. + Graphics, + /// Only supports compute and transfer operations. + Compute, + /// Only supports transfer operations. + Transfer, +} + +impl QueueType { + /// Returns true if the queue supports graphics operations. + pub fn supports_graphics(&self) -> bool { + match *self { + QueueType::General | QueueType::Graphics => true, + QueueType::Compute | QueueType::Transfer => false, + } + } + /// Returns true if the queue supports compute operations. + pub fn supports_compute(&self) -> bool { + match *self { + QueueType::General | QueueType::Graphics | QueueType::Compute => true, + QueueType::Transfer => false, + } + } + /// Returns true if the queue supports transfer operations. + pub fn supports_transfer(&self) -> bool { + true + } +} + +/// Scheduling hint for devices about the priority of a queue. Values range from `0.0` (low) to +/// `1.0` (high). +pub type QueuePriority = f32; + +/// Submission information for a [command queue][CommandQueue]. +/// +/// The submission is sent to the device through the [`submit`][CommandQueue::submit] method. +#[derive(Debug)] +pub struct Submission { + /// Command buffers to submit. + pub command_buffers: Ic, + /// Semaphores to wait being signalled before submission. + pub wait_semaphores: Iw, + /// Semaphores to signal after all command buffers in the submission have finished execution. + pub signal_semaphores: Is, +} + +/// Abstraction for an internal GPU execution engine. +/// +/// Commands are executed on the the device by submitting +/// [command buffers][crate::command::CommandBuffer]. +/// +/// Queues can also be used for presenting to a surface +/// (that is, flip the front buffer with the next one in the chain). +pub trait CommandQueue: fmt::Debug + Any + Send + Sync { + /// Submit command buffers to queue for execution. + /// + /// # Arguments + /// + /// * `submission` - information about which command buffers to submit, + /// as well as what semaphores to wait for or to signal when done. + /// * `fence` - must be in unsignaled state, and will be signaled after + /// all command buffers in the submission have finished execution. + /// + /// # Safety + /// + /// It's not checked that the queue can process the submitted command buffers. + /// + /// For example, trying to submit compute commands to a graphics queue + /// will result in undefined behavior. + unsafe fn submit<'a, T, Ic, S, Iw, Is>( + &mut self, + submission: Submission, + fence: Option<&B::Fence>, + ) where + T: 'a + Borrow, + Ic: IntoIterator, + S: 'a + Borrow, + Iw: IntoIterator, + Is: IntoIterator; + + /// Simplified version of `submit` that doesn't expect any semaphores. + unsafe fn submit_without_semaphores<'a, T, Ic>( + &mut self, + command_buffers: Ic, + fence: Option<&B::Fence>, + ) where + T: 'a + Borrow, + Ic: IntoIterator, + { + let submission = Submission { + command_buffers, + wait_semaphores: iter::empty(), + signal_semaphores: iter::empty(), + }; + self.submit::<_, _, B::Semaphore, _, _>(submission, fence) + } + + /// Present a swapchain image directly to a surface, after waiting on `wait_semaphore`. + /// + /// # Safety + /// + /// Unsafe for the same reasons as [`submit`][CommandQueue::submit]. + /// No checks are performed to verify that this queue supports present operations. + unsafe fn present( + &mut self, + surface: &mut B::Surface, + image: >::SwapchainImage, + wait_semaphore: Option<&B::Semaphore>, + ) -> Result, PresentError>; + + /// Wait for the queue to be idle. + fn wait_idle(&self) -> Result<(), OutOfMemory>; +} diff --git a/third_party/rust/gfx-hal/src/window.rs b/third_party/rust/gfx-hal/src/window.rs new file mode 100644 index 0000000000..b0bc6801e7 --- /dev/null +++ b/third_party/rust/gfx-hal/src/window.rs @@ -0,0 +1,563 @@ +//! Windowing system interoperability. +//! +//! Screen presentation (fullscreen or window) of images requires two objects: +//! +//! * [Surface][Surface] is an abstraction of a native screen or window, for graphics use. +//! * [Swapchain][Swapchain] is a chain of multiple images, which can be presented on +//! a surface. +//! +//! ## Window +//! +//! `gfx-hal` does not provide any methods for creating a native window or screen. +//! This is handled exeternally, either by managing your own window or by using a +//! library such as [winit](https://github.com/rust-windowing/winit), and providing +//! the [raw window handle](https://github.com/rust-windowing/raw-window-handle). +//! +//! ## Surface +//! +//! Once you have a window handle, you need to [create a surface][crate::Instance::create_surface] +//! compatible with the [instance][crate::Instance] of the graphics API you currently use. +//! +//! ## PresentationSurface +//! +//! A surface has an implicit swapchain in it. +//! +//! The most interesting part of a swapchain are the contained presentable images/backbuffers. +//! Presentable images are specialized images, which can be presented on the screen. They are +//! 2D color images with optionally associated depth-stencil images. +//! +//! The common steps for presentation of a frame are acquisition and presentation: +//! +//! ```no_run +//! # extern crate gfx_backend_empty as empty; +//! # extern crate gfx_hal; +//! # fn main() { +//! # use gfx_hal::prelude::*; +//! +//! # let mut surface: empty::Surface = return; +//! # let device: empty::Device = return; +//! # let mut present_queue: empty::CommandQueue = return; +//! # unsafe { +//! let render_semaphore = device.create_semaphore().unwrap(); +//! +//! let (frame, suboptimal) = surface.acquire_image(!0).unwrap(); +//! // render the scene.. +//! // `render_semaphore` will be signalled once rendering has been finished +//! present_queue.present(&mut surface, frame, Some(&render_semaphore)); +//! # }} +//! ``` +//! +//! Queues need to synchronize with the presentation engine, usually done via signalling a semaphore +//! once a frame is available for rendering and waiting on a separate semaphore until scene rendering +//! has finished. +//! +//! ### Recreation +//! +//! DOC TODO + +use crate::{device, format::Format, image, Backend}; + +use std::{ + any::Any, + borrow::Borrow, + cmp::{max, min}, + fmt, + ops::RangeInclusive, +}; + +/// Default image usage for the swapchain. +pub const DEFAULT_USAGE: image::Usage = image::Usage::COLOR_ATTACHMENT; +/// Default image count for the swapchain. +pub const DEFAULT_IMAGE_COUNT: SwapImageIndex = 3; + +/// Error occurred during swapchain creation. +#[derive(Clone, Debug, PartialEq)] +pub enum CreationError { + /// Out of either host or device memory. + OutOfMemory(device::OutOfMemory), + /// Device is lost + DeviceLost(device::DeviceLost), + /// Surface is lost + SurfaceLost(device::SurfaceLost), + /// Window in use + WindowInUse(device::WindowInUse), +} + +impl From for CreationError { + fn from(error: device::OutOfMemory) -> Self { + CreationError::OutOfMemory(error) + } +} + +impl From for CreationError { + fn from(error: device::DeviceLost) -> Self { + CreationError::DeviceLost(error) + } +} + +impl From for CreationError { + fn from(error: device::SurfaceLost) -> Self { + CreationError::SurfaceLost(error) + } +} + +impl From for CreationError { + fn from(error: device::WindowInUse) -> Self { + CreationError::WindowInUse(error) + } +} + +impl std::fmt::Display for CreationError { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + CreationError::OutOfMemory(err) => { + write!(fmt, "Failed to create or configure swapchain: {}", err) + } + CreationError::DeviceLost(err) => { + write!(fmt, "Failed to create or configure swapchain: {}", err) + } + CreationError::SurfaceLost(err) => { + write!(fmt, "Failed to create or configure swapchain: {}", err) + } + CreationError::WindowInUse(err) => { + write!(fmt, "Failed to create or configure swapchain: {}", err) + } + } + } +} + +impl std::error::Error for CreationError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + CreationError::OutOfMemory(err) => Some(err), + CreationError::DeviceLost(err) => Some(err), + CreationError::SurfaceLost(err) => Some(err), + CreationError::WindowInUse(err) => Some(err), + } + } +} + +/// An extent describes the size of a rectangle, such as +/// a window or texture. It is not used for referring to a +/// sub-rectangle; for that see `command::Rect`. +#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct Extent2D { + /// Width + pub width: image::Size, + /// Height + pub height: image::Size, +} + +impl From for Extent2D { + fn from(ex: image::Extent) -> Self { + Extent2D { + width: ex.width, + height: ex.height, + } + } +} + +impl Extent2D { + /// Convert into a regular image extent. + pub fn to_extent(&self) -> image::Extent { + image::Extent { + width: self.width, + height: self.height, + depth: 1, + } + } +} + +/// Describes information about what a `Surface`'s properties are. +/// Fetch this with [Surface::capabilities]. +#[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 or equal to `image_count.start`. + pub image_count: RangeInclusive, + + /// Current extent of the surface. + /// + /// `None` if the surface has no explicit size, depending on the swapchain extent. + pub current_extent: Option, + + /// Range of supported extents. + /// + /// `current_extent` must be inside this range. + pub extents: RangeInclusive, + + /// 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 presentation modes. + pub present_modes: PresentMode, + + /// A bitmask of supported alpha composition modes. + pub composite_alpha_modes: CompositeAlphaMode, +} + +impl SurfaceCapabilities { + fn clamped_extent(&self, default_extent: Extent2D) -> Extent2D { + match self.current_extent { + Some(current) => current, + None => { + let (min_width, max_width) = (self.extents.start().width, self.extents.end().width); + let (min_height, max_height) = + (self.extents.start().height, self.extents.end().height); + + // clamp the default_extent to within the allowed surface sizes + let width = min(max_width, max(default_extent.width, min_width)); + let height = min(max_height, max(default_extent.height, min_height)); + + Extent2D { width, height } + } + } + } +} + +/// A `Surface` abstracts the surface of a native window. +pub trait Surface: fmt::Debug + Any + Send + Sync { + /// Check if the queue family supports presentation to this surface. + fn supports_queue_family(&self, family: &B::QueueFamily) -> bool; + + /// Query surface capabilities for this physical device. + /// + /// Use this function for configuring swapchain creation. + fn capabilities(&self, physical_device: &B::PhysicalDevice) -> SurfaceCapabilities; + + /// Query surface formats for this physical device. + /// + /// This function may be slow. It's typically used during the initialization only. + /// + /// Note: technically the surface support formats may change at the point + /// where an application needs to recreate the swapchain, e.g. when the window + /// is moved to a different monitor. + /// + /// If `None` is returned then the surface has no preferred format and the + /// application may use any desired format. + fn supported_formats(&self, physical_device: &B::PhysicalDevice) -> Option>; +} + +/// A surface trait that exposes the ability to present images on the +/// associtated swap chain. +pub trait PresentationSurface: Surface { + /// An opaque type wrapping the swapchain image. + type SwapchainImage: Borrow + Borrow + fmt::Debug + Send + Sync; + + /// Set up the swapchain associated with the surface to have the given format. + unsafe fn configure_swapchain( + &mut self, + device: &B::Device, + config: SwapchainConfig, + ) -> Result<(), CreationError>; + + /// Remove the associated swapchain from this surface. + /// + /// This has to be done before the surface is dropped. + unsafe fn unconfigure_swapchain(&mut self, device: &B::Device); + + /// Acquire a new swapchain image for rendering. + /// + /// May fail according to one of the reasons indicated in `AcquireError` enum. + /// + /// # Synchronization + /// + /// The acquired image is available to render. No synchronization is required. + unsafe fn acquire_image( + &mut self, + timeout_ns: u64, + ) -> Result<(Self::SwapchainImage, Option), AcquireError>; +} + +/// Index of an image in the swapchain. +/// +/// The swapchain is a series of one or more images, usually +/// with one being drawn on while the other is displayed by +/// the GPU (aka double-buffering). A `SwapImageIndex` refers +/// to a particular image in the swapchain. +pub type SwapImageIndex = u32; + +bitflags!( + /// Specifies the mode regulating how a swapchain presents frames. + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + pub struct PresentMode: u32 { + /// Don't ever wait for v-sync. + const IMMEDIATE = 0x1; + /// Wait for v-sync, overwrite the last rendered frame. + const MAILBOX = 0x2; + /// Present frames in the same order they are rendered. + const FIFO = 0x4; + /// Don't wait for the next v-sync if we just missed it. + const RELAXED = 0x8; + } +); + +bitflags!( + /// Specifies how the alpha channel of the images should be handled during + /// compositing. + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + pub struct CompositeAlphaMode: u32 { + /// The alpha channel, if it exists, of the images is ignored in the + /// compositing process. Instead, the image is treated as if it has a + /// constant alpha of 1.0. + const OPAQUE = 0x1; + /// The alpha channel, if it exists, of the images is respected in the + /// compositing process. The non-alpha channels of the image are + /// expected to already be multiplied by the alpha channel by the + /// application. + const PREMULTIPLIED = 0x2; + /// The alpha channel, if it exists, of the images is respected in the + /// compositing process. The non-alpha channels of the image are not + /// expected to already be multiplied by the alpha channel by the + /// application; instead, the compositor will multiply the non-alpha + /// channels of the image by the alpha channel during compositing. + const POSTMULTIPLIED = 0x4; + /// The way in which the presentation engine treats the alpha channel in + /// the images is unknown to gfx-hal. Instead, the application is + /// responsible for setting the composite alpha blending mode using + /// native window system commands. If the application does not set the + /// blending mode using native window system commands, then a + /// platform-specific default will be used. + const INHERIT = 0x8; + } +); + +/// 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::window::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_mode: CompositeAlphaMode, + /// 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, +} + +impl SwapchainConfig { + /// Create a new default configuration (color images only). + pub fn new(width: u32, height: u32, format: Format, image_count: SwapImageIndex) -> Self { + SwapchainConfig { + present_mode: PresentMode::FIFO, + composite_alpha_mode: CompositeAlphaMode::OPAQUE, + format, + extent: Extent2D { width, height }, + image_count, + image_layers: 1, + image_usage: DEFAULT_USAGE, + } + } + + /// Create a swapchain configuration based on the capabilities + /// returned from a physical device query. If the surface does not + /// specify a current size, default_extent is clamped and used instead. + /// + /// The default values are taken from `DEFAULT_USAGE` and `DEFAULT_IMAGE_COUNT`. + pub fn from_caps(caps: &SurfaceCapabilities, format: Format, default_extent: Extent2D) -> Self { + let composite_alpha_mode = if caps + .composite_alpha_modes + .contains(CompositeAlphaMode::INHERIT) + { + CompositeAlphaMode::INHERIT + } else if caps + .composite_alpha_modes + .contains(CompositeAlphaMode::OPAQUE) + { + CompositeAlphaMode::OPAQUE + } else { + panic!("neither INHERIT or OPAQUE CompositeAlphaMode(s) are supported") + }; + let present_mode = if caps.present_modes.contains(PresentMode::MAILBOX) { + PresentMode::MAILBOX + } else if caps.present_modes.contains(PresentMode::FIFO) { + PresentMode::FIFO + } else { + panic!("FIFO PresentMode is not supported") + }; + + SwapchainConfig { + present_mode, + composite_alpha_mode, + format, + extent: caps.clamped_extent(default_extent), + image_count: DEFAULT_IMAGE_COUNT + .max(*caps.image_count.start()) + .min(*caps.image_count.end()), + image_layers: 1, + image_usage: DEFAULT_USAGE, + } + } + + /// Specify the presentation mode. + pub fn with_present_mode(mut self, mode: PresentMode) -> Self { + self.present_mode = mode; + self + } + + /// Specify the presentation mode. + pub fn with_composite_alpha_mode(mut self, mode: CompositeAlphaMode) -> Self { + self.composite_alpha_mode = mode; + self + } + + /// Specify the usage of backbuffer images. + pub fn with_image_usage(mut self, usage: image::Usage) -> Self { + self.image_usage = usage; + self + } + + /// Specify the count of backbuffer image. + pub fn with_image_count(mut self, count: SwapImageIndex) -> Self { + self.image_count = count; + self + } + + // TODO: depth-only, stencil-only, swapchain size, present modes, etc. +} + +/// Marker value returned if the swapchain no longer matches the surface properties exactly, +/// but can still be used to present to the surface successfully. +#[derive(Debug)] +pub struct Suboptimal; + +/// Error on acquiring the next image from a swapchain. +#[derive(Clone, Debug, PartialEq)] +pub enum AcquireError { + /// Out of either host or device memory. + OutOfMemory(device::OutOfMemory), + /// No image was ready and no timeout was specified. + NotReady, + /// No image was ready after the specified timeout expired. + Timeout, + /// The swapchain is no longer in sync with the surface, needs to be re-created. + OutOfDate, + /// The surface was lost, and the swapchain is no longer usable. + SurfaceLost(device::SurfaceLost), + /// Device is lost + DeviceLost(device::DeviceLost), +} + +impl std::fmt::Display for AcquireError { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + AcquireError::OutOfMemory(err) => write!(fmt, "Failed to acqure image: {}", err), + AcquireError::NotReady => write!( + fmt, + "Failed to acqure image: No image ready (timeout wasn't specified)" + ), + AcquireError::Timeout => { + write!(fmt, "Failed to acqure image: No image ready (timeout)") + } + AcquireError::OutOfDate => write!( + fmt, + "Failed to acqure image: Swapchain is out of date and needs to be re-created" + ), + AcquireError::SurfaceLost(err) => write!(fmt, "Failed to acqure image: {}", err), + AcquireError::DeviceLost(err) => write!(fmt, "Failed to acqure image: {}", err), + } + } +} + +impl std::error::Error for AcquireError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + AcquireError::OutOfMemory(err) => Some(err), + AcquireError::SurfaceLost(err) => Some(err), + AcquireError::DeviceLost(err) => Some(err), + _ => None, + } + } +} + +/// Error on acquiring the next image from a swapchain. +#[derive(Clone, Debug, PartialEq)] +pub enum PresentError { + /// Out of either host or device memory. + OutOfMemory(device::OutOfMemory), + /// The swapchain is no longer in sync with the surface, needs to be re-created. + OutOfDate, + /// The surface was lost, and the swapchain is no longer usable. + SurfaceLost(device::SurfaceLost), + /// Device is lost + DeviceLost(device::DeviceLost), +} + +impl std::fmt::Display for PresentError { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + PresentError::OutOfMemory(err) => write!(fmt, "Failed to present image: {}", err), + PresentError::OutOfDate => write!( + fmt, + "Failed to present image: Swapchain is out of date and needs to be re-created" + ), + PresentError::SurfaceLost(err) => write!(fmt, "Failed to present image: {}", err), + PresentError::DeviceLost(err) => write!(fmt, "Failed to present image: {}", err), + } + } +} + +impl std::error::Error for PresentError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + PresentError::OutOfMemory(err) => Some(err), + PresentError::SurfaceLost(err) => Some(err), + PresentError::DeviceLost(err) => Some(err), + _ => None, + } + } +} + +/// Error occurred during surface creation. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum InitError { + /// Window handle is not supported by the backend. + UnsupportedWindowHandle, +} + +impl std::fmt::Display for InitError { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + InitError::UnsupportedWindowHandle => write!( + fmt, + "Failed to create surface: Specified window handle is unsupported" + ), + } + } +} + +impl std::error::Error for InitError {} -- cgit v1.2.3