use alloc::{borrow::Cow, boxed::Box, sync::Arc, vec::Vec}; use core::ops::Deref; use core::ptr::NonNull; use wgpu_core_remote_types::{ encoders::{RenderBundleDescriptor, RenderBundleEncoderDescriptor}, pipelines::{ComputePipelineDescriptor, RenderPipelineDescriptor}, BufferDescriptor, ExternalTextureDescriptor, PipelineLayoutDescriptor, QuerySetDescriptor, SamplerDescriptor, ShaderModuleDescriptor, TextureDescriptor, TextureViewDescriptor, }; use wgpu_core::{ binding_model::{self}, command, device::{DeviceLostClosure, WaitIdleError}, error::EmptyErrorScopeStack, pipeline::{ self, ProgrammableStageDescriptor, RenderPipelineVertexProcessor, ResolvedGeneralRenderPipelineDescriptor, }, resource::{self, BufferAccessError, BufferMapOperation, CreateBufferError}, Label, LabelHelpers, SubmissionIndex, }; use crate::{ global::Global, hub::Hub, id::{self, DeviceId, QueueId}, registry::Registry, }; use wgt::{error::WebGpuError, BufferAddress}; pub use wgpu_core_remote_types::binding_model::*; impl Global { pub fn device_features(&self, device_id: DeviceId) -> wgt::Features { let hub = self.hub.borrow(); let device = hub.devices.get(device_id); *device.features() } pub fn device_limits(&self, device_id: DeviceId) -> wgt::Limits { let hub = self.hub.borrow(); let device = hub.devices.get(device_id); device.limits().clone() } pub fn device_adapter_info(&self, device_id: DeviceId) -> wgt::AdapterInfo { let hub = self.hub.borrow(); let device = hub.devices.get(device_id); device.adapter_info() } pub fn device_downlevel_properties(&self, device_id: DeviceId) -> wgt::DownlevelCapabilities { let hub = self.hub.borrow(); let device = hub.devices.get(device_id); device.downlevel().clone() } pub fn device_create_buffer( &self, device_id: DeviceId, desc: &BufferDescriptor, id_in: id::BufferId, ) { let mut hub = self.hub.borrow_mut(); let Hub { buffers, devices, .. } = &mut *hub; let device = devices.get(device_id); let buffer = device.create_buffer(desc); buffers.assign(id_in, buffer); } /// Assign `id_in` an error with the given `label`. /// /// Ensure that future attempts to use `id_in` as a buffer ID will propagate /// the error, following the WebGPU ["contagious invalidity"] style. /// /// Firefox uses this function to comply strictly with the WebGPU spec, /// which requires [`GPUBufferDescriptor`] validation to be generated on the /// Device timeline and leave the newly created [`GPUBuffer`] invalid. /// /// Ideally, we would simply let [`Device::create_buffer`] take care of all /// of this, but some errors must be detected before we can even construct a /// [`wgpu_types::BufferDescriptor`] to give it. For example, the WebGPU API /// allows a `GPUBufferDescriptor`'s [`usage`] property to be any WebIDL /// `unsigned long` value, but we can't construct a /// [`wgpu_types::BufferUsages`] value from values with unassigned bits /// set. This means we must validate `usage` before we can call /// `Device::create_buffer`. /// /// When that validation fails, we must arrange for the buffer id to be /// considered invalid. This method provides the means to do so. /// /// ["contagious invalidity"]: https://www.w3.org/TR/webgpu/#invalidity /// [`GPUBufferDescriptor`]: https://www.w3.org/TR/webgpu/#dictdef-gpubufferdescriptor /// [`GPUBuffer`]: https://www.w3.org/TR/webgpu/#gpubuffer /// [`wgpu_types::BufferDescriptor`]: wgt::BufferDescriptor /// [`Device::create_buffer`]: wgpu_core::device::Device::create_buffer /// [`usage`]: https://www.w3.org/TR/webgpu/#dom-gputexturedescriptor-usage /// [`wgpu_types::BufferUsages`]: wgt::BufferUsages pub fn create_buffer_error( &self, device_id: DeviceId, id_in: id::BufferId, desc: &BufferDescriptor, ) { let mut hub = self.hub.borrow_mut(); let Hub { buffers, devices, .. } = &mut *hub; let device = devices.get(device_id); buffers.assign(id_in, resource::Buffer::invalid(device, desc)); } /// Assign `id_in` an error with the given `label`. /// /// See [`Self::create_buffer_error`] for more context and explanation. pub fn create_render_bundle_error( &self, device_id: DeviceId, id_in: id::RenderBundleId, desc: &RenderBundleDescriptor, ) { let mut hub = self.hub.borrow_mut(); let Hub { render_bundles, devices, .. } = &mut *hub; let device = devices.get(device_id); render_bundles.assign(id_in, command::RenderBundle::invalid(device, desc)); } /// Assign `id_in` an error with the given `label`. /// /// See [`Self::create_buffer_error`] for more context and explanation. pub fn create_texture_error( &self, device_id: DeviceId, id_in: id::TextureId, desc: &TextureDescriptor, ) -> id::TextureId { let mut hub = self.hub.borrow_mut(); let Hub { textures, devices, .. } = &mut *hub; let device = devices.get(device_id); let texture = device.create_texture_error(desc); textures.assign(id_in, texture) } /// Assign `id_in` an error with the given `label`. /// /// See [`Self::create_buffer_error`] for more context and explanation. pub fn create_external_texture_error( &self, device_id: DeviceId, id_in: id::ExternalTextureId, desc: &ExternalTextureDescriptor, ) { let mut hub = self.hub.borrow_mut(); let Hub { external_textures, devices, .. } = &mut *hub; let device = devices.get(device_id); external_textures.assign(id_in, resource::ExternalTexture::invalid(device, desc)); } /// Assign `id_in` an error with the given `label`. /// /// In JavaScript environments, it is possible to call `GPUDevice.createBindGroupLayout` with /// entries that are invalid. Because our Rust's types for bind group layouts prevent even /// calling [`Self::device_create_bind_group`], we let standards-compliant environments /// register an invalid bind group layout so this crate's API can still be consistently used. /// /// See [`Self::create_buffer_error`] for additional context and explanation. pub fn create_bind_group_layout_error( &self, device_id: DeviceId, id_in: id::BindGroupLayoutId, label: Option>, ) { let mut hub = self.hub.borrow_mut(); let Hub { bind_group_layouts, devices, .. } = &mut *hub; let device = devices.get(device_id); bind_group_layouts.assign( id_in, binding_model::BindGroupLayout::invalid(&device, label.to_string()), ); } pub fn buffer_destroy(&self, buffer_id: id::BufferId) { let hub = self.hub.borrow(); let buffer = hub.buffers.get(buffer_id); buffer.destroy(); } pub fn buffer_remove(&self, buffer_id: id::BufferId) -> Arc { let mut hub = self.hub.borrow_mut(); hub.buffers.remove(buffer_id) } pub fn device_create_texture( &self, device_id: DeviceId, desc: &TextureDescriptor, id_in: id::TextureId, ) { let mut hub = self.hub.borrow_mut(); let Hub { textures, devices, .. } = &mut *hub; let device = devices.get(device_id); let texture = device.create_texture(desc); textures.assign(id_in, texture); } pub fn device_validate_texture_descriptor( &self, device_id: DeviceId, desc: &TextureDescriptor, ) -> Option { let hub = self.hub.borrow(); hub.devices .get(device_id) .validate_texture_descriptor(desc) .err() } /// # Safety /// /// - `hal_texture` must be created from `device_id` corresponding raw handle. /// - `hal_texture` must be created respecting `desc` /// - `hal_texture` must be initialized /// - The `initial_state` must match the actual driver-side state of /// the wrapped resource at the moment of wrap. pub unsafe fn create_texture_from_hal( &self, hal_texture: Box, device_id: DeviceId, desc: &TextureDescriptor, initial_state: wgt::TextureUses, id_in: id::TextureId, cleared: bool, ) -> (id::TextureId, Option) { let mut hub = self.hub.borrow_mut(); let Hub { textures, devices, .. } = &mut *hub; let device = devices.get(device_id); let (texture, error) = unsafe { device.create_texture_from_hal(hal_texture, desc, initial_state, cleared) }; let id = textures.assign(id_in, texture); (id, error) } /// # Safety /// /// - `hal_buffer` must be created from `device_id` corresponding raw handle. /// - `hal_buffer` must be created respecting `desc` /// - `hal_buffer` must be initialized /// - `hal_buffer` must not have zero size. pub unsafe fn create_buffer_from_hal( &self, hal_buffer: A::Buffer, device_id: DeviceId, desc: &BufferDescriptor, id_in: id::BufferId, ) -> (id::BufferId, Option) { let mut hub = self.hub.borrow_mut(); let Hub { buffers, devices, .. } = &mut *hub; let device = devices.get(device_id); let (buffer, err) = unsafe { device.create_buffer_from_hal(Box::new(hal_buffer), desc) }; let id = buffers.assign(id_in, buffer); (id, err) } pub fn texture_destroy(&self, texture_id: id::TextureId) { let hub = self.hub.borrow(); let texture = hub.textures.get(texture_id); texture.destroy(); } pub fn texture_remove(&self, texture_id: id::TextureId) -> Arc { let mut hub = self.hub.borrow_mut(); hub.textures.remove(texture_id) } /// # Safety /// /// The entire contents of the texture must already be initialized. pub unsafe fn texture_mark_externally_initialized(&self, texture_id: id::TextureId) { let hub = &self.hub.borrow(); let texture = hub.textures.get(texture_id); unsafe { texture.mark_externally_initialized() }; } pub fn texture_create_view( &self, texture_id: id::TextureId, desc: &TextureViewDescriptor, id_in: id::TextureViewId, ) { let mut hub = self.hub.borrow_mut(); let Hub { textures, texture_views, .. } = &mut *hub; let texture = textures.get(texture_id); let desc = resource::TextureViewDescriptor { label: desc.label.as_ref().map(|s| Cow::Borrowed(s.deref())), format: desc.format, dimension: desc.dimension, usage: desc.usage, range: desc.range, swizzle: desc.swizzle, }; let view = texture.create_view(&desc); texture_views.assign(id_in, view); } pub fn texture_view_remove( &self, texture_view_id: id::TextureViewId, ) -> Arc { let mut hub = self.hub.borrow_mut(); hub.texture_views.remove(texture_view_id) } pub fn device_create_external_texture( &self, device_id: DeviceId, desc: &ExternalTextureDescriptor, planes: &[id::TextureViewId], id_in: id::ExternalTextureId, ) { let mut hub = self.hub.borrow_mut(); let Hub { external_textures, devices, texture_views, .. } = &mut *hub; let device = devices.get(device_id); let planes = planes .iter() .map(|plane_id| texture_views.get(*plane_id)) .collect::>(); let external_texture = device.create_external_texture(desc, &planes); external_textures.assign(id_in, external_texture); } pub fn external_texture_destroy(&self, external_texture_id: id::ExternalTextureId) { let hub = self.hub.borrow(); let external_texture = hub.external_textures.get(external_texture_id); external_texture.destroy(); } pub fn external_texture_remove( &self, external_texture_id: id::ExternalTextureId, ) -> Arc { let mut hub = self.hub.borrow_mut(); hub.external_textures.remove(external_texture_id) } pub fn device_create_sampler( &self, device_id: DeviceId, desc: &SamplerDescriptor, id_in: id::SamplerId, ) { let mut hub = self.hub.borrow_mut(); let Hub { samplers, devices, .. } = &mut *hub; let device = devices.get(device_id); let desc = resource::SamplerDescriptor { label: desc.label.as_ref().map(|l| Cow::Borrowed(l.as_ref())), address_modes: desc.address_modes, mag_filter: desc.mag_filter, min_filter: desc.min_filter, mipmap_filter: desc.mipmap_filter, lod_min_clamp: desc.lod_min_clamp, lod_max_clamp: desc.lod_max_clamp, compare: desc.compare, anisotropy_clamp: desc.anisotropy_clamp, border_color: None, }; let sampler = device.create_sampler(&desc); samplers.assign(id_in, sampler); } pub fn sampler_remove(&self, sampler_id: id::SamplerId) -> Arc { let mut hub = self.hub.borrow_mut(); hub.samplers.remove(sampler_id) } pub fn device_create_bind_group_layout( &self, device_id: DeviceId, desc: &BindGroupLayoutDescriptor, id_in: id::BindGroupLayoutId, ) { let mut hub = self.hub.borrow_mut(); let Hub { bind_group_layouts, devices, .. } = &mut *hub; let device = devices.get(device_id); let desc = binding_model::BindGroupLayoutDescriptor { label: desc.label.as_ref().map(|l| Cow::Borrowed(l.as_ref())), entries: Cow::Borrowed(&desc.entries), }; let bgl = device.create_bind_group_layout(&desc); bind_group_layouts.assign(id_in, bgl); } pub fn bind_group_layout_remove( &self, bind_group_layout_id: id::BindGroupLayoutId, ) -> Arc { let mut hub = self.hub.borrow_mut(); hub.bind_group_layouts.remove(bind_group_layout_id) } pub fn device_create_pipeline_layout( &self, device_id: DeviceId, desc: &PipelineLayoutDescriptor, id_in: id::PipelineLayoutId, ) { let mut hub = self.hub.borrow_mut(); let Hub { pipeline_layouts, devices, bind_group_layouts, .. } = &mut *hub; let device = devices.get(device_id); let bind_group_layouts = desc .bind_group_layouts .iter() .map(|bgl_id| bgl_id.map(|bgl_id| bind_group_layouts.get(bgl_id))) .collect::>(); let desc = binding_model::PipelineLayoutDescriptor { label: desc.label.as_ref().map(|l| Cow::Borrowed(l.as_ref())), bind_group_layouts: Cow::Owned(bind_group_layouts), immediate_size: desc.immediate_size, }; let layout = device.create_pipeline_layout(&desc); pipeline_layouts.assign(id_in, layout); } pub fn pipeline_layout_remove( &self, pipeline_layout_id: id::PipelineLayoutId, ) -> Arc { let mut hub = self.hub.borrow_mut(); hub.pipeline_layouts.remove(pipeline_layout_id) } pub fn device_create_bind_group( &self, device_id: DeviceId, desc: &BindGroupDescriptor, id_in: id::BindGroupId, ) { let mut hub = self.hub.borrow_mut(); let Hub { bind_groups, devices, bind_group_layouts, buffers, samplers, texture_views, external_textures, .. } = &mut *hub; let device = devices.get(device_id); let layout = bind_group_layouts.get(desc.layout); fn resolve_entry<'a>( e: &'a BindGroupEntry, buffers: &mut Registry>, samplers: &mut Registry>, texture_views: &mut Registry>, external_textures: &mut Registry>, ) -> binding_model::BindGroupEntry<'a> { let resolve_buffer = |bb: &BufferBinding| { let buffer = buffers.get(bb.buffer); binding_model::BufferBinding { buffer, offset: bb.offset, size: bb.size.to_std(), } }; let resolve_sampler = |id: &id::SamplerId| samplers.get(*id); let resolve_view = |id: &id::TextureViewId| texture_views.get(*id); let resolve_external_texture = |id: &id::ExternalTextureId| external_textures.get(*id); let resource = match e.resource { BindingResource::Buffer(ref buffer) => { binding_model::BindingResource::Buffer(resolve_buffer(buffer)) } BindingResource::Sampler(ref sampler) => { binding_model::BindingResource::Sampler(resolve_sampler(sampler)) } BindingResource::TextureView(ref view) => { binding_model::BindingResource::TextureView(resolve_view(view)) } BindingResource::ExternalTexture(ref et) => { binding_model::BindingResource::ExternalTexture(resolve_external_texture(et)) } }; binding_model::BindGroupEntry { binding: e.binding, resource, } } let entries = desc .entries .iter() .map(|e| resolve_entry(e, buffers, samplers, texture_views, external_textures)) .collect::>(); let entries = Cow::Owned(entries); let desc = binding_model::BindGroupDescriptor { label: desc.label.clone(), layout, entries, }; let bind_group = device.create_bind_group(&desc); bind_groups.assign(id_in, bind_group); } pub fn bind_group_remove( &self, bind_group_id: id::BindGroupId, ) -> Arc { let mut hub = self.hub.borrow_mut(); hub.bind_groups.remove(bind_group_id) } /// Create a shader module with the given `source`. /// ///
// NOTE: Keep this in sync with `naga::front::wgsl::parse_str`! // NOTE: Keep this in sync with `wgpu::Device::create_shader_module`! /// /// This function may consume a lot of stack space. Compiler-enforced limits for parsing /// recursion exist; if shader compilation runs into them, it will return an error gracefully. /// However, on some build profiles and platforms, the default stack size for a thread may be /// exceeded before this limit is reached during parsing. Callers should ensure that there is /// enough stack space for this, particularly if calls to this method are exposed to user /// input. /// ///
pub fn device_create_shader_module( &self, device_id: DeviceId, desc: &ShaderModuleDescriptor, id_in: id::ShaderModuleId, ) { let mut hub = self.hub.borrow_mut(); let Hub { shader_modules, devices, .. } = &mut *hub; let device = devices.get(device_id); let code = pipeline::ShaderModuleSource::Wgsl(Cow::Borrowed(&desc.code)); let desc = pipeline::ShaderModuleDescriptor { label: desc.label.as_ref().map(|l| Cow::Borrowed(l.as_ref())), runtime_checks: wgt::ShaderRuntimeChecks::checked(), }; let shader = device.create_shader_module(&desc, code); shader_modules.assign(id_in, shader); } pub fn shader_module_compilation_info( &self, shader_module_id: id::ShaderModuleId, ) -> wgt::CompilationInfo { let hub = self.hub.borrow(); let shader_module = hub.shader_modules.get(shader_module_id); shader_module.compilation_info().clone() } pub fn shader_module_remove( &self, shader_module_id: id::ShaderModuleId, ) -> Arc { let mut hub = self.hub.borrow_mut(); hub.shader_modules.remove(shader_module_id) } pub fn device_create_command_encoder( &self, device_id: DeviceId, desc: &wgt::CommandEncoderDescriptor