use alloc::{borrow::Cow, boxed::Box, string::String, sync::Arc, vec::Vec}; use core::{ptr::NonNull, sync::atomic::Ordering}; #[cfg(feature = "trace")] use crate::device::trace; use crate::{ api_log, binding_model::{ self, BindGroupEntry, BindingResource, BufferBinding, ResolvedBindGroupDescriptor, ResolvedBindGroupEntry, ResolvedBindingResource, ResolvedBufferBinding, }, command::{self, CommandEncoder}, conv, device::{bgl, life::WaitIdleError, DeviceError, DeviceLostClosure}, global::Global, id::{self, AdapterId, DeviceId, QueueId, SurfaceId}, instance::{self, Adapter, Surface}, pipeline::{ self, RenderPipelineVertexProcessor, ResolvedComputePipelineDescriptor, ResolvedFragmentState, ResolvedGeneralRenderPipelineDescriptor, ResolvedMeshState, ResolvedProgrammableStageDescriptor, ResolvedTaskState, ResolvedVertexState, }, present, resource::{ self, BufferAccessError, BufferAccessResult, BufferMapOperation, CreateBufferError, Fallible, }, storage::Storage, Label, LabelHelpers, }; use wgt::{BufferAddress, TextureFormat}; use super::UserClosures; impl Global { pub fn adapter_is_surface_supported( &self, adapter_id: AdapterId, surface_id: SurfaceId, ) -> bool { let surface = self.surfaces.get(surface_id); let adapter = self.hub.adapters.get(adapter_id); adapter.is_surface_supported(&surface) } pub fn surface_get_capabilities( &self, surface_id: SurfaceId, adapter_id: AdapterId, ) -> Result { profiling::scope!("Surface::get_capabilities"); self.fetch_adapter_and_surface::<_, _>(surface_id, adapter_id, |adapter, surface| { let mut hal_caps = surface.get_capabilities(adapter)?; hal_caps.formats.sort_by_key(|f| !f.is_srgb()); let usages = conv::map_texture_usage_from_hal(hal_caps.usage); Ok(wgt::SurfaceCapabilities { formats: hal_caps.formats, present_modes: hal_caps.present_modes, alpha_modes: hal_caps.composite_alpha_modes, usages, }) }) } fn fetch_adapter_and_surface B, B>( &self, surface_id: SurfaceId, adapter_id: AdapterId, get_supported_callback: F, ) -> B { let surface = self.surfaces.get(surface_id); let adapter = self.hub.adapters.get(adapter_id); get_supported_callback(&adapter, &surface) } pub fn device_features(&self, device_id: DeviceId) -> wgt::Features { let device = self.hub.devices.get(device_id); device.features } pub fn device_limits(&self, device_id: DeviceId) -> wgt::Limits { let device = self.hub.devices.get(device_id); device.limits.clone() } pub fn device_downlevel_properties(&self, device_id: DeviceId) -> wgt::DownlevelCapabilities { let device = self.hub.devices.get(device_id); device.downlevel.clone() } pub fn device_create_buffer( &self, device_id: DeviceId, desc: &resource::BufferDescriptor, id_in: Option, ) -> (id::BufferId, Option) { profiling::scope!("Device::create_buffer"); let hub = &self.hub; let fid = hub.buffers.prepare(id_in); let error = 'error: { let device = self.hub.devices.get(device_id); #[cfg(feature = "trace")] if let Some(ref mut trace) = *device.trace.lock() { let mut desc = desc.clone(); let mapped_at_creation = core::mem::replace(&mut desc.mapped_at_creation, false); if mapped_at_creation && !desc.usage.contains(wgt::BufferUsages::MAP_WRITE) { desc.usage |= wgt::BufferUsages::COPY_DST; } trace.add(trace::Action::CreateBuffer(fid.id(), desc)); } let buffer = match device.create_buffer(desc) { Ok(buffer) => buffer, Err(e) => { break 'error e; } }; let id = fid.assign(Fallible::Valid(buffer)); api_log!( "Device::create_buffer({:?}{}) -> {id:?}", desc.label.as_deref().unwrap_or(""), if desc.mapped_at_creation { ", mapped_at_creation" } else { "" } ); return (id, None); }; let id = fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string()))); (id, Some(error)) } /// 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`]: Global::device_create_buffer /// [`usage`]: https://www.w3.org/TR/webgpu/#dom-gputexturedescriptor-usage /// [`wgpu_types::BufferUsages`]: wgt::BufferUsages pub fn create_buffer_error( &self, id_in: Option, desc: &resource::BufferDescriptor, ) { let fid = self.hub.buffers.prepare(id_in); fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string()))); } /// 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, id_in: Option, desc: &command::RenderBundleDescriptor, ) { let fid = self.hub.render_bundles.prepare(id_in); fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string()))); } /// 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, id_in: Option, desc: &resource::TextureDescriptor, ) { let fid = self.hub.textures.prepare(id_in); fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string()))); } /// 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, id_in: Option, desc: &resource::ExternalTextureDescriptor, ) { let fid = self.hub.external_textures.prepare(id_in); fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string()))); } #[cfg(feature = "replay")] pub fn device_set_buffer_data( &self, buffer_id: id::BufferId, offset: BufferAddress, data: &[u8], ) -> BufferAccessResult { use crate::resource::RawResourceAccess; let hub = &self.hub; let buffer = hub.buffers.get(buffer_id).get()?; let device = &buffer.device; device.check_is_valid()?; buffer.check_usage(wgt::BufferUsages::MAP_WRITE)?; let last_submission = device.get_queue().and_then(|queue| { queue .lock_life() .get_buffer_latest_submission_index(&buffer) }); if let Some(last_submission) = last_submission { device.wait_for_submit(last_submission)?; } let snatch_guard = device.snatchable_lock.read(); let raw_buf = buffer.try_raw(&snatch_guard)?; let mapping = unsafe { device .raw() .map_buffer(raw_buf, offset..offset + data.len() as u64) } .map_err(|e| device.handle_hal_error(e))?; unsafe { core::ptr::copy_nonoverlapping(data.as_ptr(), mapping.ptr.as_ptr(), data.len()) }; if !mapping.is_coherent { #[allow(clippy::single_range_in_vec_init)] unsafe { device .raw() .flush_mapped_ranges(raw_buf, &[offset..offset + data.len() as u64]) }; } unsafe { device.raw().unmap_buffer(raw_buf) }; Ok(()) } pub fn buffer_destroy(&self, buffer_id: id::BufferId) { profiling::scope!("Buffer::destroy"); api_log!("Buffer::destroy {buffer_id:?}"); let hub = &self.hub; let Ok(buffer) = hub.buffers.get(buffer_id).get() else { // If the buffer is already invalid, there's nothing to do. return; }; #[cfg(feature = "trace")] if let Some(trace) = buffer.device.trace.lock().as_mut() { trace.add(trace::Action::FreeBuffer(buffer_id)); } let _ = buffer.unmap( #[cfg(feature = "trace")] buffer_id, ); buffer.destroy(); } pub fn buffer_drop(&self, buffer_id: id::BufferId) { profiling::scope!("Buffer::drop"); api_log!("Buffer::drop {buffer_id:?}"); let hub = &self.hub; let buffer = match hub.buffers.remove(buffer_id).get() { Ok(buffer) => buffer, Err(_) => { return; } }; #[cfg(feature = "trace")] if let Some(t) = buffer.device.trace.lock().as_mut() { t.add(trace::Action::DestroyBuffer(buffer_id)); } let _ = buffer.unmap( #[cfg(feature = "trace")] buffer_id, ); } pub fn device_create_texture( &self, device_id: DeviceId, desc: &resource::TextureDescriptor, id_in: Option, ) -> (id::TextureId, Option) { profiling::scope!("Device::create_texture"); let hub = &self.hub; let fid = hub.textures.prepare(id_in); let error = 'error: { let device = self.hub.devices.get(device_id); #[cfg(feature = "trace")] if let Some(ref mut trace) = *device.trace.lock() { trace.add(trace::Action::CreateTexture(fid.id(), desc.clone())); } let texture = match device.create_texture(desc) { Ok(texture) => texture, Err(error) => break 'error error, }; let id = fid.assign(Fallible::Valid(texture)); api_log!("Device::create_texture({desc:?}) -> {id:?}"); return (id, None); }; let id = fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string()))); (id, Some(error)) } /// # Safety /// /// - `hal_texture` must be created from `device_id` corresponding raw handle. /// - `hal_texture` must be created respecting `desc` /// - `hal_texture` must be initialized pub unsafe fn create_texture_from_hal( &self, hal_texture: Box, device_id: DeviceId, desc: &resource::TextureDescriptor, id_in: Option, ) -> (id::TextureId, Option) { profiling::scope!("Device::create_texture_from_hal"); let hub = &self.hub; let fid = hub.textures.prepare(id_in); let error = 'error: { let device = self.hub.devices.get(device_id); // NB: Any change done through the raw texture handle will not be // recorded in the replay #[cfg(feature = "trace")] if let Some(ref mut trace) = *device.trace.lock() { trace.add(trace::Action::CreateTexture(fid.id(), desc.clone())); } let texture = match device.create_texture_from_hal(hal_texture, desc) { Ok(texture) => texture, Err(error) => break 'error error, }; let id = fid.assign(Fallible::Valid(texture)); api_log!("Device::create_texture({desc:?}) -> {id:?}"); return (id, None); }; let id = fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string()))); (id, Some(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: &resource::BufferDescriptor, id_in: Option, ) -> (id::BufferId, Option) { profiling::scope!("Device::create_buffer"); let hub = &self.hub; let fid = hub.buffers.prepare(id_in); let device = self.hub.devices.get(device_id); // NB: Any change done through the raw buffer handle will not be // recorded in the replay #[cfg(feature = "trace")] if let Some(trace) = device.trace.lock().as_mut() { trace.add(trace::Action::CreateBuffer(fid.id(), desc.clone())); } let (buffer, err) = unsafe { device.create_buffer_from_hal(Box::new(hal_buffer), desc) }; let id = fid.assign(buffer); api_log!("Device::create_buffer -> {id:?}"); (id, err) } pub fn texture_destroy(&self, texture_id: id::TextureId) { profiling::scope!("Texture::destroy"); api_log!("Texture::destroy {texture_id:?}"); let hub = &self.hub; let Ok(texture) = hub.textures.get(texture_id).get() else { // If the texture is already invalid, there's nothing to do. return; }; #[cfg(feature = "trace")] if let Some(trace) = texture.device.trace.lock().as_mut() { trace.add(trace::Action::FreeTexture(texture_id)); } texture.destroy(); } pub fn texture_drop(&self, texture_id: id::TextureId) { profiling::scope!("Texture::drop"); api_log!("Texture::drop {texture_id:?}"); let hub = &self.hub; let _texture = hub.textures.remove(texture_id); #[cfg(feature = "trace")] if let Ok(texture) = _texture.get() { if let Some(t) = texture.device.trace.lock().as_mut() { t.add(trace::Action::DestroyTexture(texture_id)); } } } pub fn texture_create_view( &self, texture_id: id::TextureId, desc: &resource::TextureViewDescriptor, id_in: Option, ) -> (id::TextureViewId, Option) { profiling::scope!("Texture::create_view"); let hub = &self.hub; let fid = hub.texture_views.prepare(id_in); let error = 'error: { let texture = match hub.textures.get(texture_id).get() { Ok(texture) => texture, Err(e) => break 'error e.into(), }; let device = &texture.device; #[cfg(feature = "trace")] if let Some(ref mut trace) = *device.trace.lock() { trace.add(trace::Action::CreateTextureView { id: fid.id(), parent_id: texture_id, desc: desc.clone(), }); } let view = match device.create_texture_view(&texture, desc) { Ok(view) => view, Err(e) => break 'error e, }; let id = fid.assign(Fallible::Valid(view)); api_log!("Texture::create_view({texture_id:?}) -> {id:?}"); return (id, None); }; let id = fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string()))); (id, Some(error)) } pub fn texture_view_drop( &self, texture_view_id: id::TextureViewId, ) -> Result<(), resource::TextureViewDestroyError> { profiling::scope!("TextureView::drop"); api_log!("TextureView::drop {texture_view_id:?}"); let hub = &self.hub; let _view = hub.texture_views.remove(texture_view_id); #[cfg(feature = "trace")] if let Ok(view) = _view.get() { if let Some(t) = view.device.trace.lock().as_mut() { t.add(trace::Action::DestroyTextureView(texture_view_id)); } } Ok(()) } pub fn device_create_external_texture( &self, device_id: DeviceId, desc: &resource::ExternalTextureDescriptor, planes: &[id::TextureViewId], id_in: Option, ) -> ( id::ExternalTextureId, Option, ) { profiling::scope!("Device::create_external_texture"); let hub = &self.hub; let fid = hub.external_textures.prepare(id_in); let error = 'error: { let device = self.hub.devices.get(device_id); #[cfg(feature = "trace")] if let Some(ref mut trace) = *device.trace.lock() { let planes = Box::from(planes); trace.add(trace::Action::CreateExternalTexture { id: fid.id(), desc: desc.clone(), planes, }); } let planes = planes .iter() .map(|plane_id| self.hub.texture_views.get(*plane_id).get()) .collect::, _>>(); let planes = match planes { Ok(planes) => planes, Err(error) => break 'error error.into(), }; let external_texture = match device.create_external_texture(desc, &planes) { Ok(external_texture) => external_texture, Err(error) => break 'error error, }; let id = fid.assign(Fallible::Valid(external_texture)); api_log!("Device::create_external_texture({desc:?}) -> {id:?}"); return (id, None); }; let id = fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string()))); (id, Some(error)) } pub fn external_texture_destroy(&self, external_texture_id: id::ExternalTextureId) { profiling::scope!("ExternalTexture::destroy"); api_log!("ExternalTexture::destroy {external_texture_id:?}"); let hub = &self.hub; let Ok(external_texture) = hub.external_textures.get(external_texture_id).get() else { // If the external texture is already invalid, there's nothing to do. return; }; #[cfg(feature = "trace")] if let Some(trace) = external_texture.device.trace.lock().as_mut() { trace.add(trace::Action::FreeExternalTexture(external_texture_id)); } external_texture.destroy(); } pub fn external_texture_drop(&self, external_texture_id: id::ExternalTextureId) { profiling::scope!("ExternalTexture::drop"); api_log!("ExternalTexture::drop {external_texture_id:?}"); let hub = &self.hub; let _external_texture = hub.external_textures.remove(external_texture_id); #[cfg(feature = "trace")] if let Ok(external_texture) = _external_texture.get() { if let Some(t) = external_texture.device.trace.lock().as_mut() { t.add(trace::Action::DestroyExternalTexture(external_texture_id)); } } } pub fn device_create_sampler( &self, device_id: DeviceId, desc: &resource::SamplerDescriptor, id_in: Option, ) -> (id::SamplerId, Option) { profiling::scope!("Device::create_sampler"); let hub = &self.hub; let fid = hub.samplers.prepare(id_in); let error = 'error: { let device = self.hub.devices.get(device_id); #[cfg(feature = "trace")] if let Some(ref mut trace) = *device.trace.lock() { trace.add(trace::Action::CreateSampler(fid.id(), desc.clone())); } let sampler = match device.create_sampler(desc) { Ok(sampler) => sampler, Err(e) => break 'error e, }; let id = fid.assign(Fallible::Valid(sampler)); api_log!("Device::create_sampler -> {id:?}"); return (id, None); }; let id = fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string()))); (id, Some(error)) } pub fn sampler_drop(&self, sampler_id: id::SamplerId) { profiling::scope!("Sampler::drop"); api_log!("Sampler::drop {sampler_id:?}"); let hub = &self.hub; let _sampler = hub.samplers.remove(sampler_id); #[cfg(feature = "trace")] if let Ok(sampler) = _sampler.get() { if let Some(t) = sampler.device.trace.lock().as_mut() { t.add(trace::Action::DestroySampler(sampler_id)); } } } pub fn device_create_bind_group_layout( &self, device_id: DeviceId, desc: &binding_model::BindGroupLayoutDescriptor, id_in: Option, ) -> ( id::BindGroupLayoutId, Option, ) { profiling::scope!("Device::create_bind_group_layout"); let hub = &self.hub; let fid = hub.bind_group_layouts.prepare(id_in); let error = 'error: { let device = self.hub.devices.get(device_id); #[cfg(feature = "trace")] if let Some(ref mut trace) = *device.trace.lock() { trace.add(trace::Action::CreateBindGroupLayout(fid.id(), desc.clone())); } // this check can't go in the body of `create_bind_group_layout` since the closure might not get called if let Err(e) = device.check_is_valid() { break 'error e.into(); } let entry_map = match bgl::EntryMap::from_entries(&device.limits, &desc.entries) { Ok(map) => map, Err(e) => break 'error e, }; let bgl_result = device.bgl_pool.get_or_init(entry_map, |entry_map| { let bgl = device.create_bind_group_layout(&desc.label, entry_map, bgl::Origin::Pool)?; bgl.exclusive_pipeline .set(binding_model::ExclusivePipeline::None) .unwrap(); Ok(bgl) }); let layout = match bgl_result { Ok(layout) => layout, Err(e) => break 'error e, }; let id = fid.assign(Fallible::Valid(layout.clone())); api_log!("Device::create_bind_group_layout -> {id:?}"); return (id, None); }; let id = fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string()))); (id, Some(error)) } pub fn bind_group_layout_drop(&self, bind_group_layout_id: id::BindGroupLayoutId) { profiling::scope!("BindGroupLayout::drop"); api_log!("BindGroupLayout::drop {bind_group_layout_id:?}"); let hub = &self.hub; let _layout = hub.bind_group_layouts.remove(bind_group_layout_id); #[cfg(feature = "trace")] if let Ok(layout) = _layout.get() { if let Some(t) = layout.device.trace.lock().as_mut() { t.add(trace::Action::DestroyBindGroupLayout(bind_group_layout_id)); } } } pub fn device_create_pipeline_layout( &self, device_id: DeviceId, desc: &binding_model::PipelineLayoutDescriptor, id_in: Option, ) -> ( id::PipelineLayoutId, Option, ) { profiling::scope!("Device::create_pipeline_layout"); let hub = &self.hub; let fid = hub.pipeline_layouts.prepare(id_in); let error = 'error: { let device = self.hub.devices.get(device_id); #[cfg(feature = "trace")] if let Some(ref mut trace) = *device.trace.lock() { trace.add(trace::Action::CreatePipelineLayout(fid.id(), desc.clone())); } if let Err(e) = device.check_is_valid() { break 'error e.into(); } let bind_group_layouts = { let bind_group_layouts_guard = hub.bind_group_layouts.read(); desc.bind_group_layouts .iter() .map(|bgl_id| bind_group_layouts_guard.get(*bgl_id).get()) .collect::, _>>() }; let bind_group_layouts = match bind_group_layouts { Ok(bind_group_layouts) => bind_group_layouts, Err(e) => break 'error e.into(), }; let desc = binding_model::ResolvedPipelineLayoutDescriptor { label: desc.label.clone(), bind_group_layouts: Cow::Owned(bind_group_layouts), push_constant_ranges: desc.push_constant_ranges.clone(), }; let layout = match device.create_pipeline_layout(&desc) { Ok(layout) => layout, Err(e) => break 'error e, }; let id = fid.assign(Fallible::Valid(layout)); api_log!("Device::create_pipeline_layout -> {id:?}"); return (id, None); }; let id = fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string()))); (id, Some(error)) } pub fn pipeline_layout_drop(&self, pipeline_layout_id: id::PipelineLayoutId) { profiling::scope!("PipelineLayout::drop"); api_log!("PipelineLayout::drop {pipeline_layout_id:?}"); let hub = &self.hub; let _layout = hub.pipeline_layouts.remove(pipeline_layout_id); #[cfg(feature = "trace")] if let Ok(layout) = _layout.get() { if let Some(t) = layout.device.trace.lock().as_mut() { t.add(trace::Action::DestroyPipelineLayout(pipeline_layout_id)); } } } pub fn device_create_bind_group( &self, device_id: DeviceId, desc: &binding_model::BindGroupDescriptor, id_in: Option, ) -> (id::BindGroupId, Option) { profiling::scope!("Device::create_bind_group"); let hub = &self.hub; let fid = hub.bind_groups.prepare(id_in); let error = 'error: { let device = self.hub.devices.get(device_id); #[cfg(feature = "trace")] if let Some(ref mut trace) = *device.trace.lock() { trace.add(trace::Action::CreateBindGroup(fid.id(), desc.clone())); } if let Err(e) = device.check_is_valid() { break 'error e.into(); } let layout = match hub.bind_group_layouts.get(desc.layout).get() { Ok(layout) => layout, Err(e) => break 'error e.into(), }; fn resolve_entry<'a>( e: &BindGroupEntry<'a>, buffer_storage: &Storage>, sampler_storage: &Storage>, texture_view_storage: &Storage>, tlas_storage: &Storage>, external_texture_storage: &Storage>, ) -> Result, binding_model::CreateBindGroupError> { let resolve_buffer = |bb: &BufferBinding| { buffer_storage .get(bb.buffer) .get() .map(|buffer| ResolvedBufferBinding { buffer, offset: bb.offset, size: bb.size, }) .map_err(binding_model::CreateBindGroupError::from) }; let resolve_sampler = |id: &id::SamplerId| { sampler_storage .get(*id) .get() .map_err(binding_model::CreateBindGroupError::from) }; let resolve_view = |id: &id::TextureViewId| { texture_view_storage .get(*id) .get() .map_err(binding_model::CreateBindGroupError::from) }; let resolve_tlas = |id: &id::TlasId| { tlas_storage .get(*id) .get() .map_err(binding_model::CreateBindGroupError::from) }; let resolve_external_texture = |id: &id::ExternalTextureId| { external_texture_storage .get(*id) .get() .map_err(binding_model::CreateBindGroupError::from) }; let resource = match e.resource { BindingResource::Buffer(ref buffer) => { ResolvedBindingResource::Buffer(resolve_buffer(buffer)?) } BindingResource::BufferArray(ref buffers) => { let buffers = buffers .iter() .map(resolve_buffer) .collect::, _>>()?; ResolvedBindingResource::BufferArray(Cow::Owned(buffers)) } BindingResource::Sampler(ref sampler) => { ResolvedBindingResource::Sampler(resolve_sampler(sampler)?) } BindingResource::SamplerArray(ref samplers) => { let samplers = samplers .iter() .map(resolve_sampler) .collect::, _>>()?; ResolvedBindingResource::SamplerArray(Cow::Owned(samplers)) } BindingResource::TextureView(ref view) => { ResolvedBindingResource::TextureView(resolve_view(view)?) } BindingResource::TextureViewArray(ref views) => { let views = views .iter() .map(resolve_view) .collect::, _>>()?; ResolvedBindingResource::TextureViewArray(Cow::Owned(views)) } BindingResource::AccelerationStructure(ref tlas) => { ResolvedBindingResource::AccelerationStructure(resolve_tlas(tlas)?) } BindingResource::ExternalTexture(ref et) => { ResolvedBindingResource::ExternalTexture(resolve_external_texture(et)?) } }; Ok(ResolvedBindGroupEntry { binding: e.binding, resource, }) } let entries = { let buffer_guard = hub.buffers.read(); let texture_view_guard = hub.texture_views.read(); let sampler_guard = hub.samplers.read(); let tlas_guard = hub.tlas_s.read(); let external_texture_guard = hub.external_textures.read(); desc.entries .iter() .map(|e| { resolve_entry( e, &buffer_guard, &sampler_guard, &texture_view_guard, &tlas_guard, &external_texture_guard, ) }) .collect::, _>>() }; let entries = match entries { Ok(entries) => Cow::Owned(entries), Err(e) => break 'error e, }; let desc = ResolvedBindGroupDescriptor { label: desc.label.clone(), layout, entries, }; let bind_group = match device.create_bind_group(desc) { Ok(bind_group) => bind_group, Err(e) => break 'error e, }; let id = fid.assign(Fallible::Valid(bind_group)); api_log!("Device::create_bind_group -> {id:?}"); return (id, None); }; let id = fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string()))); (id, Some(error)) } pub fn bind_group_drop(&self, bind_group_id: id::BindGroupId) { profiling::scope!("BindGroup::drop"); api_log!("BindGroup::drop {bind_group_id:?}"); let hub = &self.hub; let _bind_group = hub.bind_groups.remove(bind_group_id); #[cfg(feature = "trace")] if let Ok(_bind_group) = _bind_group.get() { if let Some(t) = _bind_group.device.trace.lock().as_mut() { t.add(trace::Action::DestroyBindGroup(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: &pipeline::ShaderModuleDescriptor, source: pipeline::ShaderModuleSource, id_in: Option, ) -> ( id::ShaderModuleId, Option, ) { profiling::scope!("Device::create_shader_module"); let hub = &self.hub; let fid = hub.shader_modules.prepare(id_in); let error = 'error: { let device = self.hub.devices.get(device_id); #[cfg(feature = "trace")] if let Some(ref mut trace) = *device.trace.lock() { let data = match source { #[cfg(feature = "wgsl")] pipeline::ShaderModuleSource::Wgsl(ref code) => { trace.make_binary("wgsl", code.as_bytes()) } #[cfg(feature = "glsl")] pipeline::ShaderModuleSource::Glsl(ref code, _) => { trace.make_binary("glsl", code.as_bytes()) } #[cfg(feature = "spirv")] pipeline::ShaderModuleSource::SpirV(ref code, _) => { trace.make_binary("spirv", bytemuck::cast_slice::(code)) } pipeline::ShaderModuleSource::Naga(ref module) => { let string = ron::ser::to_string_pretty(module, ron::ser::PrettyConfig::default()) .unwrap(); trace.make_binary("ron", string.as_bytes()) } pipeline::ShaderModuleSource::Dummy(_) => { panic!("found `ShaderModuleSource::Dummy`") } }; trace.add(trace::Action::CreateShaderModule { id: fid.id(), desc: desc.clone(), data, }); }; let shader = match device.create_shader_module(desc, source) { Ok(shader) => shader, Err(e) => break 'error e, }; let id = fid.assign(Fallible::Valid(shader)); api_log!("Device::create_shader_module -> {id:?}"); return (id, None); }; let id = fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string()))); (id, Some(error)) } #[allow(unused_unsafe)] /// # Safety /// /// This function passes source code or binary to the backend as-is and can potentially result in a /// driver crash. pub unsafe fn device_create_shader_module_passthrough( &self, device_id: DeviceId, desc: &pipeline::ShaderModuleDescriptorPassthrough<'_>, id_in: Option, ) -> ( id::ShaderModuleId, Option, ) { profiling::scope!("Device::create_shader_module_passthrough"); let hub = &self.hub; let fid = hub.shader_modules.prepare(id_in); let error = 'error: { let device = self.hub.devices.get(device_id); #[cfg(feature = "trace")] if let Some(ref mut trace) = *device.trace.lock() { let mut file_names = Vec::new(); for (data, ext) in [ (desc.spirv.as_ref().map(|a| bytemuck::cast_slice(a)), "spv"), (desc.dxil.as_deref(), "dxil"), (desc.hlsl.as_ref().map(|a| a.as_bytes()), "hlsl"), (desc.msl.as_ref().map(|a| a.as_bytes()), "msl"), (desc.glsl.as_ref().map(|a| a.as_bytes()), "glsl"), (desc.wgsl.as_ref().map(|a| a.as_bytes()), "wgsl"), ] { if let Some(data) = data { file_names.push(trace.make_binary(ext, data)); } } trace.add(trace::Action::CreateShaderModulePassthrough { id: fid.id(), data: file_names, entry_point: desc.entry_point.clone(), label: desc.label.clone(), num_workgroups: desc.num_workgroups, runtime_checks: desc.runtime_checks, }); }; let result = unsafe { device.create_shader_module_passthrough(desc) }; let shader = match result { Ok(shader) => shader, Err(e) => break 'error e, }; let id = fid.assign(Fallible::Valid(shader)); api_log!("Device::create_shader_module_spirv -> {id:?}"); return (id, None); }; let id = fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string()))); (id, Some(error)) } pub fn shader_module_drop(&self, shader_module_id: id::ShaderModuleId) { profiling::scope!("ShaderModule::drop"); api_log!("ShaderModule::drop {shader_module_id:?}"); let hub = &self.hub; let _shader_module = hub.shader_modules.remove(shader_module_id); #[cfg(feature = "trace")] if let Ok(shader_module) = _shader_module.get() { if let Some(t) = shader_module.device.trace.lock().as_mut() { t.add(trace::Action::DestroyShaderModule(shader_module_id)); } } } pub fn device_create_command_encoder( &self, device_id: DeviceId, desc: &wgt::CommandEncoderDescriptor