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 [`Self::create_buffer_error`] for more context and explanation. pubfn create_render_bundle_error(
&self,
id_in: Option<id::RenderBundleId>,
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. 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())));
}
/// Assign `id_in` an error with the given `label`. /// /// See [`Self::create_buffer_error`] for more context and explanation. pubfn create_external_texture_error(
&self,
id_in: Option<id::ExternalTextureId>,
desc: &resource::ExternalTextureDescriptor,
) { let fid = self.hub.external_textures.prepare(id_in);
fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string())));
}
/// 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. pubfn create_bind_group_layout_error(
&self,
id_in: Option<id::BindGroupLayoutId>,
label: Option<Cow<'_, str>>,
) { let fid = self.hub.bind_group_layouts.prepare(id_in);
fid.assign(Fallible::Invalid(Arc::new(label.to_string())));
}
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 /// - The `initial_state` must match the actual driver-side state of /// the wrapped resource at the moment of wrap. pubunsafefn create_texture_from_hal(
&self,
hal_texture: Box<dyn hal::DynTexture>,
device_id: DeviceId,
desc: &resource::TextureDescriptor,
initial_state: wgt::TextureUses,
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);
let texture = match device.create_texture_from_hal(hal_texture, desc, initial_state) {
Ok(texture) => texture,
Err(error) => break'error error,
};
// 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(
texture.to_trace(),
desc.clone(),
));
}
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. pubunsafefn create_buffer_from_hal<A: hal::Api>(
&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);
let (buffer, err) = unsafe { device.create_buffer_from_hal(Box::new(hal_buffer), desc) };
// 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() { match &buffer {
Fallible::Valid(arc) => {
trace.add(trace::Action::CreateBuffer(arc.to_trace(), desc.clone()))
}
Fallible::Invalid(_) => {}
}
}
let id = fid.assign(buffer);
api_log!("Device::create_buffer -> {id:?}");
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;
};
/// 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);
#[cfg(feature = "trace")] let data = device.trace.lock().as_mut().map(|trace| { usecrate::device::trace::DataKind;
let shader = match device.create_shader_module(desc, source) {
Ok(shader) => shader,
Err(e) => break'error e,
};
#[cfg(feature = "trace")] iflet Some(data) = data { // We don't need these two operations with the trace to be atomic.
device
.trace
.lock()
.as_mut()
.expect("trace went away during create_shader_module?")
.add(trace::Action::CreateShaderModule {
id: shader.to_trace(),
desc: desc.clone(),
data,
});
};
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))
}
/// # Safety /// /// This function passes source code or binary to the backend as-is and can potentially result in a /// driver crash. pubunsafefn device_create_shader_module_passthrough(
&self,
device_id: DeviceId,
desc: &pipeline::ShaderModuleDescriptorPassthrough<'_>,
id_in: Option<id::ShaderModuleId>,
) -> (
id::ShaderModuleId,
Option<pipeline::CreateShaderModuleError>,
) {
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);
let result = unsafe { device.create_shader_module_passthrough(desc) };
let shader = match result {
Ok(shader) => shader,
Err(e) => break'error e,
};
let pipeline = match res {
Ok(pair) => pair,
Err(e) => break'error e,
};
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())));
(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 pipeline = match res {
Ok(pair) => pair,
Err(e) => break'error e,
};
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())));
(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(),
};
/// # 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);
/// 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,
poll_type: wgt::PollType<crate::SubmissionIndex>,
) -> Result<wgt::PollStatus, WaitIdleError> {
api_log!("Device::poll {poll_type:?}");
let device = self.hub.devices.get(device_id);
let (closures, result) = device.poll_and_return_closures(poll_type);
closures.fire();
result
}
/// 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,
closure_list: &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 poll_type = if force_wait { // TODO(#8286): Should expose timeout to poll_all.
wgt::PollType::wait_indefinitely()
} else {
wgt::PollType::Poll
};
let (closures, result) = device.poll_and_return_closures(poll_type);
let is_queue_empty = matches!(result, Ok(wgt::PollStatus::QueueEmpty));
all_queue_empty &= is_queue_empty;
closure_list.extend(closures);
}
}
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)?;
closures.fire();
Ok(all_queue_empty)
}
/// # Safety /// /// - See [wgpu::Device::start_graphics_debugger_capture][api] for details the safety. /// /// [api]: ../../wgpu/struct.Device.html#method.start_graphics_debugger_capture pubunsafefn device_start_graphics_debugger_capture(&self, device_id: DeviceId) { unsafe { self.hub
.devices
.get(device_id)
.start_graphics_debugger_capture();
}
}
/// # Safety /// /// - See [wgpu::Device::stop_graphics_debugger_capture][api] for details the safety. /// /// [api]: ../../wgpu/struct.Device.html#method.stop_graphics_debugger_capture pubunsafefn device_stop_graphics_debugger_capture(&self, device_id: DeviceId) { unsafe { self.hub
.devices
.get(device_id)
.stop_graphics_debugger_capture();
}
}
// 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.