//! A cross-platform graphics and compute library based on WebGPU. mod backend; #[macro_use] mod macros; use std::{ future::Future, marker::PhantomData, ops::{Bound, Range, RangeBounds}, sync::Arc, thread, }; use futures::FutureExt as _; use parking_lot::Mutex; #[cfg(not(target_arch = "wasm32"))] pub use wgc::instance::{AdapterInfo, DeviceType}; pub use wgt::{ read_spirv, AddressMode, Backend, BackendBit, BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingType, BlendDescriptor, BlendFactor, BlendOperation, BufferAddress, BufferSize, BufferUsage, Color, ColorStateDescriptor, ColorWrite, CommandBufferDescriptor, CompareFunction, CullMode, DepthStencilStateDescriptor, DeviceDescriptor, DynamicOffset, Extensions, Extent3d, FilterMode, FrontFace, IndexFormat, InputStepMode, Limits, LoadOp, Origin3d, PowerPreference, PresentMode, PrimitiveTopology, RasterizationStateDescriptor, ShaderLocation, ShaderStage, StencilOperation, StencilStateFaceDescriptor, StoreOp, SwapChainDescriptor, SwapChainStatus, TextureAspect, TextureComponentType, TextureDataLayout, TextureDimension, TextureFormat, TextureUsage, TextureViewDimension, UnsafeExtensions, VertexAttributeDescriptor, VertexFormat, BIND_BUFFER_ALIGNMENT, COPY_BYTES_PER_ROW_ALIGNMENT, }; use backend::Context as C; trait ComputePassInner { fn set_pipeline(&mut self, pipeline: &Ctx::ComputePipelineId); fn set_bind_group( &mut self, index: u32, bind_group: &Ctx::BindGroupId, offsets: &[DynamicOffset], ); fn dispatch(&mut self, x: u32, y: u32, z: u32); fn dispatch_indirect( &mut self, indirect_buffer: &Ctx::BufferId, indirect_offset: BufferAddress, ); } trait RenderPassInner { fn set_pipeline(&mut self, pipeline: &Ctx::RenderPipelineId); fn set_bind_group( &mut self, index: u32, bind_group: &Ctx::BindGroupId, offsets: &[DynamicOffset], ); fn set_index_buffer(&mut self, buffer: &Ctx::BufferId, offset: BufferAddress, size: BufferSize); fn set_vertex_buffer( &mut self, slot: u32, buffer: &Ctx::BufferId, offset: BufferAddress, size: BufferSize, ); fn set_blend_color(&mut self, color: wgt::Color); fn set_scissor_rect(&mut self, x: u32, y: u32, width: u32, height: u32); fn set_viewport( &mut self, x: f32, y: f32, width: f32, height: f32, min_depth: f32, max_depth: f32, ); fn set_stencil_reference(&mut self, reference: u32); fn draw(&mut self, vertices: Range, instances: Range); fn draw_indexed(&mut self, indices: Range, base_vertex: i32, instances: Range); fn draw_indirect(&mut self, indirect_buffer: &Ctx::BufferId, indirect_offset: BufferAddress); fn draw_indexed_indirect( &mut self, indirect_buffer: &Ctx::BufferId, indirect_offset: BufferAddress, ); } trait Context: Sized { type AdapterId: Send + Sync; type DeviceId: Send + Sync; type QueueId: Send + Sync; type ShaderModuleId: Send + Sync; type BindGroupLayoutId: Send + Sync; type BindGroupId: Send + Sync; type TextureViewId: Send + Sync; type SamplerId: Send + Sync; type BufferId: Send + Sync; type TextureId: Send + Sync; type PipelineLayoutId: Send + Sync; type RenderPipelineId: Send + Sync; type ComputePipelineId: Send + Sync; type CommandEncoderId; type ComputePassId: ComputePassInner; type CommandBufferId: Send + Sync; type SurfaceId: Send + Sync; type SwapChainId: Send + Sync; type RenderPassId: RenderPassInner; type SwapChainOutputDetail: Send; type RequestAdapterFuture: Future> + Send; type RequestDeviceFuture: Future> + Send; type MapAsyncFuture: Future> + Send; fn init() -> Self; fn instance_create_surface( &self, handle: &impl raw_window_handle::HasRawWindowHandle, ) -> Self::SurfaceId; fn instance_request_adapter( &self, options: &RequestAdapterOptions<'_>, unsafe_extensions: wgt::UnsafeExtensions, backends: wgt::BackendBit, ) -> Self::RequestAdapterFuture; fn adapter_request_device( &self, adapter: &Self::AdapterId, desc: &DeviceDescriptor, trace_dir: Option<&std::path::Path>, ) -> Self::RequestDeviceFuture; fn adapter_extensions(&self, adapter: &Self::AdapterId) -> Extensions; fn adapter_limits(&self, adapter: &Self::AdapterId) -> Limits; fn device_extensions(&self, device: &Self::DeviceId) -> Extensions; fn device_limits(&self, device: &Self::DeviceId) -> Limits; fn device_create_swap_chain( &self, device: &Self::DeviceId, surface: &Self::SurfaceId, desc: &SwapChainDescriptor, ) -> Self::SwapChainId; fn device_create_shader_module( &self, device: &Self::DeviceId, spv: &[u32], ) -> Self::ShaderModuleId; fn device_create_bind_group_layout( &self, device: &Self::DeviceId, desc: &BindGroupLayoutDescriptor, ) -> Self::BindGroupLayoutId; fn device_create_bind_group( &self, device: &Self::DeviceId, desc: &BindGroupDescriptor, ) -> Self::BindGroupId; fn device_create_pipeline_layout( &self, device: &Self::DeviceId, desc: &PipelineLayoutDescriptor, ) -> Self::PipelineLayoutId; fn device_create_render_pipeline( &self, device: &Self::DeviceId, desc: &RenderPipelineDescriptor, ) -> Self::RenderPipelineId; fn device_create_compute_pipeline( &self, device: &Self::DeviceId, desc: &ComputePipelineDescriptor, ) -> Self::ComputePipelineId; fn device_create_buffer( &self, device: &Self::DeviceId, desc: &BufferDescriptor, ) -> Self::BufferId; fn device_create_texture( &self, device: &Self::DeviceId, desc: &TextureDescriptor, ) -> Self::TextureId; fn device_create_sampler( &self, device: &Self::DeviceId, desc: &SamplerDescriptor, ) -> Self::SamplerId; fn device_create_command_encoder( &self, device: &Self::DeviceId, desc: &CommandEncoderDescriptor, ) -> Self::CommandEncoderId; fn device_drop(&self, device: &Self::DeviceId); fn device_poll(&self, device: &Self::DeviceId, maintain: Maintain); fn buffer_map_async( &self, buffer: &Self::BufferId, mode: MapMode, range: Range, ) -> Self::MapAsyncFuture; //TODO: we might be able to merge these, depending on how Web backend // turns out to be implemented. fn buffer_get_mapped_range( &self, buffer: &Self::BufferId, sub_range: Range, ) -> &[u8]; fn buffer_get_mapped_range_mut( &self, buffer: &Self::BufferId, sub_range: Range, ) -> &mut [u8]; fn buffer_unmap(&self, buffer: &Self::BufferId); fn swap_chain_get_next_texture( &self, swap_chain: &Self::SwapChainId, ) -> ( Option, SwapChainStatus, Self::SwapChainOutputDetail, ); fn swap_chain_present(&self, view: &Self::TextureViewId, detail: &Self::SwapChainOutputDetail); fn texture_create_view( &self, texture: &Self::TextureId, desc: Option<&TextureViewDescriptor>, ) -> Self::TextureViewId; fn texture_drop(&self, texture: &Self::TextureId); fn texture_view_drop(&self, texture_view: &Self::TextureViewId); fn sampler_drop(&self, sampler: &Self::SamplerId); fn buffer_drop(&self, buffer: &Self::BufferId); fn bind_group_drop(&self, bind_group: &Self::BindGroupId); fn bind_group_layout_drop(&self, bind_group_layout: &Self::BindGroupLayoutId); fn pipeline_layout_drop(&self, pipeline_layout: &Self::PipelineLayoutId); fn shader_module_drop(&self, shader_module: &Self::ShaderModuleId); fn command_buffer_drop(&self, command_buffer: &Self::CommandBufferId); fn compute_pipeline_drop(&self, pipeline: &Self::ComputePipelineId); fn render_pipeline_drop(&self, pipeline: &Self::RenderPipelineId); fn encoder_copy_buffer_to_buffer( &self, encoder: &Self::CommandEncoderId, source: &Self::BufferId, source_offset: BufferAddress, destination: &Self::BufferId, destination_offset: BufferAddress, copy_size: BufferAddress, ); fn encoder_copy_buffer_to_texture( &self, encoder: &Self::CommandEncoderId, source: BufferCopyView, destination: TextureCopyView, copy_size: Extent3d, ); fn encoder_copy_texture_to_buffer( &self, encoder: &Self::CommandEncoderId, source: TextureCopyView, destination: BufferCopyView, copy_size: Extent3d, ); fn encoder_copy_texture_to_texture( &self, encoder: &Self::CommandEncoderId, source: TextureCopyView, destination: TextureCopyView, copy_size: Extent3d, ); fn encoder_begin_compute_pass(&self, encoder: &Self::CommandEncoderId) -> Self::ComputePassId; fn encoder_end_compute_pass( &self, encoder: &Self::CommandEncoderId, pass: &mut Self::ComputePassId, ); fn encoder_begin_render_pass<'a>( &self, encoder: &Self::CommandEncoderId, desc: &RenderPassDescriptor<'a, '_>, ) -> Self::RenderPassId; fn encoder_end_render_pass( &self, encoder: &Self::CommandEncoderId, pass: &mut Self::RenderPassId, ); fn encoder_finish(&self, encoder: &Self::CommandEncoderId) -> Self::CommandBufferId; fn queue_write_buffer( &self, queue: &Self::QueueId, buffer: &Self::BufferId, offset: BufferAddress, data: &[u8], ); fn queue_write_texture( &self, queue: &Self::QueueId, texture: TextureCopyView, data: &[u8], data_layout: wgt::TextureDataLayout, size: wgt::Extent3d, ); fn queue_submit>( &self, queue: &Self::QueueId, command_buffers: I, ); } /// An instance sets up the context for all other wgpu objects. /// /// An `Adapter` can be used to open a connection to the corresponding device on the host system, /// yielding a [`Device`] object. pub struct Instance { context: Arc, } /// A handle to a physical graphics and/or compute device. /// /// An `Adapter` can be used to open a connection to the corresponding device on the host system, /// yielding a [`Device`] object. pub struct Adapter { context: Arc, id: ::AdapterId, } /// Options for requesting adapter. #[derive(Clone)] pub struct RequestAdapterOptions<'a> { /// Power preference for the adapter. pub power_preference: PowerPreference, /// Surface that is required to be presentable with the requested adapter. pub compatible_surface: Option<&'a Surface>, } /// An open connection to a graphics and/or compute device. /// /// The `Device` is the responsible for the creation of most rendering and compute resources, as /// well as exposing [`Queue`] objects. pub struct Device { context: Arc, id: ::DeviceId, } /// This is passed to `Device::poll` to control whether /// it should block or not. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum Maintain { Wait, Poll, } /// The main purpose of this struct is to resolve mapped ranges /// (convert sizes to end points), and to ensure that the sub-ranges /// don't intersect. #[derive(Debug)] struct MapContext { total_size: BufferAddress, initial_range: Range, sub_ranges: Vec>, } impl MapContext { fn new(total_size: BufferAddress) -> Self { MapContext { total_size, initial_range: 0..0, sub_ranges: Vec::new(), } } fn reset(&mut self) { self.initial_range = 0..0; self.sub_ranges.clear(); } fn add(&mut self, offset: BufferAddress, size: BufferSize) -> BufferAddress { let end = if size == BufferSize::WHOLE { self.initial_range.end } else { offset + size.0 }; assert!(self.initial_range.start <= offset && end <= self.initial_range.end); for sub in self.sub_ranges.iter() { assert!( end <= sub.start || offset >= sub.end, "Intersecting map range with {:?}", sub ); } self.sub_ranges.push(offset..end); end } } /// A handle to a GPU-accessible buffer. pub struct Buffer { context: Arc, id: ::BufferId, map_context: Mutex, } /// A description of what portion of a buffer to use pub struct BufferSlice<'a> { buffer: &'a Buffer, offset: BufferAddress, size: BufferSize, } /// A handle to a texture on the GPU. pub struct Texture { context: Arc, id: ::TextureId, owned: bool, } /// A handle to a texture view. /// /// A `TextureView` object describes a texture and associated metadata needed by a /// [`RenderPipeline`] or [`BindGroup`]. pub struct TextureView { context: Arc, id: ::TextureViewId, owned: bool, } /// A handle to a sampler. /// /// A `Sampler` object defines how a pipeline will sample from a [`TextureView`]. Samplers define /// image filters (including anisotropy) and address (wrapping) modes, among other things. See /// the documentation for [`SamplerDescriptor`] for more information. pub struct Sampler { context: Arc, id: ::SamplerId, } impl Drop for Sampler { fn drop(&mut self) { self.context.sampler_drop(&self.id); } } /// A handle to a presentable surface. /// /// A `Surface` represents a platform-specific surface (e.g. a window) to which rendered images may /// be presented. A `Surface` may be created with [`Surface::create`]. pub struct Surface { id: ::SurfaceId, } /// A handle to a swap chain. /// /// A `SwapChain` represents the image or series of images that will be presented to a [`Surface`]. /// A `SwapChain` may be created with [`Device::create_swap_chain`]. pub struct SwapChain { context: Arc, id: ::SwapChainId, } /// An opaque handle to a binding group layout. /// /// A `BindGroupLayout` is a handle to the GPU-side layout of a binding group. It can be used to /// create a [`BindGroupDescriptor`] object, which in turn can be used to create a [`BindGroup`] /// object with [`Device::create_bind_group`]. A series of `BindGroupLayout`s can also be used to /// create a [`PipelineLayoutDescriptor`], which can be used to create a [`PipelineLayout`]. pub struct BindGroupLayout { context: Arc, id: ::BindGroupLayoutId, } impl Drop for BindGroupLayout { fn drop(&mut self) { self.context.bind_group_layout_drop(&self.id); } } /// An opaque handle to a binding group. /// /// A `BindGroup` represents the set of resources bound to the bindings described by a /// [`BindGroupLayout`]. It can be created with [`Device::create_bind_group`]. A `BindGroup` can /// be bound to a particular [`RenderPass`] with [`RenderPass::set_bind_group`], or to a /// [`ComputePass`] with [`ComputePass::set_bind_group`]. pub struct BindGroup { context: Arc, id: ::BindGroupId, } impl Drop for BindGroup { fn drop(&mut self) { self.context.bind_group_drop(&self.id); } } /// A handle to a compiled shader module. /// /// A `ShaderModule` represents a compiled shader module on the GPU. It can be created by passing /// valid SPIR-V source code to [`Device::create_shader_module`]. Shader modules are used to define /// programmable stages of a pipeline. pub struct ShaderModule { context: Arc, id: ::ShaderModuleId, } impl Drop for ShaderModule { fn drop(&mut self) { self.context.shader_module_drop(&self.id); } } /// An opaque handle to a pipeline layout. /// /// A `PipelineLayout` object describes the available binding groups of a pipeline. pub struct PipelineLayout { context: Arc, id: ::PipelineLayoutId, } impl Drop for PipelineLayout { fn drop(&mut self) { self.context.pipeline_layout_drop(&self.id); } } /// A handle to a rendering (graphics) pipeline. /// /// A `RenderPipeline` object represents a graphics pipeline and its stages, bindings, vertex /// buffers and targets. A `RenderPipeline` may be created with [`Device::create_render_pipeline`]. pub struct RenderPipeline { context: Arc, id: ::RenderPipelineId, } impl Drop for RenderPipeline { fn drop(&mut self) { self.context.render_pipeline_drop(&self.id); } } /// A handle to a compute pipeline. pub struct ComputePipeline { context: Arc, id: ::ComputePipelineId, } impl Drop for ComputePipeline { fn drop(&mut self) { self.context.compute_pipeline_drop(&self.id); } } /// An opaque handle to a command buffer on the GPU. /// /// A `CommandBuffer` represents a complete sequence of commands that may be submitted to a command /// queue with [`Queue::submit`]. A `CommandBuffer` is obtained by recording a series of commands to /// a [`CommandEncoder`] and then calling [`CommandEncoder::finish`]. pub struct CommandBuffer { context: Arc, id: Option<::CommandBufferId>, } impl Drop for CommandBuffer { fn drop(&mut self) { if let Some(ref id) = self.id { self.context.command_buffer_drop(id); } } } /// An object that encodes GPU operations. /// /// A `CommandEncoder` can record [`RenderPass`]es, [`ComputePass`]es, and transfer operations /// between driver-managed resources like [`Buffer`]s and [`Texture`]s. /// /// When finished recording, call [`CommandEncoder::finish`] to obtain a [`CommandBuffer`] which may /// be submitted for execution. pub struct CommandEncoder { context: Arc, id: ::CommandEncoderId, /// This type should be !Send !Sync, because it represents an allocation on this thread's /// command buffer. _p: PhantomData<*const u8>, } /// An in-progress recording of a render pass. pub struct RenderPass<'a> { id: ::RenderPassId, parent: &'a mut CommandEncoder, } /// An in-progress recording of a compute pass. pub struct ComputePass<'a> { id: ::ComputePassId, parent: &'a mut CommandEncoder, } /// A handle to a command queue on a device. /// /// A `Queue` executes recorded [`CommandBuffer`] objects. pub struct Queue { context: Arc, id: ::QueueId, } /// A resource that can be bound to a pipeline. #[non_exhaustive] pub enum BindingResource<'a> { Buffer(BufferSlice<'a>), Sampler(&'a Sampler), TextureView(&'a TextureView), TextureViewArray(&'a [TextureView]), } /// A bindable resource and the slot to bind it to. pub struct Binding<'a> { pub binding: u32, pub resource: BindingResource<'a>, } /// A description of a group of bindings and the resources to be bound. #[derive(Clone)] pub struct BindGroupDescriptor<'a> { /// The layout for this bind group. pub layout: &'a BindGroupLayout, /// The resources to bind to this bind group. pub bindings: &'a [Binding<'a>], /// An optional label to apply to the bind group. /// This can be useful for debugging and performance analysis. pub label: Option<&'a str>, } /// A description of a pipeline layout. /// /// A `PipelineLayoutDescriptor` can be passed to [`Device::create_pipeline_layout`] to obtain a /// [`PipelineLayout`]. #[derive(Clone)] pub struct PipelineLayoutDescriptor<'a> { pub bind_group_layouts: &'a [&'a BindGroupLayout], } /// A description of a programmable pipeline stage. #[derive(Clone)] pub struct ProgrammableStageDescriptor<'a> { /// The compiled shader module for this stage. pub module: &'a ShaderModule, /// The name of the entry point in the compiled shader. pub entry_point: &'a str, } /// The vertex input state for a render pipeline. #[derive(Clone, Debug)] pub struct VertexStateDescriptor<'a> { /// The format of any index buffers used with this pipeline. pub index_format: IndexFormat, /// The format of any vertex buffers used with this pipeline. pub vertex_buffers: &'a [VertexBufferDescriptor<'a>], } /// A description of a vertex buffer. #[derive(Clone, Debug)] pub struct VertexBufferDescriptor<'a> { /// The stride, in bytes, between elements of this buffer. pub stride: BufferAddress, pub step_mode: InputStepMode, /// The list of attributes which comprise a single vertex. pub attributes: &'a [VertexAttributeDescriptor], } /// A complete description of a render (graphics) pipeline. #[derive(Clone)] pub struct RenderPipelineDescriptor<'a> { /// The layout of bind groups for this pipeline. pub layout: &'a PipelineLayout, /// The compiled vertex stage and its entry point. pub vertex_stage: ProgrammableStageDescriptor<'a>, /// The compiled fragment stage and its entry point, if any. pub fragment_stage: Option>, /// The rasterization process for this pipeline. pub rasterization_state: Option, /// The primitive topology used to interpret vertices. pub primitive_topology: PrimitiveTopology, /// The effect of draw calls on the color aspect of the output target. pub color_states: &'a [ColorStateDescriptor], /// The effect of draw calls on the depth and stencil aspects of the output target, if any. pub depth_stencil_state: Option, /// The vertex input state for this pipeline. pub vertex_state: VertexStateDescriptor<'a>, /// The number of samples calculated per pixel (for MSAA). pub sample_count: u32, /// Bitmask that restricts the samples of a pixel modified by this pipeline. pub sample_mask: u32, /// When enabled, produces another sample mask per pixel based on the alpha output value, that /// is ANDed with the sample_mask and the primitive coverage to restrict the set of samples /// affected by a primitive. /// The implicit mask produced for alpha of zero is guaranteed to be zero, and for alpha of one /// is guaranteed to be all 1-s. pub alpha_to_coverage_enabled: bool, } /// A complete description of a compute pipeline. #[derive(Clone)] pub struct ComputePipelineDescriptor<'a> { /// The layout of bind groups for this pipeline. pub layout: &'a PipelineLayout, /// The compiled compute stage and its entry point. pub compute_stage: ProgrammableStageDescriptor<'a>, } pub type RenderPassColorAttachmentDescriptor<'a> = wgt::RenderPassColorAttachmentDescriptorBase<&'a TextureView>; pub type RenderPassDepthStencilAttachmentDescriptor<'a> = wgt::RenderPassDepthStencilAttachmentDescriptorBase<&'a TextureView>; /// A description of all the attachments of a render pass. pub struct RenderPassDescriptor<'a, 'b> { /// The color attachments of the render pass. pub color_attachments: &'b [RenderPassColorAttachmentDescriptor<'a>], /// The depth and stencil attachment of the render pass, if any. pub depth_stencil_attachment: Option>, } /// A description of a buffer. pub type BufferDescriptor<'a> = wgt::BufferDescriptor>; /// A description of a command encoder. #[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] pub struct CommandEncoderDescriptor<'a> { /// An optional label to apply to the command encoder. /// This can be useful for debugging and performance analysis. pub label: Option<&'a str>, } /// A description of a texture. pub type TextureDescriptor<'a> = wgt::TextureDescriptor>; /// A description of a texture view. pub type TextureViewDescriptor<'a> = wgt::TextureViewDescriptor>; /// A description of a sampler. pub type SamplerDescriptor<'a> = wgt::SamplerDescriptor>; /// A swap chain image that can be rendered to. pub struct SwapChainTexture { pub view: TextureView, detail: ::SwapChainOutputDetail, } /// The result of a successful call to `SwapChain::get_next_frame`. pub struct SwapChainFrame { pub output: SwapChainTexture, pub suboptimal: bool, } /// The result of an unsuccessful call to `SwapChain::get_next_frame`. #[derive(Debug)] pub enum SwapChainError { Timeout, Outdated, Lost, OutOfMemory, } /// A view of a buffer which can be used to copy to or from a texture. #[derive(Clone)] pub struct BufferCopyView<'a> { /// The buffer to be copied to or from. pub buffer: &'a Buffer, /// The layout of the texture data in this buffer. pub layout: wgt::TextureDataLayout, } /// A view of a texture which can be used to copy to or from a buffer or another texture. #[derive(Clone)] pub struct TextureCopyView<'a> { /// The texture to be copied to or from. pub texture: &'a Texture, /// The target mip level of the texture. pub mip_level: u32, /// The base texel of the texture in the selected `mip_level`. pub origin: Origin3d, } impl Instance { /// Create an new instance. pub fn new() -> Self { Instance { context: Arc::new(C::init()), } } /// Retrieves all available [`Adapter`]s that match the given backends. #[cfg(not(target_arch = "wasm32"))] pub fn enumerate_adapters( &self, unsafe_extensions: wgt::UnsafeExtensions, backends: wgt::BackendBit, ) -> impl Iterator { let context = Arc::clone(&self.context); self.context .enumerate_adapters( unsafe_extensions, wgc::instance::AdapterInputs::Mask(backends, |_| PhantomData), ) .into_iter() .map(move |id| crate::Adapter { id, context: Arc::clone(&context), }) } /// Creates a surface from a raw window handle. pub unsafe fn create_surface( &self, window: &W, ) -> Surface { Surface { id: Context::instance_create_surface(&*self.context, window), } } #[cfg(any(target_os = "ios", target_os = "macos"))] pub unsafe fn create_surface_from_core_animation_layer( &self, layer: *mut std::ffi::c_void, ) -> Surface { let surface = wgc::instance::Surface { #[cfg(feature = "vulkan-portability")] vulkan: self .context .instance .vulkan .create_surface_from_layer(layer as *mut _, cfg!(debug_assertions)), metal: self .context .instance .metal .create_surface_from_layer(layer as *mut _, cfg!(debug_assertions)), }; crate::Surface { id: self.context.surfaces.register_identity( PhantomData, surface, &mut wgc::hub::Token::root(), ), } } /// Retrieves an [`Adapter`] which matches the given options. /// /// Some options are "soft", so treated as non-mandatory. Others are "hard". /// /// If no adapters are found that suffice all the "hard" options, `None` is returned. pub fn request_adapter( &self, options: &RequestAdapterOptions<'_>, unsafe_extensions: wgt::UnsafeExtensions, backends: BackendBit, ) -> impl Future> + Send { let context = Arc::clone(&self.context); self.context .instance_request_adapter(options, unsafe_extensions, backends) .map(|option| option.map(|id| Adapter { context, id })) } } impl Adapter { /// Requests a connection to a physical device, creating a logical device. /// Returns the device together with a queue that executes command buffers. /// /// # Panics /// /// Panics if the extensions specified by `desc` are not supported by this adapter. pub fn request_device( &self, desc: &DeviceDescriptor, trace_path: Option<&std::path::Path>, ) -> impl Future> + Send { let context = Arc::clone(&self.context); Context::adapter_request_device(&*self.context, &self.id, desc, trace_path).map(|result| { result.map(|(device_id, queue_id)| { ( Device { context: Arc::clone(&context), id: device_id, }, Queue { context, id: queue_id, }, ) }) }) } pub fn extensions(&self) -> Extensions { Context::adapter_extensions(&*self.context, &self.id) } pub fn limits(&self) -> Limits { Context::adapter_limits(&*self.context, &self.id) } #[cfg(not(target_arch = "wasm32"))] pub fn get_info(&self) -> AdapterInfo { //wgn::adapter_get_info(self.id) unimplemented!() } } impl Device { /// Check for resource cleanups and mapping callbacks. pub fn poll(&self, maintain: Maintain) { Context::device_poll(&*self.context, &self.id, maintain); } pub fn extensions(&self) -> Extensions { Context::device_extensions(&*self.context, &self.id) } pub fn limits(&self) -> Limits { Context::device_limits(&*self.context, &self.id) } /// Creates a shader module from SPIR-V source code. pub fn create_shader_module(&self, spv: &[u32]) -> ShaderModule { ShaderModule { context: Arc::clone(&self.context), id: Context::device_create_shader_module(&*self.context, &self.id, spv), } } /// Creates an empty [`CommandEncoder`]. pub fn create_command_encoder(&self, desc: &CommandEncoderDescriptor) -> CommandEncoder { CommandEncoder { context: Arc::clone(&self.context), id: Context::device_create_command_encoder(&*self.context, &self.id, desc), _p: Default::default(), } } /// Creates a new bind group. pub fn create_bind_group(&self, desc: &BindGroupDescriptor) -> BindGroup { BindGroup { context: Arc::clone(&self.context), id: Context::device_create_bind_group(&*self.context, &self.id, desc), } } /// Creates a bind group layout. pub fn create_bind_group_layout(&self, desc: &BindGroupLayoutDescriptor) -> BindGroupLayout { BindGroupLayout { context: Arc::clone(&self.context), id: Context::device_create_bind_group_layout(&*self.context, &self.id, desc), } } /// Creates a pipeline layout. pub fn create_pipeline_layout(&self, desc: &PipelineLayoutDescriptor) -> PipelineLayout { PipelineLayout { context: Arc::clone(&self.context), id: Context::device_create_pipeline_layout(&*self.context, &self.id, desc), } } /// Creates a render pipeline. pub fn create_render_pipeline(&self, desc: &RenderPipelineDescriptor) -> RenderPipeline { RenderPipeline { context: Arc::clone(&self.context), id: Context::device_create_render_pipeline(&*self.context, &self.id, desc), } } /// Creates a compute pipeline. pub fn create_compute_pipeline(&self, desc: &ComputePipelineDescriptor) -> ComputePipeline { ComputePipeline { context: Arc::clone(&self.context), id: Context::device_create_compute_pipeline(&*self.context, &self.id, desc), } } /// Creates a new buffer. pub fn create_buffer(&self, desc: &BufferDescriptor) -> Buffer { let mut map_context = MapContext::new(desc.size); if desc.mapped_at_creation { map_context.initial_range = 0..desc.size; } Buffer { context: Arc::clone(&self.context), id: Context::device_create_buffer(&*self.context, &self.id, desc), map_context: Mutex::new(map_context), } } /// Creates a new buffer, maps it into host-visible memory, copies data from the given slice, /// and finally unmaps it, returning a [`Buffer`]. pub fn create_buffer_with_data(&self, data: &[u8], usage: BufferUsage) -> Buffer { let size = data.len() as u64; let buffer = self.create_buffer(&BufferDescriptor { label: None, size, usage, mapped_at_creation: true, }); Context::buffer_get_mapped_range_mut(&*self.context, &buffer.id, 0..size) .copy_from_slice(data); buffer.unmap(); buffer } /// Creates a new [`Texture`]. /// /// `desc` specifies the general format of the texture. pub fn create_texture(&self, desc: &TextureDescriptor) -> Texture { Texture { context: Arc::clone(&self.context), id: Context::device_create_texture(&*self.context, &self.id, desc), owned: true, } } /// Creates a new [`Sampler`]. /// /// `desc` specifies the behavior of the sampler. pub fn create_sampler(&self, desc: &SamplerDescriptor) -> Sampler { Sampler { context: Arc::clone(&self.context), id: Context::device_create_sampler(&*self.context, &self.id, desc), } } /// Create a new [`SwapChain`] which targets `surface`. pub fn create_swap_chain(&self, surface: &Surface, desc: &SwapChainDescriptor) -> SwapChain { SwapChain { context: Arc::clone(&self.context), id: Context::device_create_swap_chain(&*self.context, &self.id, &surface.id, desc), } } } impl Drop for Device { fn drop(&mut self) { self.context.device_drop(&self.id); } } #[derive(Clone, PartialEq, Eq, Debug)] pub struct RequestDeviceError; #[derive(Clone, PartialEq, Eq, Debug)] pub struct BufferAsyncError; #[derive(Debug, Clone, Copy, PartialEq)] pub enum MapMode { Read, Write, } impl Buffer { /// Use only a portion of this Buffer for a given operation. Choosing a range with 0 size will /// return a slice that extends to the end of the buffer. pub fn slice>(&self, bounds: S) -> BufferSlice { let offset = match bounds.start_bound() { Bound::Included(&bound) => bound, Bound::Excluded(&bound) => bound + 1, Bound::Unbounded => 0, }; let size = match bounds.end_bound() { Bound::Included(&bound) => BufferSize(bound + 1 - offset), Bound::Excluded(&bound) => BufferSize(bound - offset), Bound::Unbounded => BufferSize::WHOLE, }; BufferSlice { buffer: self, offset, size, } } /// Map the buffer. Buffer is ready to map once the future is resolved. /// /// For the future to complete, `device.poll(...)` must be called elsewhere in the runtime, possibly integrated /// into an event loop, run on a separate thread, or continually polled in the same task runtime that this /// future will be run on. /// /// It's expected that wgpu will eventually supply its own event loop infrastructure that will be easy to integrate /// into other event loops, like winit's. pub fn map_async( &self, mode: MapMode, offset: BufferAddress, size: BufferSize, ) -> impl Future> + Send { let end = { let mut mc = self.map_context.lock(); assert_eq!( mc.initial_range, 0..0, "Buffer {:?} is already mapped", self.id ); let end = if size == BufferSize::WHOLE { mc.total_size } else { offset + size.0 }; mc.initial_range = offset..end; end }; Context::buffer_map_async(&*self.context, &self.id, mode, offset..end) } pub fn get_mapped_range(&self, offset: BufferAddress, size: BufferSize) -> &[u8] { let end = self.map_context.lock().add(offset, size); Context::buffer_get_mapped_range(&*self.context, &self.id, offset..end) } pub fn get_mapped_range_mut(&self, offset: BufferAddress, size: BufferSize) -> &mut [u8] { let end = self.map_context.lock().add(offset, size); Context::buffer_get_mapped_range_mut(&*self.context, &self.id, offset..end) } /// Flushes any pending write operations and unmaps the buffer from host memory. pub fn unmap(&self) { self.map_context.lock().reset(); Context::buffer_unmap(&*self.context, &self.id); } } impl Drop for Buffer { fn drop(&mut self) { self.context.buffer_drop(&self.id); } } impl Texture { /// Creates a view of this texture. pub fn create_view(&self, desc: &TextureViewDescriptor) -> TextureView { TextureView { context: Arc::clone(&self.context), id: Context::texture_create_view(&*self.context, &self.id, Some(desc)), owned: true, } } /// Creates a default view of this whole texture. pub fn create_default_view(&self) -> TextureView { TextureView { context: Arc::clone(&self.context), id: Context::texture_create_view(&*self.context, &self.id, None), owned: true, } } } impl Drop for Texture { fn drop(&mut self) { if self.owned { self.context.texture_drop(&self.id); } } } impl Drop for TextureView { fn drop(&mut self) { if self.owned { self.context.texture_view_drop(&self.id); } } } impl CommandEncoder { /// Finishes recording and returns a [`CommandBuffer`] that can be submitted for execution. pub fn finish(self) -> CommandBuffer { CommandBuffer { context: Arc::clone(&self.context), id: Some(Context::encoder_finish(&*self.context, &self.id)), } } /// Begins recording of a render pass. /// /// This function returns a [`RenderPass`] object which records a single render pass. pub fn begin_render_pass<'a>( &'a mut self, desc: &RenderPassDescriptor<'a, '_>, ) -> RenderPass<'a> { RenderPass { id: Context::encoder_begin_render_pass(&*self.context, &self.id, desc), parent: self, } } /// Begins recording of a compute pass. /// /// This function returns a [`ComputePass`] object which records a single compute pass. pub fn begin_compute_pass(&mut self) -> ComputePass { ComputePass { id: Context::encoder_begin_compute_pass(&*self.context, &self.id), parent: self, } } /// Copy data from one buffer to another. pub fn copy_buffer_to_buffer( &mut self, source: &Buffer, source_offset: BufferAddress, destination: &Buffer, destination_offset: BufferAddress, copy_size: BufferAddress, ) { Context::encoder_copy_buffer_to_buffer( &*self.context, &self.id, &source.id, source_offset, &destination.id, destination_offset, copy_size, ); } /// Copy data from a buffer to a texture. pub fn copy_buffer_to_texture( &mut self, source: BufferCopyView, destination: TextureCopyView, copy_size: Extent3d, ) { Context::encoder_copy_buffer_to_texture( &*self.context, &self.id, source, destination, copy_size, ); } /// Copy data from a texture to a buffer. pub fn copy_texture_to_buffer( &mut self, source: TextureCopyView, destination: BufferCopyView, copy_size: Extent3d, ) { Context::encoder_copy_texture_to_buffer( &*self.context, &self.id, source, destination, copy_size, ); } /// Copy data from one texture to another. pub fn copy_texture_to_texture( &mut self, source: TextureCopyView, destination: TextureCopyView, copy_size: Extent3d, ) { Context::encoder_copy_texture_to_texture( &*self.context, &self.id, source, destination, copy_size, ); } } impl<'a> RenderPass<'a> { /// Sets the active bind group for a given bind group index. pub fn set_bind_group( &mut self, index: u32, bind_group: &'a BindGroup, offsets: &[DynamicOffset], ) { RenderPassInner::set_bind_group(&mut self.id, index, &bind_group.id, offsets) } /// Sets the active render pipeline. /// /// Subsequent draw calls will exhibit the behavior defined by `pipeline`. pub fn set_pipeline(&mut self, pipeline: &'a RenderPipeline) { RenderPassInner::set_pipeline(&mut self.id, &pipeline.id) } pub fn set_blend_color(&mut self, color: Color) { self.id.set_blend_color(color) } /// Sets the active index buffer. /// /// Subsequent calls to [`draw_indexed`](RenderPass::draw_indexed) on this [`RenderPass`] will /// use `buffer` as the source index buffer. pub fn set_index_buffer(&mut self, buffer_slice: BufferSlice<'a>) { RenderPassInner::set_index_buffer( &mut self.id, &buffer_slice.buffer.id, buffer_slice.offset, buffer_slice.size, ) } /// Assign a vertex buffer to a slot. /// /// Subsequent calls to [`draw`] and [`draw_indexed`] on this /// [`RenderPass`] will use `buffer` as one of the source vertex buffers. /// /// The `slot` refers to the index of the matching descriptor in /// [`RenderPipelineDescriptor::vertex_buffers`]. /// /// [`draw`]: #method.draw /// [`draw_indexed`]: #method.draw_indexed /// [`RenderPass`]: struct.RenderPass.html /// [`RenderPipelineDescriptor::vertex_buffers`]: struct.RenderPipelineDescriptor.html#structfield.vertex_buffers pub fn set_vertex_buffer(&mut self, slot: u32, buffer_slice: BufferSlice<'a>) { RenderPassInner::set_vertex_buffer( &mut self.id, slot, &buffer_slice.buffer.id, buffer_slice.offset, buffer_slice.size, ) } /// Sets the scissor region. /// /// Subsequent draw calls will discard any fragments that fall outside this region. pub fn set_scissor_rect(&mut self, x: u32, y: u32, width: u32, height: u32) { self.id.set_scissor_rect(x, y, width, height); } /// Sets the viewport region. /// /// Subsequent draw calls will draw any fragments in this region. pub fn set_viewport(&mut self, x: f32, y: f32, w: f32, h: f32, min_depth: f32, max_depth: f32) { self.id.set_viewport(x, y, w, h, min_depth, max_depth); } /// Sets the stencil reference. /// /// Subsequent stencil tests will test against this value. pub fn set_stencil_reference(&mut self, reference: u32) { self.id.set_stencil_reference(reference); } /// Draws primitives from the active vertex buffer(s). /// /// The active vertex buffers can be set with [`RenderPass::set_vertex_buffer`]. pub fn draw(&mut self, vertices: Range, instances: Range) { RenderPassInner::draw(&mut self.id, vertices, instances) } /// Draws indexed primitives using the active index buffer and the active vertex buffers. /// /// The active index buffer can be set with [`RenderPass::set_index_buffer`], while the active /// vertex buffers can be set with [`RenderPass::set_vertex_buffer`]. pub fn draw_indexed(&mut self, indices: Range, base_vertex: i32, instances: Range) { RenderPassInner::draw_indexed(&mut self.id, indices, base_vertex, instances); } /// Draws primitives from the active vertex buffer(s) based on the contents of the `indirect_buffer`. /// /// The active vertex buffers can be set with [`RenderPass::set_vertex_buffer`]. /// /// The structure expected in `indirect_buffer` is the following: /// /// ```rust /// #[repr(C)] /// struct DrawIndirect { /// vertex_count: u32, // The number of vertices to draw. /// instance_count: u32, // The number of instances to draw. /// base_vertex: u32, // The Index of the first vertex to draw. /// base_instance: u32, // The instance ID of the first instance to draw. /// } /// ``` pub fn draw_indirect(&mut self, indirect_buffer: &'a Buffer, indirect_offset: BufferAddress) { self.id.draw_indirect(&indirect_buffer.id, indirect_offset); } /// Draws indexed primitives using the active index buffer and the active vertex buffers, /// based on the contents of the `indirect_buffer`. /// /// The active index buffer can be set with [`RenderPass::set_index_buffer`], while the active /// vertex buffers can be set with [`RenderPass::set_vertex_buffer`]. /// /// The structure expected in `indirect_buffer` is the following: /// /// ```rust /// #[repr(C)] /// struct DrawIndexedIndirect { /// vertex_count: u32, // The number of vertices to draw. /// instance_count: u32, // The number of instances to draw. /// base_index: u32, // The base index within the index buffer. /// vertex_offset: i32, // The value added to the vertex index before indexing into the vertex buffer. /// base_instance: u32, // The instance ID of the first instance to draw. /// } /// ``` pub fn draw_indexed_indirect( &mut self, indirect_buffer: &'a Buffer, indirect_offset: BufferAddress, ) { self.id .draw_indexed_indirect(&indirect_buffer.id, indirect_offset); } } impl<'a> Drop for RenderPass<'a> { fn drop(&mut self) { if !thread::panicking() { self.parent .context .encoder_end_render_pass(&self.parent.id, &mut self.id); } } } impl<'a> ComputePass<'a> { /// Sets the active bind group for a given bind group index. pub fn set_bind_group( &mut self, index: u32, bind_group: &'a BindGroup, offsets: &[DynamicOffset], ) { ComputePassInner::set_bind_group(&mut self.id, index, &bind_group.id, offsets); } /// Sets the active compute pipeline. pub fn set_pipeline(&mut self, pipeline: &'a ComputePipeline) { ComputePassInner::set_pipeline(&mut self.id, &pipeline.id); } /// Dispatches compute work operations. /// /// `x`, `y` and `z` denote the number of work groups to dispatch in each dimension. pub fn dispatch(&mut self, x: u32, y: u32, z: u32) { ComputePassInner::dispatch(&mut self.id, x, y, z); } /// Dispatches compute work operations, based on the contents of the `indirect_buffer`. pub fn dispatch_indirect( &mut self, indirect_buffer: &'a Buffer, indirect_offset: BufferAddress, ) { ComputePassInner::dispatch_indirect(&mut self.id, &indirect_buffer.id, indirect_offset); } } impl<'a> Drop for ComputePass<'a> { fn drop(&mut self) { if !thread::panicking() { self.parent .context .encoder_end_compute_pass(&self.parent.id, &mut self.id); } } } impl Queue { /// Schedule a data write into `buffer` starting at `offset`. pub fn write_buffer(&self, buffer: &Buffer, offset: BufferAddress, data: &[u8]) { Context::queue_write_buffer(&*self.context, &self.id, &buffer.id, offset, data) } /// Schedule a data write into `texture`. pub fn write_texture( &self, texture: TextureCopyView, data: &[u8], data_layout: wgt::TextureDataLayout, size: wgt::Extent3d, ) { Context::queue_write_texture(&*self.context, &self.id, texture, data, data_layout, size) } /// Submits a series of finished command buffers for execution. pub fn submit>(&self, command_buffers: I) { Context::queue_submit( &*self.context, &self.id, command_buffers .into_iter() .map(|mut comb| comb.id.take().unwrap()), ); } } impl Drop for SwapChainTexture { fn drop(&mut self) { if !thread::panicking() { Context::swap_chain_present(&*self.view.context, &self.view.id, &self.detail); } } } impl SwapChain { /// Returns the next texture to be presented by the swapchain for drawing. /// /// When the [`SwapChainOutput`] returned by this method is dropped, the swapchain will present /// the texture to the associated [`Surface`]. pub fn get_next_frame(&mut self) -> Result { let (view_id, status, detail) = Context::swap_chain_get_next_texture(&*self.context, &self.id); let output = view_id.map(|id| SwapChainTexture { view: TextureView { context: Arc::clone(&self.context), id: id, owned: false, }, detail, }); match status { SwapChainStatus::Good => Ok(SwapChainFrame { output: output.unwrap(), suboptimal: false, }), SwapChainStatus::Suboptimal => Ok(SwapChainFrame { output: output.unwrap(), suboptimal: true, }), SwapChainStatus::Timeout => Err(SwapChainError::Timeout), SwapChainStatus::Outdated => Err(SwapChainError::Outdated), SwapChainStatus::Lost => Err(SwapChainError::Lost), SwapChainStatus::OutOfMemory => Err(SwapChainError::OutOfMemory), } } }