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 pubfn create_buffer_error(
&self,
id_in: Option<id::BufferId>,
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 `create_buffer_error` for more context and explanation. pubfn create_texture_error(
&self,
id_in: Option<id::TextureId>,
desc: &resource::TextureDescriptor,
) { let fid = self.hub.textures.prepare(id_in);
fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string())));
}
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 pubunsafefn create_texture_from_hal(
&self,
hal_texture: Box<dyn hal::DynTexture>,
device_id: DeviceId,
desc: &resource::TextureDescriptor,
id_in: Option<id::TextureId>,
) -> (id::TextureId, Option<resource::CreateTextureError>) {
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")] iflet Some(refmut 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 pubunsafefn create_buffer_from_hal<A: HalApi>(
&self,
hal_buffer: A::Buffer,
device_id: DeviceId,
desc: &resource::BufferDescriptor,
id_in: Option<id::BufferId>,
) -> (id::BufferId, Option<CreateBufferError>) {
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")] iflet Some(trace) = device.trace.lock().as_mut() {
trace.add(trace::Action::CreateBuffer(fid.id(), desc.clone()));
}
let (buffer, err) = device.create_buffer_from_hal(Box::new(hal_buffer), desc);
let id = fid.assign(buffer);
api_log!("Device::create_buffer -> {id:?}");
// this check can't go in the body of `create_bind_group_layout` since the closure might not get called iflet 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()));
/// Create a shader module with the given `source`. /// /// <div class="warning"> // 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. /// /// </div> pubfn device_create_shader_module(
&self,
device_id: DeviceId,
desc: &pipeline::ShaderModuleDescriptor,
source: pipeline::ShaderModuleSource,
id_in: Option<id::ShaderModuleId>,
) -> (
id::ShaderModuleId,
Option<pipeline::CreateShaderModuleError>,
) {
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);
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))
}
// Unsafe-ness of internal calls has little to do with unsafe-ness of this. #[allow(unused_unsafe)] /// # Safety /// /// This function passes SPIR-V binary to the backend as-is and can potentially result in a /// driver crash. pubunsafefn device_create_shader_module_spirv(
&self,
device_id: DeviceId,
desc: &pipeline::ShaderModuleDescriptor,
source: Cow<[u32]>,
id_in: Option<id::ShaderModuleId>,
) -> (
id::ShaderModuleId,
Option<pipeline::CreateShaderModuleError>,
) {
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);
let hub = &self.hub; let fid = hub
.command_buffers
.prepare(id_in.map(|id| id.into_command_buffer_id()));
let device = self.hub.devices.get(device_id);
let error = 'error: { let command_buffer = match device.create_command_encoder(&desc.label) {
Ok(command_buffer) => command_buffer,
Err(e) => break'error e,
};
let id = fid.assign(command_buffer);
api_log!("Device::create_command_encoder -> {id:?}"); return (id.into_command_encoder_id(), None);
};
let id = fid.assign(Arc::new(CommandBuffer::new_invalid(&device, &desc.label)));
(id.into_command_encoder_id(), Some(error))
}
let missing_implicit_pipeline_ids =
desc.layout.is_none() && id_in.is_some() && implicit_pipeline_ids.is_none();
let fid = hub.render_pipelines.prepare(id_in); let implicit_context = implicit_pipeline_ids.map(|ipi| ipi.prepare(hub));
let error = 'error: { if missing_implicit_pipeline_ids { // TODO: categorize this error as API misuse break'error pipeline::ImplicitLayoutError::MissingImplicitPipelineIds.into();
}
let pipeline = match device.create_render_pipeline(desc) {
Ok(pair) => pair,
Err(e) => break'error e,
};
iflet Some(ids) = implicit_context.as_ref() { let group_count = pipeline.layout.bind_group_layouts.len(); if ids.group_ids.len() < group_count {
log::error!( "Not enough bind group IDs ({}) specified for the implicit layout ({})",
ids.group_ids.len(),
group_count
); // TODO: categorize this error as API misuse break'error pipeline::ImplicitLayoutError::MissingIds(group_count as _)
.into();
}
letmut pipeline_layout_guard = hub.pipeline_layouts.write(); letmut bgl_guard = hub.bind_group_layouts.write();
pipeline_layout_guard.insert(ids.root_id, Fallible::Valid(pipeline.layout.clone())); letmut group_ids = ids.group_ids.iter(); // NOTE: If the first iterator is longer than the second, the `.zip()` impl will still advance the // the first iterator before realizing that the second iterator has finished. // The `pipeline.layout.bind_group_layouts` iterator will always be shorter than `ids.group_ids`, // so using it as the first iterator for `.zip()` will work properly. for (bgl, bgl_id) in pipeline
.layout
.bind_group_layouts
.iter()
.zip(&mut group_ids)
{
bgl_guard.insert(*bgl_id, Fallible::Valid(bgl.clone()));
} for bgl_id in group_ids {
bgl_guard.insert(*bgl_id, Fallible::Invalid(Arc::new(String::new())));
}
}
let id = fid.assign(Fallible::Valid(pipeline));
api_log!("Device::create_render_pipeline -> {id:?}");
return (id, None);
};
let id = fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string())));
// We also need to assign errors to the implicit pipeline layout and the // implicit bind group layouts. iflet Some(ids) = implicit_context { letmut pipeline_layout_guard = hub.pipeline_layouts.write(); letmut bgl_guard = hub.bind_group_layouts.write();
pipeline_layout_guard.insert(ids.root_id, Fallible::Invalid(Arc::new(String::new()))); for bgl_id in ids.group_ids {
bgl_guard.insert(bgl_id, Fallible::Invalid(Arc::new(String::new())));
}
}
(id, Some(error))
}
/// Get an ID of one of the bind group layouts. The ID adds a refcount, /// which needs to be released by calling `bind_group_layout_drop`. pubfn render_pipeline_get_bind_group_layout(
&self,
pipeline_id: id::RenderPipelineId,
index: u32,
id_in: Option<id::BindGroupLayoutId>,
) -> (
id::BindGroupLayoutId,
Option<binding_model::GetBindGroupLayoutError>,
) { let hub = &self.hub;
let fid = hub.bind_group_layouts.prepare(id_in);
let error = 'error: { let pipeline = match hub.render_pipelines.get(pipeline_id).get() {
Ok(pipeline) => pipeline,
Err(e) => break'error e.into(),
}; let id = match pipeline.layout.bind_group_layouts.get(index as usize) {
Some(bg) => fid.assign(Fallible::Valid(bg.clone())),
None => { break'error binding_model::GetBindGroupLayoutError::InvalidGroupIndex(index)
}
}; return (id, None);
};
let id = fid.assign(Fallible::Invalid(Arc::new(String::new())));
(id, Some(error))
}
let missing_implicit_pipeline_ids =
desc.layout.is_none() && id_in.is_some() && implicit_pipeline_ids.is_none();
let fid = hub.compute_pipelines.prepare(id_in); let implicit_context = implicit_pipeline_ids.map(|ipi| ipi.prepare(hub));
let error = 'error: { if missing_implicit_pipeline_ids { // TODO: categorize this error as API misuse break'error pipeline::ImplicitLayoutError::MissingImplicitPipelineIds.into();
}
let pipeline = match device.create_compute_pipeline(desc) {
Ok(pair) => pair,
Err(e) => break'error e,
};
iflet Some(ids) = implicit_context.as_ref() { let group_count = pipeline.layout.bind_group_layouts.len(); if ids.group_ids.len() < group_count {
log::error!( "Not enough bind group IDs ({}) specified for the implicit layout ({})",
ids.group_ids.len(),
group_count
); // TODO: categorize this error as API misuse break'error pipeline::ImplicitLayoutError::MissingIds(group_count as _)
.into();
}
letmut pipeline_layout_guard = hub.pipeline_layouts.write(); letmut bgl_guard = hub.bind_group_layouts.write();
pipeline_layout_guard.insert(ids.root_id, Fallible::Valid(pipeline.layout.clone())); letmut group_ids = ids.group_ids.iter(); // NOTE: If the first iterator is longer than the second, the `.zip()` impl will still advance the // the first iterator before realizing that the second iterator has finished. // The `pipeline.layout.bind_group_layouts` iterator will always be shorter than `ids.group_ids`, // so using it as the first iterator for `.zip()` will work properly. for (bgl, bgl_id) in pipeline
.layout
.bind_group_layouts
.iter()
.zip(&mut group_ids)
{
bgl_guard.insert(*bgl_id, Fallible::Valid(bgl.clone()));
} for bgl_id in group_ids {
bgl_guard.insert(*bgl_id, Fallible::Invalid(Arc::new(String::new())));
}
}
let id = fid.assign(Fallible::Valid(pipeline));
api_log!("Device::create_compute_pipeline -> {id:?}");
return (id, None);
};
let id = fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string())));
// We also need to assign errors to the implicit pipeline layout and the // implicit bind group layouts. iflet Some(ids) = implicit_context { letmut pipeline_layout_guard = hub.pipeline_layouts.write(); letmut bgl_guard = hub.bind_group_layouts.write();
pipeline_layout_guard.insert(ids.root_id, Fallible::Invalid(Arc::new(String::new()))); for bgl_id in ids.group_ids {
bgl_guard.insert(bgl_id, Fallible::Invalid(Arc::new(String::new())));
}
}
(id, Some(error))
}
/// Get an ID of one of the bind group layouts. The ID adds a refcount, /// which needs to be released by calling `bind_group_layout_drop`. pubfn compute_pipeline_get_bind_group_layout(
&self,
pipeline_id: id::ComputePipelineId,
index: u32,
id_in: Option<id::BindGroupLayoutId>,
) -> (
id::BindGroupLayoutId,
Option<binding_model::GetBindGroupLayoutError>,
) { let hub = &self.hub;
let fid = hub.bind_group_layouts.prepare(id_in);
let error = 'error: { let pipeline = match hub.compute_pipelines.get(pipeline_id).get() {
Ok(pipeline) => pipeline,
Err(e) => break'error e.into(),
};
let id = match pipeline.layout.bind_group_layouts.get(index as usize) {
Some(bg) => fid.assign(Fallible::Valid(bg.clone())),
None => { break'error binding_model::GetBindGroupLayoutError::InvalidGroupIndex(index)
}
};
return (id, None);
};
let id = fid.assign(Fallible::Invalid(Arc::new(String::new())));
(id, Some(error))
}
/// # Safety /// The `data` argument of `desc` must have been returned by /// [Self::pipeline_cache_get_data] for the same adapter pubunsafefn device_create_pipeline_cache(
&self,
device_id: DeviceId,
desc: &pipeline::PipelineCacheDescriptor<'_>,
id_in: Option<id::PipelineCacheId>,
) -> (
id::PipelineCacheId,
Option<pipeline::CreatePipelineCacheError>,
) {
profiling::scope!("Device::create_pipeline_cache");
let hub = &self.hub;
let fid = hub.pipeline_caches.prepare(id_in); let error: pipeline::CreatePipelineCacheError = 'error: { let device = self.hub.devices.get(device_id);
for &fallback in fallbacks { if caps.composite_alpha_modes.contains(&fallback) { break'alpha fallback;
}
}
unreachable!( "Fallback system failed to choose alpha mode. This is a bug. \
AlphaMode: {:?}, Options: {:?}",
config.composite_alpha_mode, &caps.composite_alpha_modes
);
};
log::debug!("configuring surface with {:?}", config);
let error = 'error: { // User callbacks must not be called while we are holding locks. let user_callbacks;
{ let device = self.hub.devices.get(device_id);
// Wait for all work to finish before configuring the surface. let snatch_guard = device.snatchable_lock.read(); let fence = device.fence.read(); match device.maintain(fence, wgt::Maintain::Wait, snatch_guard) {
Ok((closures, _)) => {
user_callbacks = closures;
}
Err(e) => { break'error e.into();
}
}
// All textures must be destroyed before the surface can be re-configured. iflet Some(present) = surface.presentation.lock().take() { if present.acquired_texture.is_some() { break'error E::PreviousOutputExists;
}
}
// TODO: Texture views may still be alive that point to the texture. // this will allow the user to render to the surface texture, long after // it has been removed. // // https://github.com/gfx-rs/wgpu/issues/4105
/// Check `device_id` for freeable resources and completed buffer mappings. /// /// Return `queue_empty` indicating whether there are more queue submissions still in flight. pubfn device_poll(
&self,
device_id: DeviceId,
maintain: wgt::Maintain<crate::SubmissionIndex>,
) -> Result<bool, WaitIdleError> {
api_log!("Device::poll {maintain:?}");
let device = self.hub.devices.get(device_id);
let DevicePoll {
closures,
queue_empty,
} = Self::poll_single_device(&device, maintain)?;
closures.fire();
Ok(queue_empty)
}
fn poll_single_device(
device: &crate::device::Device,
maintain: wgt::Maintain<crate::SubmissionIndex>,
) -> Result<DevicePoll, WaitIdleError> { let snatch_guard = device.snatchable_lock.read(); let fence = device.fence.read(); let (closures, queue_empty) = device.maintain(fence, maintain, snatch_guard)?;
// Some deferred destroys are scheduled in maintain so run this right after // to avoid holding on to them until the next device poll.
device.deferred_resource_destruction();
Ok(DevicePoll {
closures,
queue_empty,
})
}
/// Poll all devices belonging to the specified backend. /// /// If `force_wait` is true, block until all buffer mappings are done. /// /// Return `all_queue_empty` indicating whether there are more queue /// submissions still in flight. fn poll_all_devices_of_api(
&self,
force_wait: bool,
closures: &mut UserClosures,
) -> Result<bool, WaitIdleError> {
profiling::scope!("poll_device");
let hub = &self.hub; letmut all_queue_empty = true;
{ let device_guard = hub.devices.read();
for (_id, device) in device_guard.iter() { let maintain = if force_wait {
wgt::Maintain::Wait
} else {
wgt::Maintain::Poll
};
let DevicePoll {
closures: cbs,
queue_empty,
} = Self::poll_single_device(device, maintain)?;
all_queue_empty &= queue_empty;
closures.extend(cbs);
}
}
Ok(all_queue_empty)
}
/// Poll all devices on all backends. /// /// This is the implementation of `wgpu::Instance::poll_all`. /// /// Return `all_queue_empty` indicating whether there are more queue /// submissions still in flight. pubfn poll_all_devices(&self, force_wait: bool) -> Result<bool, WaitIdleError> {
api_log!("poll_all_devices"); letmut closures = UserClosures::default(); let all_queue_empty = self.poll_all_devices_of_api(force_wait, &mut closures)?;
// Follow the steps at // https://gpuweb.github.io/gpuweb/#dom-gpudevice-destroy. // It's legal to call destroy multiple times, but if the device // is already invalid, there's nothing more to do. There's also // no need to return an error. if !device.is_valid() { return;
}
// The last part of destroy is to lose the device. The spec says // delay that until all "currently-enqueued operations on any // queue on this device are completed." This is accomplished by // setting valid to false, and then relying upon maintain to // check for empty queues and a DeviceLostClosure. At that time, // the DeviceLostClosure will be called with "destroyed" as the // reason.
device.valid.store(false, Ordering::Release);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.