/*! This library describes the internal unsafe graphics abstraction API. * It follows WebGPU for the most part, re-using wgpu-types, * with the following deviations: * - Fully unsafe: zero overhead, zero validation. * - Compile-time backend selection via traits. * - Objects are passed by references and returned by value. No IDs. * - Mapping is persistent, with explicit synchronization. * - Resource transitions are explicit. * - All layouts are explicit. Binding model has compatibility. * * General design direction is to follow the majority by the following weights: * - wgpu-core: 1.5 * - primary backends (Vulkan/Metal/DX12): 1.0 each * - secondary backend (GLES): 0.5 */ #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] #![allow( // this happens on the GL backend, where it is both thread safe and non-thread safe in the same code. clippy::arc_with_non_send_sync, // for `if_then_panic` until it reaches stable unknown_lints, // We use loops for getting early-out of scope without closures. clippy::never_loop, // We don't use syntax sugar where it's not necessary. clippy::match_like_matches_macro, // Redundant matching is more explicit. clippy::redundant_pattern_matching, // Explicit lifetimes are often easier to reason about. clippy::needless_lifetimes, // No need for defaults in the internal types. clippy::new_without_default, // Matches are good and extendable, no need to make an exception here. clippy::single_match, // Push commands are more regular than macros. clippy::vec_init_then_push, // "if panic" is a good uniform construct. clippy::if_then_panic, // We unsafe impl `Send` for a reason. clippy::non_send_fields_in_send_ty, // TODO! clippy::missing_safety_doc, // Clashes with clippy::pattern_type_mismatch clippy::needless_borrowed_reference, )] #![warn( trivial_casts, trivial_numeric_casts, unsafe_op_in_unsafe_fn, unused_extern_crates, unused_qualifications, // We don't match on a reference, unless required. clippy::pattern_type_mismatch, )] /// DirectX12 API internals. #[cfg(dx12)] pub mod dx12; /// A dummy API implementation. pub mod empty; /// GLES API internals. #[cfg(gles)] pub mod gles; /// Metal API internals. #[cfg(metal)] pub mod metal; /// Vulkan API internals. #[cfg(vulkan)] pub mod vulkan; pub mod auxil; pub mod api { #[cfg(dx12)] pub use super::dx12::Api as Dx12; pub use super::empty::Api as Empty; #[cfg(gles)] pub use super::gles::Api as Gles; #[cfg(metal)] pub use super::metal::Api as Metal; #[cfg(vulkan)] pub use super::vulkan::Api as Vulkan; } use std::{ borrow::{Borrow, Cow}, fmt, num::NonZeroU32, ops::{Range, RangeInclusive}, ptr::NonNull, sync::Arc, }; use bitflags::bitflags; use parking_lot::Mutex; use thiserror::Error; use wgt::WasmNotSendSync; // - Vertex + Fragment // - Compute pub const MAX_CONCURRENT_SHADER_STAGES: usize = 2; pub const MAX_ANISOTROPY: u8 = 16; pub const MAX_BIND_GROUPS: usize = 8; pub const MAX_VERTEX_BUFFERS: usize = 16; pub const MAX_COLOR_ATTACHMENTS: usize = 8; pub const MAX_MIP_LEVELS: u32 = 16; /// Size of a single occlusion/timestamp query, when copied into a buffer, in bytes. pub const QUERY_SIZE: wgt::BufferAddress = 8; pub type Label<'a> = Option<&'a str>; pub type MemoryRange = Range; pub type FenceValue = u64; /// Drop guard to signal wgpu-hal is no longer using an externally created object. pub type DropGuard = Box; #[derive(Clone, Debug, PartialEq, Eq, Error)] pub enum DeviceError { #[error("Out of memory")] OutOfMemory, #[error("Device is lost")] Lost, #[error("Creation of a resource failed for a reason other than running out of memory.")] ResourceCreationFailed, } #[derive(Clone, Debug, Eq, PartialEq, Error)] pub enum ShaderError { #[error("Compilation failed: {0:?}")] Compilation(String), #[error(transparent)] Device(#[from] DeviceError), } #[derive(Clone, Debug, Eq, PartialEq, Error)] pub enum PipelineError { #[error("Linkage failed for stage {0:?}: {1}")] Linkage(wgt::ShaderStages, String), #[error("Entry point for stage {0:?} is invalid")] EntryPoint(naga::ShaderStage), #[error(transparent)] Device(#[from] DeviceError), } #[derive(Clone, Debug, Eq, PartialEq, Error)] pub enum SurfaceError { #[error("Surface is lost")] Lost, #[error("Surface is outdated, needs to be re-created")] Outdated, #[error(transparent)] Device(#[from] DeviceError), #[error("Other reason: {0}")] Other(&'static str), } /// Error occurring while trying to create an instance, or create a surface from an instance; /// typically relating to the state of the underlying graphics API or hardware. #[derive(Clone, Debug, Error)] #[error("{message}")] pub struct InstanceError { /// These errors are very platform specific, so do not attempt to encode them as an enum. /// /// This message should describe the problem in sufficient detail to be useful for a /// user-to-developer “why won't this work on my machine” bug report, and otherwise follow /// . message: String, /// Underlying error value, if any is available. #[source] source: Option>, } impl InstanceError { #[allow(dead_code)] // may be unused on some platforms pub(crate) fn new(message: String) -> Self { Self { message, source: None, } } #[allow(dead_code)] // may be unused on some platforms pub(crate) fn with_source( message: String, source: impl std::error::Error + Send + Sync + 'static, ) -> Self { Self { message, source: Some(Arc::new(source)), } } } pub trait Api: Clone + fmt::Debug + Sized { type Instance: Instance; type Surface: Surface; type Adapter: Adapter; type Device: Device; type Queue: Queue; type CommandEncoder: CommandEncoder; type CommandBuffer: WasmNotSendSync + fmt::Debug; type Buffer: fmt::Debug + WasmNotSendSync + 'static; type Texture: fmt::Debug + WasmNotSendSync + 'static; type SurfaceTexture: fmt::Debug + WasmNotSendSync + Borrow; type TextureView: fmt::Debug + WasmNotSendSync; type Sampler: fmt::Debug + WasmNotSendSync; type QuerySet: fmt::Debug + WasmNotSendSync; type Fence: fmt::Debug + WasmNotSendSync; type BindGroupLayout: fmt::Debug + WasmNotSendSync; type BindGroup: fmt::Debug + WasmNotSendSync; type PipelineLayout: fmt::Debug + WasmNotSendSync; type ShaderModule: fmt::Debug + WasmNotSendSync; type RenderPipeline: fmt::Debug + WasmNotSendSync; type ComputePipeline: fmt::Debug + WasmNotSendSync; type AccelerationStructure: fmt::Debug + WasmNotSendSync + 'static; } pub trait Instance: Sized + WasmNotSendSync { type A: Api; unsafe fn init(desc: &InstanceDescriptor) -> Result; unsafe fn create_surface( &self, display_handle: raw_window_handle::RawDisplayHandle, window_handle: raw_window_handle::RawWindowHandle, ) -> Result<::Surface, InstanceError>; unsafe fn destroy_surface(&self, surface: ::Surface); unsafe fn enumerate_adapters(&self) -> Vec>; } pub trait Surface: WasmNotSendSync { type A: Api; /// Configures the surface to use the given device. /// /// # Safety /// /// - All gpu work that uses the surface must have been completed. /// - All [`AcquiredSurfaceTexture`]s must have been destroyed. /// - All [`Api::TextureView`]s derived from the [`AcquiredSurfaceTexture`]s must have been destroyed. /// - All surfaces created using other devices must have been unconfigured before this call. unsafe fn configure( &self, device: &::Device, config: &SurfaceConfiguration, ) -> Result<(), SurfaceError>; /// Unconfigures the surface on the given device. /// /// # Safety /// /// - All gpu work that uses the surface must have been completed. /// - All [`AcquiredSurfaceTexture`]s must have been destroyed. /// - All [`Api::TextureView`]s derived from the [`AcquiredSurfaceTexture`]s must have been destroyed. /// - The surface must have been configured on the given device. unsafe fn unconfigure(&self, device: &::Device); /// Returns the next texture to be presented by the swapchain for drawing /// /// A `timeout` of `None` means to wait indefinitely, with no timeout. /// /// # Portability /// /// Some backends can't support a timeout when acquiring a texture and /// the timeout will be ignored. /// /// Returns `None` on timing out. unsafe fn acquire_texture( &self, timeout: Option, ) -> Result>, SurfaceError>; unsafe fn discard_texture(&self, texture: ::SurfaceTexture); } pub trait Adapter: WasmNotSendSync { type A: Api; unsafe fn open( &self, features: wgt::Features, limits: &wgt::Limits, ) -> Result, DeviceError>; /// Return the set of supported capabilities for a texture format. unsafe fn texture_format_capabilities( &self, format: wgt::TextureFormat, ) -> TextureFormatCapabilities; /// Returns the capabilities of working with a specified surface. /// /// `None` means presentation is not supported for it. unsafe fn surface_capabilities( &self, surface: &::Surface, ) -> Option; /// Creates a [`PresentationTimestamp`] using the adapter's WSI. /// /// [`PresentationTimestamp`]: wgt::PresentationTimestamp unsafe fn get_presentation_timestamp(&self) -> wgt::PresentationTimestamp; } pub trait Device: WasmNotSendSync { type A: Api; /// Exit connection to this logical device. unsafe fn exit(self, queue: ::Queue); /// Creates a new buffer. /// /// The initial usage is `BufferUses::empty()`. unsafe fn create_buffer( &self, desc: &BufferDescriptor, ) -> Result<::Buffer, DeviceError>; unsafe fn destroy_buffer(&self, buffer: ::Buffer); //TODO: clarify if zero-sized mapping is allowed unsafe fn map_buffer( &self, buffer: &::Buffer, range: MemoryRange, ) -> Result; unsafe fn unmap_buffer(&self, buffer: &::Buffer) -> Result<(), DeviceError>; unsafe fn flush_mapped_ranges(&self, buffer: &::Buffer, ranges: I) where I: Iterator; unsafe fn invalidate_mapped_ranges(&self, buffer: &::Buffer, ranges: I) where I: Iterator; /// Creates a new texture. /// /// The initial usage for all subresources is `TextureUses::UNINITIALIZED`. unsafe fn create_texture( &self, desc: &TextureDescriptor, ) -> Result<::Texture, DeviceError>; unsafe fn destroy_texture(&self, texture: ::Texture); unsafe fn create_texture_view( &self, texture: &::Texture, desc: &TextureViewDescriptor, ) -> Result<::TextureView, DeviceError>; unsafe fn destroy_texture_view(&self, view: ::TextureView); unsafe fn create_sampler( &self, desc: &SamplerDescriptor, ) -> Result<::Sampler, DeviceError>; unsafe fn destroy_sampler(&self, sampler: ::Sampler); /// Create a fresh [`CommandEncoder`]. /// /// The new `CommandEncoder` is in the "closed" state. unsafe fn create_command_encoder( &self, desc: &CommandEncoderDescriptor, ) -> Result<::CommandEncoder, DeviceError>; unsafe fn destroy_command_encoder(&self, pool: ::CommandEncoder); /// Creates a bind group layout. unsafe fn create_bind_group_layout( &self, desc: &BindGroupLayoutDescriptor, ) -> Result<::BindGroupLayout, DeviceError>; unsafe fn destroy_bind_group_layout(&self, bg_layout: ::BindGroupLayout); unsafe fn create_pipeline_layout( &self, desc: &PipelineLayoutDescriptor, ) -> Result<::PipelineLayout, DeviceError>; unsafe fn destroy_pipeline_layout(&self, pipeline_layout: ::PipelineLayout); unsafe fn create_bind_group( &self, desc: &BindGroupDescriptor, ) -> Result<::BindGroup, DeviceError>; unsafe fn destroy_bind_group(&self, group: ::BindGroup); unsafe fn create_shader_module( &self, desc: &ShaderModuleDescriptor, shader: ShaderInput, ) -> Result<::ShaderModule, ShaderError>; unsafe fn destroy_shader_module(&self, module: ::ShaderModule); unsafe fn create_render_pipeline( &self, desc: &RenderPipelineDescriptor, ) -> Result<::RenderPipeline, PipelineError>; unsafe fn destroy_render_pipeline(&self, pipeline: ::RenderPipeline); unsafe fn create_compute_pipeline( &self, desc: &ComputePipelineDescriptor, ) -> Result<::ComputePipeline, PipelineError>; unsafe fn destroy_compute_pipeline(&self, pipeline: ::ComputePipeline); unsafe fn create_query_set( &self, desc: &wgt::QuerySetDescriptor