summaryrefslogtreecommitdiffstats
path: root/third_party/rust/wgpu-core/src/device
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-12 05:43:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-12 05:43:14 +0000
commit8dd16259287f58f9273002717ec4d27e97127719 (patch)
tree3863e62a53829a84037444beab3abd4ed9dfc7d0 /third_party/rust/wgpu-core/src/device
parentReleasing progress-linux version 126.0.1-1~progress7.99u1. (diff)
downloadfirefox-8dd16259287f58f9273002717ec4d27e97127719.tar.xz
firefox-8dd16259287f58f9273002717ec4d27e97127719.zip
Merging upstream version 127.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/wgpu-core/src/device')
-rw-r--r--third_party/rust/wgpu-core/src/device/any_device.rs2
-rw-r--r--third_party/rust/wgpu-core/src/device/bgl.rs2
-rw-r--r--third_party/rust/wgpu-core/src/device/global.rs77
-rw-r--r--third_party/rust/wgpu-core/src/device/life.rs123
-rw-r--r--third_party/rust/wgpu-core/src/device/mod.rs37
-rw-r--r--third_party/rust/wgpu-core/src/device/queue.rs169
-rw-r--r--third_party/rust/wgpu-core/src/device/resource.rs544
7 files changed, 500 insertions, 454 deletions
diff --git a/third_party/rust/wgpu-core/src/device/any_device.rs b/third_party/rust/wgpu-core/src/device/any_device.rs
index 693155a753..9e459c1a94 100644
--- a/third_party/rust/wgpu-core/src/device/any_device.rs
+++ b/third_party/rust/wgpu-core/src/device/any_device.rs
@@ -34,7 +34,7 @@ impl AnyDevice {
unsafe fn drop_glue<A: HalApi>(ptr: *mut ()) {
// Drop the arc this instance is holding.
unsafe {
- _ = Arc::from_raw(ptr.cast::<A::Surface>());
+ _ = Arc::from_raw(ptr.cast::<A::Device>());
}
}
diff --git a/third_party/rust/wgpu-core/src/device/bgl.rs b/third_party/rust/wgpu-core/src/device/bgl.rs
index d606f049a3..911ac8a435 100644
--- a/third_party/rust/wgpu-core/src/device/bgl.rs
+++ b/third_party/rust/wgpu-core/src/device/bgl.rs
@@ -58,7 +58,7 @@ impl EntryMap {
assert!(self.sorted);
}
- /// Create a new [`BindGroupLayoutEntryMap`] from a slice of [`wgt::BindGroupLayoutEntry`]s.
+ /// Create a new [`EntryMap`] from a slice of [`wgt::BindGroupLayoutEntry`]s.
///
/// Errors if there are duplicate bindings or if any binding index is greater than
/// the device's limits.
diff --git a/third_party/rust/wgpu-core/src/device/global.rs b/third_party/rust/wgpu-core/src/device/global.rs
index 0c97e1b504..be524840b8 100644
--- a/third_party/rust/wgpu-core/src/device/global.rs
+++ b/third_party/rust/wgpu-core/src/device/global.rs
@@ -11,6 +11,7 @@ use crate::{
id::{self, AdapterId, DeviceId, QueueId, SurfaceId},
init_tracker::TextureInitTracker,
instance::{self, Adapter, Surface},
+ lock::{rank, RwLock},
pipeline, present,
resource::{self, BufferAccessResult},
resource::{BufferAccessError, BufferMapOperation, CreateBufferError, Resource},
@@ -20,7 +21,6 @@ use crate::{
use arrayvec::ArrayVec;
use hal::Device as _;
-use parking_lot::RwLock;
use wgt::{BufferAddress, TextureFormat};
@@ -257,7 +257,7 @@ impl Global {
hal::BufferUses::COPY_DST
};
- let (id, resource) = fid.assign(buffer);
+ let (id, resource) = fid.assign(Arc::new(buffer));
api_log!("Device::create_buffer({desc:?}) -> {id:?}");
device
@@ -572,7 +572,7 @@ impl Global {
Err(error) => break error,
};
- let (id, resource) = fid.assign(texture);
+ let (id, resource) = fid.assign(Arc::new(texture));
api_log!("Device::create_texture({desc:?}) -> {id:?}");
device
@@ -643,10 +643,12 @@ impl Global {
texture.hal_usage |= hal::TextureUses::COPY_DST;
}
- texture.initialization_status =
- RwLock::new(TextureInitTracker::new(desc.mip_level_count, 0));
+ texture.initialization_status = RwLock::new(
+ rank::TEXTURE_INITIALIZATION_STATUS,
+ TextureInitTracker::new(desc.mip_level_count, 0),
+ );
- let (id, resource) = fid.assign(texture);
+ let (id, resource) = fid.assign(Arc::new(texture));
api_log!("Device::create_texture({desc:?}) -> {id:?}");
device
@@ -699,7 +701,7 @@ impl Global {
let buffer = device.create_buffer_from_hal(hal_buffer, desc);
- let (id, buffer) = fid.assign(buffer);
+ let (id, buffer) = fid.assign(Arc::new(buffer));
api_log!("Device::create_buffer -> {id:?}");
device
@@ -818,7 +820,7 @@ impl Global {
Err(e) => break e,
};
- let (id, resource) = fid.assign(view);
+ let (id, resource) = fid.assign(Arc::new(view));
{
let mut views = texture.views.lock();
@@ -900,7 +902,7 @@ impl Global {
Err(e) => break e,
};
- let (id, resource) = fid.assign(sampler);
+ let (id, resource) = fid.assign(Arc::new(sampler));
api_log!("Device::create_sampler -> {id:?}");
device.trackers.lock().samplers.insert_single(resource);
@@ -982,7 +984,7 @@ impl Global {
let bgl =
device.create_bind_group_layout(&desc.label, entry_map, bgl::Origin::Pool)?;
- let (id_inner, arc) = fid.take().unwrap().assign(bgl);
+ let (id_inner, arc) = fid.take().unwrap().assign(Arc::new(bgl));
id = Some(id_inner);
Ok(arc)
@@ -1063,7 +1065,7 @@ impl Global {
Err(e) => break e,
};
- let (id, _) = fid.assign(layout);
+ let (id, _) = fid.assign(Arc::new(layout));
api_log!("Device::create_pipeline_layout -> {id:?}");
return (id, None);
};
@@ -1130,7 +1132,7 @@ impl Global {
Err(e) => break e,
};
- let (id, resource) = fid.assign(bind_group);
+ let (id, resource) = fid.assign(Arc::new(bind_group));
let weak_ref = Arc::downgrade(&resource);
for range in &resource.used_texture_ranges {
@@ -1170,6 +1172,20 @@ impl Global {
}
}
+ /// 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>
pub fn device_create_shader_module<A: HalApi>(
&self,
device_id: DeviceId,
@@ -1231,7 +1247,7 @@ impl Global {
Err(e) => break e,
};
- let (id, _) = fid.assign(shader);
+ let (id, _) = fid.assign(Arc::new(shader));
api_log!("Device::create_shader_module -> {id:?}");
return (id, None);
};
@@ -1288,7 +1304,7 @@ impl Global {
Ok(shader) => shader,
Err(e) => break e,
};
- let (id, _) = fid.assign(shader);
+ let (id, _) = fid.assign(Arc::new(shader));
api_log!("Device::create_shader_module_spirv -> {id:?}");
return (id, None);
};
@@ -1320,7 +1336,9 @@ impl Global {
profiling::scope!("Device::create_command_encoder");
let hub = A::hub(self);
- let fid = hub.command_buffers.prepare(id_in.map(|id| id.transmute()));
+ let fid = hub
+ .command_buffers
+ .prepare(id_in.map(|id| id.into_command_buffer_id()));
let error = loop {
let device = match hub.devices.get(device_id) {
@@ -1335,9 +1353,6 @@ impl Global {
};
let encoder = match device
.command_allocator
- .lock()
- .as_mut()
- .unwrap()
.acquire_encoder(device.raw(), queue.raw.as_ref().unwrap())
{
Ok(raw) => raw,
@@ -1353,13 +1368,13 @@ impl Global {
.map(|s| s.to_string()),
);
- let (id, _) = fid.assign(command_buffer);
+ let (id, _) = fid.assign(Arc::new(command_buffer));
api_log!("Device::create_command_encoder -> {id:?}");
- return (id.transmute(), None);
+ return (id.into_command_encoder_id(), None);
};
let id = fid.assign_error(desc.label.borrow_or_default());
- (id.transmute(), Some(error))
+ (id.into_command_encoder_id(), Some(error))
}
pub fn command_buffer_label<A: HalApi>(&self, id: id::CommandBufferId) -> String {
@@ -1374,7 +1389,7 @@ impl Global {
if let Some(cmd_buf) = hub
.command_buffers
- .unregister(command_encoder_id.transmute())
+ .unregister(command_encoder_id.into_command_buffer_id())
{
cmd_buf.data.lock().as_mut().unwrap().encoder.discard();
cmd_buf
@@ -1386,7 +1401,7 @@ impl Global {
pub fn command_buffer_drop<A: HalApi>(&self, command_buffer_id: id::CommandBufferId) {
profiling::scope!("CommandBuffer::drop");
api_log!("CommandBuffer::drop {command_buffer_id:?}");
- self.command_encoder_drop::<A>(command_buffer_id.transmute())
+ self.command_encoder_drop::<A>(command_buffer_id.into_command_encoder_id())
}
pub fn device_create_render_bundle_encoder(
@@ -1446,7 +1461,7 @@ impl Global {
Err(e) => break e,
};
- let (id, resource) = fid.assign(render_bundle);
+ let (id, resource) = fid.assign(Arc::new(render_bundle));
api_log!("RenderBundleEncoder::finish -> {id:?}");
device.trackers.lock().bundles.insert_single(resource);
return (id, None);
@@ -1509,7 +1524,7 @@ impl Global {
Err(err) => break err,
};
- let (id, resource) = fid.assign(query_set);
+ let (id, resource) = fid.assign(Arc::new(query_set));
api_log!("Device::create_query_set -> {id:?}");
device.trackers.lock().query_sets.insert_single(resource);
@@ -1587,7 +1602,7 @@ impl Global {
Err(e) => break e,
};
- let (id, resource) = fid.assign(pipeline);
+ let (id, resource) = fid.assign(Arc::new(pipeline));
api_log!("Device::create_render_pipeline -> {id:?}");
device
@@ -1720,7 +1735,7 @@ impl Global {
Err(e) => break e,
};
- let (id, resource) = fid.assign(pipeline);
+ let (id, resource) = fid.assign(Arc::new(pipeline));
api_log!("Device::create_compute_pipeline -> {id:?}");
device
@@ -1956,7 +1971,7 @@ impl Global {
};
let caps = unsafe {
- let suf = A::get_surface(surface);
+ let suf = A::surface_as_hal(surface);
let adapter = &device.adapter;
match adapter.raw.adapter.surface_capabilities(suf.unwrap()) {
Some(caps) => caps,
@@ -2018,7 +2033,6 @@ impl Global {
// Wait for all work to finish before configuring the surface.
let snatch_guard = device.snatchable_lock.read();
let fence = device.fence.read();
- let fence = fence.as_ref().unwrap();
match device.maintain(fence, wgt::Maintain::Wait, snatch_guard) {
Ok((closures, _)) => {
user_callbacks = closures;
@@ -2042,7 +2056,7 @@ impl Global {
// https://github.com/gfx-rs/wgpu/issues/4105
match unsafe {
- A::get_surface(surface)
+ A::surface_as_hal(surface)
.unwrap()
.configure(device.raw(), &hal_config)
} {
@@ -2107,7 +2121,7 @@ impl Global {
.map_err(|_| DeviceError::Invalid)?;
if let wgt::Maintain::WaitForSubmissionIndex(submission_index) = maintain {
- if submission_index.queue_id != device_id.transmute() {
+ if submission_index.queue_id != device_id.into_queue_id() {
return Err(WaitIdleError::WrongSubmissionIndex(
submission_index.queue_id,
device_id,
@@ -2131,7 +2145,6 @@ impl Global {
) -> Result<DevicePoll, WaitIdleError> {
let snatch_guard = device.snatchable_lock.read();
let fence = device.fence.read();
- let fence = fence.as_ref().unwrap();
let (closures, queue_empty) = device.maintain(fence, maintain, snatch_guard)?;
// Some deferred destroys are scheduled in maintain so run this right after
diff --git a/third_party/rust/wgpu-core/src/device/life.rs b/third_party/rust/wgpu-core/src/device/life.rs
index af345015df..0df580e6e6 100644
--- a/third_party/rust/wgpu-core/src/device/life.rs
+++ b/third_party/rust/wgpu-core/src/device/life.rs
@@ -7,6 +7,7 @@ use crate::{
},
hal_api::HalApi,
id,
+ lock::Mutex,
pipeline::{ComputePipeline, RenderPipeline},
resource::{
self, Buffer, DestroyedBuffer, DestroyedTexture, QuerySet, Resource, Sampler,
@@ -18,12 +19,10 @@ use crate::{
};
use smallvec::SmallVec;
-use parking_lot::Mutex;
use std::sync::Arc;
use thiserror::Error;
/// A struct that keeps lists of resources that are no longer needed by the user.
-#[derive(Default)]
pub(crate) struct ResourceMaps<A: HalApi> {
pub buffers: FastHashMap<TrackerIndex, Arc<Buffer<A>>>,
pub staging_buffers: FastHashMap<TrackerIndex, Arc<StagingBuffer<A>>>,
@@ -94,7 +93,7 @@ impl<A: HalApi> ResourceMaps<A> {
destroyed_textures.clear();
}
- pub(crate) fn extend(&mut self, mut other: Self) {
+ pub(crate) fn extend(&mut self, other: &mut Self) {
let ResourceMaps {
buffers,
staging_buffers,
@@ -128,7 +127,37 @@ impl<A: HalApi> ResourceMaps<A> {
}
}
-/// Resources used by a queue submission, and work to be done once it completes.
+/// A command submitted to the GPU for execution.
+///
+/// ## Keeping resources alive while the GPU is using them
+///
+/// [`wgpu_hal`] requires that, when a command is submitted to a queue, all the
+/// resources it uses must remain alive until it has finished executing.
+///
+/// The natural way to satisfy this would be for `ActiveSubmission` to hold
+/// strong references to all the resources used by its commands. However, that
+/// would entail dropping those strong references every time a queue submission
+/// finishes, adjusting the reference counts of all the resources it used. This
+/// is usually needless work: it's rare for the active submission queue to be
+/// the final reference to an object. Usually the user is still holding on to
+/// it.
+///
+/// To avoid this, an `ActiveSubmission` does not initially hold any strong
+/// references to its commands' resources. Instead, each resource tracks the
+/// most recent submission index at which it has been used in
+/// [`ResourceInfo::submission_index`]. When the user drops a resource, if the
+/// submission in which it was last used is still present in the device's queue,
+/// we add the resource to [`ActiveSubmission::last_resources`]. Finally, when
+/// this `ActiveSubmission` is dequeued and dropped in
+/// [`LifetimeTracker::triage_submissions`], we drop `last_resources` along with
+/// it. Thus, unless a resource is dropped by the user, it doesn't need to be
+/// touched at all when processing completed work.
+///
+/// However, it's not clear that this is effective. See [#5560].
+///
+/// [`wgpu_hal`]: hal
+/// [`ResourceInfo::submission_index`]: crate::resource::ResourceInfo
+/// [#5560]: https://github.com/gfx-rs/wgpu/issues/5560
struct ActiveSubmission<A: HalApi> {
/// The index of the submission we track.
///
@@ -150,6 +179,18 @@ struct ActiveSubmission<A: HalApi> {
/// Buffers to be mapped once this submission has completed.
mapped: Vec<Arc<Buffer<A>>>,
+ /// Command buffers used by this submission, and the encoder that owns them.
+ ///
+ /// [`wgpu_hal::Queue::submit`] requires the submitted command buffers to
+ /// remain alive until the submission has completed execution. Command
+ /// encoders double as allocation pools for command buffers, so holding them
+ /// here and cleaning them up in [`LifetimeTracker::triage_submissions`]
+ /// satisfies that requirement.
+ ///
+ /// Once this submission has completed, the command buffers are reset and
+ /// the command encoder is recycled.
+ ///
+ /// [`wgpu_hal::Queue::submit`]: hal::Queue::submit
encoders: Vec<EncoderInFlight<A>>,
/// List of queue "on_submitted_work_done" closures to be called once this
@@ -330,28 +371,25 @@ impl<A: HalApi> LifetimeTracker<A> {
///
/// Assume that all submissions up through `last_done` have completed.
///
- /// - Buffers used by those submissions are now ready to map, if
- /// requested. Add any buffers in the submission's [`mapped`] list to
- /// [`self.ready_to_map`], where [`LifetimeTracker::handle_mapping`] will find
- /// them.
+ /// - Buffers used by those submissions are now ready to map, if requested.
+ /// Add any buffers in the submission's [`mapped`] list to
+ /// [`self.ready_to_map`], where [`LifetimeTracker::handle_mapping`]
+ /// will find them.
///
/// - Resources whose final use was in those submissions are now ready to
- /// free. Add any resources in the submission's [`last_resources`] table
- /// to [`self.free_resources`], where [`LifetimeTracker::cleanup`] will find
- /// them.
+ /// free. Dropping the submission's [`last_resources`] table does so.
///
/// Return a list of [`SubmittedWorkDoneClosure`]s to run.
///
/// [`mapped`]: ActiveSubmission::mapped
/// [`self.ready_to_map`]: LifetimeTracker::ready_to_map
/// [`last_resources`]: ActiveSubmission::last_resources
- /// [`self.free_resources`]: LifetimeTracker::free_resources
/// [`SubmittedWorkDoneClosure`]: crate::device::queue::SubmittedWorkDoneClosure
#[must_use]
pub fn triage_submissions(
&mut self,
last_done: SubmissionIndex,
- command_allocator: &mut super::CommandAllocator<A>,
+ command_allocator: &crate::command::CommandAllocator<A>,
) -> SmallVec<[SubmittedWorkDoneClosure; 1]> {
profiling::scope!("triage_submissions");
@@ -558,6 +596,18 @@ impl<A: HalApi> LifetimeTracker<A> {
&mut trackers.textures,
|maps| &mut maps.textures,
);
+
+ // We may have been suspected because a texture view or bind group
+ // referring to us was dropped. Remove stale weak references, so that
+ // the backlink table doesn't grow without bound.
+ for texture in self.suspected_resources.textures.values() {
+ texture.views.lock().retain(|view| view.strong_count() > 0);
+ texture
+ .bind_groups
+ .lock()
+ .retain(|bg| bg.strong_count() > 0);
+ }
+
self
}
@@ -583,6 +633,13 @@ impl<A: HalApi> LifetimeTracker<A> {
|maps| &mut maps.buffers,
);
+ // We may have been suspected because a bind group referring to us was
+ // dropped. Remove stale weak references, so that the backlink table
+ // doesn't grow without bound.
+ for buffer in self.suspected_resources.buffers.values() {
+ buffer.bind_groups.lock().retain(|bg| bg.strong_count() > 0);
+ }
+
self
}
@@ -693,13 +750,10 @@ impl<A: HalApi> LifetimeTracker<A> {
/// Identify resources to free, according to `trackers` and `self.suspected_resources`.
///
- /// Given `trackers`, the [`Tracker`] belonging to same [`Device`] as
- /// `self`, and `hub`, the [`Hub`] to which that `Device` belongs:
- ///
- /// Remove from `trackers` each resource mentioned in
- /// [`self.suspected_resources`]. If `trackers` held the final reference to
- /// that resource, add it to the appropriate free list, to be destroyed by
- /// the hal:
+ /// Remove from `trackers`, the [`Tracker`] belonging to same [`Device`] as
+ /// `self`, each resource mentioned in [`self.suspected_resources`]. If
+ /// `trackers` held the final reference to that resource, add it to the
+ /// appropriate free list, to be destroyed by the hal:
///
/// - Add resources used by queue submissions still in flight to the
/// [`last_resources`] table of the last such submission's entry in
@@ -799,29 +853,33 @@ impl<A: HalApi> LifetimeTracker<A> {
*buffer.map_state.lock() = resource::BufferMapState::Idle;
log::trace!("Buffer ready to map {tracker_index:?} is not tracked anymore");
} else {
- let mapping = match std::mem::replace(
+ // This _cannot_ be inlined into the match. If it is, the lock will be held
+ // open through the whole match, resulting in a deadlock when we try to re-lock
+ // the buffer back to active.
+ let mapping = std::mem::replace(
&mut *buffer.map_state.lock(),
resource::BufferMapState::Idle,
- ) {
+ );
+ let pending_mapping = match mapping {
resource::BufferMapState::Waiting(pending_mapping) => pending_mapping,
// Mapping cancelled
resource::BufferMapState::Idle => continue,
// Mapping queued at least twice by map -> unmap -> map
// and was already successfully mapped below
- active @ resource::BufferMapState::Active { .. } => {
- *buffer.map_state.lock() = active;
+ resource::BufferMapState::Active { .. } => {
+ *buffer.map_state.lock() = mapping;
continue;
}
_ => panic!("No pending mapping."),
};
- let status = if mapping.range.start != mapping.range.end {
+ let status = if pending_mapping.range.start != pending_mapping.range.end {
log::debug!("Buffer {tracker_index:?} map state -> Active");
- let host = mapping.op.host;
- let size = mapping.range.end - mapping.range.start;
+ let host = pending_mapping.op.host;
+ let size = pending_mapping.range.end - pending_mapping.range.start;
match super::map_buffer(
raw,
&buffer,
- mapping.range.start,
+ pending_mapping.range.start,
size,
host,
snatch_guard,
@@ -829,7 +887,8 @@ impl<A: HalApi> LifetimeTracker<A> {
Ok(ptr) => {
*buffer.map_state.lock() = resource::BufferMapState::Active {
ptr,
- range: mapping.range.start..mapping.range.start + size,
+ range: pending_mapping.range.start
+ ..pending_mapping.range.start + size,
host,
};
Ok(())
@@ -842,12 +901,12 @@ impl<A: HalApi> LifetimeTracker<A> {
} else {
*buffer.map_state.lock() = resource::BufferMapState::Active {
ptr: std::ptr::NonNull::dangling(),
- range: mapping.range,
- host: mapping.op.host,
+ range: pending_mapping.range,
+ host: pending_mapping.op.host,
};
Ok(())
};
- pending_callbacks.push((mapping.op, status));
+ pending_callbacks.push((pending_mapping.op, status));
}
}
pending_callbacks
diff --git a/third_party/rust/wgpu-core/src/device/mod.rs b/third_party/rust/wgpu-core/src/device/mod.rs
index e2ab6c2690..e9da11b7a8 100644
--- a/third_party/rust/wgpu-core/src/device/mod.rs
+++ b/third_party/rust/wgpu-core/src/device/mod.rs
@@ -4,7 +4,6 @@ use crate::{
hub::Hub,
id::{BindGroupLayoutId, PipelineLayoutId},
resource::{Buffer, BufferAccessError, BufferAccessResult, BufferMapOperation},
- resource_log,
snatch::SnatchGuard,
Label, DOWNLEVEL_ERROR_MESSAGE,
};
@@ -377,42 +376,6 @@ fn map_buffer<A: HalApi>(
Ok(mapping.ptr)
}
-pub(crate) struct CommandAllocator<A: HalApi> {
- free_encoders: Vec<A::CommandEncoder>,
-}
-
-impl<A: HalApi> CommandAllocator<A> {
- fn acquire_encoder(
- &mut self,
- device: &A::Device,
- queue: &A::Queue,
- ) -> Result<A::CommandEncoder, hal::DeviceError> {
- match self.free_encoders.pop() {
- Some(encoder) => Ok(encoder),
- None => unsafe {
- let hal_desc = hal::CommandEncoderDescriptor { label: None, queue };
- device.create_command_encoder(&hal_desc)
- },
- }
- }
-
- fn release_encoder(&mut self, encoder: A::CommandEncoder) {
- self.free_encoders.push(encoder);
- }
-
- fn dispose(self, device: &A::Device) {
- resource_log!(
- "CommandAllocator::dispose encoders {}",
- self.free_encoders.len()
- );
- for cmd_encoder in self.free_encoders {
- unsafe {
- device.destroy_command_encoder(cmd_encoder);
- }
- }
- }
-}
-
#[derive(Clone, Debug, Error)]
#[error("Device is invalid")]
pub struct InvalidDevice;
diff --git a/third_party/rust/wgpu-core/src/device/queue.rs b/third_party/rust/wgpu-core/src/device/queue.rs
index 3cb5f695a7..f0db961ffc 100644
--- a/third_party/rust/wgpu-core/src/device/queue.rs
+++ b/third_party/rust/wgpu-core/src/device/queue.rs
@@ -4,16 +4,17 @@ use crate::{
api_log,
command::{
extract_texture_selector, validate_linear_texture_data, validate_texture_copy_range,
- ClearError, CommandBuffer, CopySide, ImageCopyTexture, TransferError,
+ ClearError, CommandAllocator, CommandBuffer, CopySide, ImageCopyTexture, TransferError,
},
conv,
- device::{life::ResourceMaps, DeviceError, WaitIdleError},
+ device::{DeviceError, WaitIdleError},
get_lowest_common_denom,
global::Global,
hal_api::HalApi,
hal_label,
id::{self, DeviceId, QueueId},
init_tracker::{has_copy_partial_init_tracker_coverage, TextureInitRange},
+ lock::{rank, Mutex, RwLockWriteGuard},
resource::{
Buffer, BufferAccessError, BufferMapState, DestroyedBuffer, DestroyedTexture, Resource,
ResourceInfo, ResourceType, StagingBuffer, Texture, TextureInner,
@@ -22,7 +23,6 @@ use crate::{
};
use hal::{CommandEncoder as _, Device as _, Queue as _};
-use parking_lot::Mutex;
use smallvec::SmallVec;
use std::{
@@ -34,9 +34,9 @@ use thiserror::Error;
use super::Device;
pub struct Queue<A: HalApi> {
- pub device: Option<Arc<Device<A>>>,
- pub raw: Option<A::Queue>,
- pub info: ResourceInfo<Queue<A>>,
+ pub(crate) device: Option<Arc<Device<A>>>,
+ pub(crate) raw: Option<A::Queue>,
+ pub(crate) info: ResourceInfo<Queue<A>>,
}
impl<A: HalApi> Resource for Queue<A> {
@@ -152,13 +152,21 @@ pub enum TempResource<A: HalApi> {
Texture(Arc<Texture<A>>),
}
-/// A queue execution for a particular command encoder.
+/// A series of raw [`CommandBuffer`]s that have been submitted to a
+/// queue, and the [`wgpu_hal::CommandEncoder`] that built them.
+///
+/// [`CommandBuffer`]: hal::Api::CommandBuffer
+/// [`wgpu_hal::CommandEncoder`]: hal::CommandEncoder
pub(crate) struct EncoderInFlight<A: HalApi> {
raw: A::CommandEncoder,
cmd_buffers: Vec<A::CommandBuffer>,
}
impl<A: HalApi> EncoderInFlight<A> {
+ /// Free all of our command buffers.
+ ///
+ /// Return the command encoder, fully reset and ready to be
+ /// reused.
pub(crate) unsafe fn land(mut self) -> A::CommandEncoder {
unsafe { self.raw.reset_all(self.cmd_buffers.into_iter()) };
self.raw
@@ -192,6 +200,8 @@ pub(crate) struct PendingWrites<A: HalApi> {
/// True if `command_encoder` is in the "recording" state, as
/// described in the docs for the [`wgpu_hal::CommandEncoder`]
/// trait.
+ ///
+ /// [`wgpu_hal::CommandEncoder`]: hal::CommandEncoder
pub is_recording: bool,
pub temp_resources: Vec<TempResource<A>>,
@@ -253,7 +263,7 @@ impl<A: HalApi> PendingWrites<A> {
#[must_use]
fn post_submit(
&mut self,
- command_allocator: &mut super::CommandAllocator<A>,
+ command_allocator: &CommandAllocator<A>,
device: &A::Device,
queue: &A::Queue,
) -> Option<EncoderInFlight<A>> {
@@ -307,7 +317,7 @@ fn prepare_staging_buffer<A: HalApi>(
let mapping = unsafe { device.raw().map_buffer(&buffer, 0..size) }?;
let staging_buffer = StagingBuffer {
- raw: Mutex::new(Some(buffer)),
+ raw: Mutex::new(rank::STAGING_BUFFER_RAW, Some(buffer)),
device: device.clone(),
size,
info: ResourceInfo::new(
@@ -490,7 +500,7 @@ impl Global {
prepare_staging_buffer(device, buffer_size.get(), device.instance_flags)?;
let fid = hub.staging_buffers.prepare(id_in);
- let (id, _) = fid.assign(staging_buffer);
+ let (id, _) = fid.assign(Arc::new(staging_buffer));
resource_log!("Queue::create_staging_buffer {id:?}");
Ok((id, staging_buffer_ptr))
@@ -707,7 +717,7 @@ impl Global {
.get(destination.texture)
.map_err(|_| TransferError::InvalidTexture(destination.texture))?;
- if dst.device.as_info().id() != queue_id.transmute() {
+ if dst.device.as_info().id().into_queue_id() != queue_id {
return Err(DeviceError::WrongDevice.into());
}
@@ -1152,8 +1162,8 @@ impl Global {
let snatch_guard = device.snatchable_lock.read();
// Fence lock must be acquired after the snatch lock everywhere to avoid deadlocks.
- let mut fence = device.fence.write();
- let fence = fence.as_mut().unwrap();
+ let mut fence_guard = device.fence.write();
+ let fence = fence_guard.as_mut().unwrap();
let submit_index = device
.active_submission_index
.fetch_add(1, Ordering::Relaxed)
@@ -1173,11 +1183,6 @@ impl Global {
//TODO: if multiple command buffers are submitted, we can re-use the last
// native command buffer of the previous chain instead of always creating
// a temporary one, since the chains are not finished.
- let mut temp_suspected = device.temp_suspected.lock();
- {
- let mut suspected = temp_suspected.replace(ResourceMaps::new()).unwrap();
- suspected.clear();
- }
// finish all the command buffers first
for &cmb_id in command_buffer_ids {
@@ -1191,7 +1196,7 @@ impl Global {
Err(_) => continue,
};
- if cmdbuf.device.as_info().id() != queue_id.transmute() {
+ if cmdbuf.device.as_info().id().into_queue_id() != queue_id {
return Err(DeviceError::WrongDevice.into());
}
@@ -1210,13 +1215,10 @@ impl Global {
));
}
if !cmdbuf.is_finished() {
- if let Some(cmdbuf) = Arc::into_inner(cmdbuf) {
- device.destroy_command_buffer(cmdbuf);
- } else {
- panic!(
- "Command buffer cannot be destroyed because is still in use"
- );
- }
+ let cmdbuf = Arc::into_inner(cmdbuf).expect(
+ "Command buffer cannot be destroyed because is still in use",
+ );
+ device.destroy_command_buffer(cmdbuf);
continue;
}
@@ -1228,41 +1230,23 @@ impl Global {
// update submission IDs
for buffer in cmd_buf_trackers.buffers.used_resources() {
- let tracker_index = buffer.info.tracker_index();
- let raw_buf = match buffer.raw.get(&snatch_guard) {
- Some(raw) => raw,
- None => {
- return Err(QueueSubmitError::DestroyedBuffer(
- buffer.info.id(),
- ));
- }
- };
+ if buffer.raw.get(&snatch_guard).is_none() {
+ return Err(QueueSubmitError::DestroyedBuffer(
+ buffer.info.id(),
+ ));
+ }
buffer.info.use_at(submit_index);
- if buffer.is_unique() {
- if let BufferMapState::Active { .. } = *buffer.map_state.lock()
- {
- log::warn!("Dropped buffer has a pending mapping.");
- unsafe { device.raw().unmap_buffer(raw_buf) }
- .map_err(DeviceError::from)?;
- }
- temp_suspected
- .as_mut()
- .unwrap()
- .buffers
- .insert(tracker_index, buffer.clone());
- } else {
- match *buffer.map_state.lock() {
- BufferMapState::Idle => (),
- _ => {
- return Err(QueueSubmitError::BufferStillMapped(
- buffer.info.id(),
- ))
- }
+
+ match *buffer.map_state.lock() {
+ BufferMapState::Idle => (),
+ _ => {
+ return Err(QueueSubmitError::BufferStillMapped(
+ buffer.info.id(),
+ ))
}
}
}
for texture in cmd_buf_trackers.textures.used_resources() {
- let tracker_index = texture.info.tracker_index();
let should_extend = match texture.inner.get(&snatch_guard) {
None => {
return Err(QueueSubmitError::DestroyedTexture(
@@ -1279,13 +1263,6 @@ impl Global {
}
};
texture.info.use_at(submit_index);
- if texture.is_unique() {
- temp_suspected
- .as_mut()
- .unwrap()
- .textures
- .insert(tracker_index, texture.clone());
- }
if should_extend {
unsafe {
used_surface_textures
@@ -1296,12 +1273,6 @@ impl Global {
}
for texture_view in cmd_buf_trackers.views.used_resources() {
texture_view.info.use_at(submit_index);
- if texture_view.is_unique() {
- temp_suspected.as_mut().unwrap().texture_views.insert(
- texture_view.as_info().tracker_index(),
- texture_view.clone(),
- );
- }
}
{
for bg in cmd_buf_trackers.bind_groups.used_resources() {
@@ -1315,13 +1286,6 @@ impl Global {
for sampler in bg.used.samplers.used_resources() {
sampler.info.use_at(submit_index);
}
- if bg.is_unique() {
- temp_suspected
- .as_mut()
- .unwrap()
- .bind_groups
- .insert(bg.as_info().tracker_index(), bg.clone());
- }
}
}
// assert!(cmd_buf_trackers.samplers.is_empty());
@@ -1329,32 +1293,14 @@ impl Global {
cmd_buf_trackers.compute_pipelines.used_resources()
{
compute_pipeline.info.use_at(submit_index);
- if compute_pipeline.is_unique() {
- temp_suspected.as_mut().unwrap().compute_pipelines.insert(
- compute_pipeline.as_info().tracker_index(),
- compute_pipeline.clone(),
- );
- }
}
for render_pipeline in
cmd_buf_trackers.render_pipelines.used_resources()
{
render_pipeline.info.use_at(submit_index);
- if render_pipeline.is_unique() {
- temp_suspected.as_mut().unwrap().render_pipelines.insert(
- render_pipeline.as_info().tracker_index(),
- render_pipeline.clone(),
- );
- }
}
for query_set in cmd_buf_trackers.query_sets.used_resources() {
query_set.info.use_at(submit_index);
- if query_set.is_unique() {
- temp_suspected.as_mut().unwrap().query_sets.insert(
- query_set.as_info().tracker_index(),
- query_set.clone(),
- );
- }
}
for bundle in cmd_buf_trackers.bundles.used_resources() {
bundle.info.use_at(submit_index);
@@ -1369,13 +1315,6 @@ impl Global {
for query_set in bundle.used.query_sets.read().used_resources() {
query_set.info.use_at(submit_index);
}
- if bundle.is_unique() {
- temp_suspected
- .as_mut()
- .unwrap()
- .render_bundles
- .insert(bundle.as_info().tracker_index(), bundle.clone());
- }
}
}
let mut baked = cmdbuf.from_arc_into_baked();
@@ -1452,8 +1391,8 @@ impl Global {
}
}
- let mut pending_writes = device.pending_writes.lock();
- let pending_writes = pending_writes.as_mut().unwrap();
+ let mut pending_writes_guard = device.pending_writes.lock();
+ let pending_writes = pending_writes_guard.as_mut().unwrap();
{
used_surface_textures.set_size(hub.textures.read().len());
@@ -1528,7 +1467,7 @@ impl Global {
profiling::scope!("cleanup");
if let Some(pending_execution) = pending_writes.post_submit(
- device.command_allocator.lock().as_mut().unwrap(),
+ &device.command_allocator,
device.raw(),
queue.raw.as_ref().unwrap(),
) {
@@ -1543,18 +1482,22 @@ impl Global {
active_executions,
);
- // This will schedule destruction of all resources that are no longer needed
- // by the user but used in the command stream, among other things.
- let (closures, _) = match device.maintain(fence, wgt::Maintain::Poll, snatch_guard) {
- Ok(closures) => closures,
- Err(WaitIdleError::Device(err)) => return Err(QueueSubmitError::Queue(err)),
- Err(WaitIdleError::StuckGpu) => return Err(QueueSubmitError::StuckGpu),
- Err(WaitIdleError::WrongSubmissionIndex(..)) => unreachable!(),
- };
-
// pending_write_resources has been drained, so it's empty, but we
// want to retain its heap allocation.
pending_writes.temp_resources = pending_write_resources;
+ drop(pending_writes_guard);
+
+ // This will schedule destruction of all resources that are no longer needed
+ // by the user but used in the command stream, among other things.
+ let fence_guard = RwLockWriteGuard::downgrade(fence_guard);
+ let (closures, _) =
+ match device.maintain(fence_guard, wgt::Maintain::Poll, snatch_guard) {
+ Ok(closures) => closures,
+ Err(WaitIdleError::Device(err)) => return Err(QueueSubmitError::Queue(err)),
+ Err(WaitIdleError::StuckGpu) => return Err(QueueSubmitError::StuckGpu),
+ Err(WaitIdleError::WrongSubmissionIndex(..)) => unreachable!(),
+ };
+
device.lock_life().post_submit();
(submit_index, closures)
diff --git a/third_party/rust/wgpu-core/src/device/resource.rs b/third_party/rust/wgpu-core/src/device/resource.rs
index 4892aecb75..2541af7c70 100644
--- a/third_party/rust/wgpu-core/src/device/resource.rs
+++ b/third_party/rust/wgpu-core/src/device/resource.rs
@@ -7,18 +7,20 @@ use crate::{
bgl,
life::{LifetimeTracker, WaitIdleError},
queue::PendingWrites,
- AttachmentData, CommandAllocator, DeviceLostInvocation, MissingDownlevelFlags,
- MissingFeatures, RenderPassContext, CLEANUP_WAIT_MS,
+ AttachmentData, DeviceLostInvocation, MissingDownlevelFlags, MissingFeatures,
+ RenderPassContext, CLEANUP_WAIT_MS,
},
hal_api::HalApi,
hal_label,
hub::Hub,
+ id,
init_tracker::{
BufferInitTracker, BufferInitTrackerAction, MemoryInitKind, TextureInitRange,
TextureInitTracker, TextureInitTrackerAction,
},
instance::Adapter,
- pipeline,
+ lock::{rank, Mutex, MutexGuard, RwLock},
+ pipeline::{self},
pool::ResourcePool,
registry::Registry,
resource::{
@@ -41,7 +43,6 @@ use crate::{
use arrayvec::ArrayVec;
use hal::{CommandEncoder as _, Device as _};
use once_cell::sync::OnceCell;
-use parking_lot::{Mutex, MutexGuard, RwLock};
use smallvec::SmallVec;
use thiserror::Error;
@@ -97,7 +98,7 @@ pub struct Device<A: HalApi> {
pub(crate) zero_buffer: Option<A::Buffer>,
pub(crate) info: ResourceInfo<Device<A>>,
- pub(crate) command_allocator: Mutex<Option<CommandAllocator<A>>>,
+ pub(crate) command_allocator: command::CommandAllocator<A>,
//Note: The submission index here corresponds to the last submission that is done.
pub(crate) active_submission_index: AtomicU64, //SubmissionIndex,
// NOTE: if both are needed, the `snatchable_lock` must be consistently acquired before the
@@ -126,9 +127,6 @@ pub struct Device<A: HalApi> {
pub(crate) tracker_indices: TrackerIndexAllocators,
// Life tracker should be locked right after the device and before anything else.
life_tracker: Mutex<LifetimeTracker<A>>,
- /// Temporary storage for resource management functions. Cleared at the end
- /// of every call (unless an error occurs).
- pub(crate) temp_suspected: Mutex<Option<ResourceMaps<A>>>,
/// Pool of bind group layouts, allowing deduplication.
pub(crate) bgl_pool: ResourcePool<bgl::EntryMap, BindGroupLayout<A>>,
pub(crate) alignments: hal::Alignments,
@@ -141,6 +139,10 @@ pub struct Device<A: HalApi> {
#[cfg(feature = "trace")]
pub(crate) trace: Mutex<Option<trace::Trace>>,
pub(crate) usage_scopes: UsageScopePool<A>,
+
+ /// Temporary storage, cleared at the start of every call,
+ /// retained only to save allocations.
+ temp_suspected: Mutex<Option<ResourceMaps<A>>>,
}
pub(crate) enum DeferredDestroy<A: HalApi> {
@@ -165,7 +167,7 @@ impl<A: HalApi> Drop for Device<A> {
let raw = self.raw.take().unwrap();
let pending_writes = self.pending_writes.lock().take().unwrap();
pending_writes.dispose(&raw);
- self.command_allocator.lock().take().unwrap().dispose(&raw);
+ self.command_allocator.dispose(&raw);
unsafe {
raw.destroy_buffer(self.zero_buffer.take().unwrap());
raw.destroy_fence(self.fence.write().take().unwrap());
@@ -223,10 +225,8 @@ impl<A: HalApi> Device<A> {
let fence =
unsafe { raw_device.create_fence() }.map_err(|_| CreateDeviceError::OutOfMemory)?;
- let mut com_alloc = CommandAllocator {
- free_encoders: Vec::new(),
- };
- let pending_encoder = com_alloc
+ let command_allocator = command::CommandAllocator::new();
+ let pending_encoder = command_allocator
.acquire_encoder(&raw_device, raw_queue)
.map_err(|_| CreateDeviceError::OutOfMemory)?;
let mut pending_writes = queue::PendingWrites::<A>::new(pending_encoder);
@@ -271,38 +271,44 @@ impl<A: HalApi> Device<A> {
queue_to_drop: OnceCell::new(),
zero_buffer: Some(zero_buffer),
info: ResourceInfo::new("<device>", None),
- command_allocator: Mutex::new(Some(com_alloc)),
+ command_allocator,
active_submission_index: AtomicU64::new(0),
- fence: RwLock::new(Some(fence)),
- snatchable_lock: unsafe { SnatchLock::new() },
+ fence: RwLock::new(rank::DEVICE_FENCE, Some(fence)),
+ snatchable_lock: unsafe { SnatchLock::new(rank::DEVICE_SNATCHABLE_LOCK) },
valid: AtomicBool::new(true),
- trackers: Mutex::new(Tracker::new()),
+ trackers: Mutex::new(rank::DEVICE_TRACKERS, Tracker::new()),
tracker_indices: TrackerIndexAllocators::new(),
- life_tracker: Mutex::new(life::LifetimeTracker::new()),
- temp_suspected: Mutex::new(Some(life::ResourceMaps::new())),
+ life_tracker: Mutex::new(rank::DEVICE_LIFE_TRACKER, life::LifetimeTracker::new()),
+ temp_suspected: Mutex::new(
+ rank::DEVICE_TEMP_SUSPECTED,
+ Some(life::ResourceMaps::new()),
+ ),
bgl_pool: ResourcePool::new(),
#[cfg(feature = "trace")]
- trace: Mutex::new(trace_path.and_then(|path| match trace::Trace::new(path) {
- Ok(mut trace) => {
- trace.add(trace::Action::Init {
- desc: desc.clone(),
- backend: A::VARIANT,
- });
- Some(trace)
- }
- Err(e) => {
- log::error!("Unable to start a trace in '{path:?}': {e}");
- None
- }
- })),
+ trace: Mutex::new(
+ rank::DEVICE_TRACE,
+ trace_path.and_then(|path| match trace::Trace::new(path) {
+ Ok(mut trace) => {
+ trace.add(trace::Action::Init {
+ desc: desc.clone(),
+ backend: A::VARIANT,
+ });
+ Some(trace)
+ }
+ Err(e) => {
+ log::error!("Unable to start a trace in '{path:?}': {e}");
+ None
+ }
+ }),
+ ),
alignments,
limits: desc.required_limits.clone(),
features: desc.required_features,
downlevel,
instance_flags,
- pending_writes: Mutex::new(Some(pending_writes)),
- deferred_destroy: Mutex::new(Vec::new()),
- usage_scopes: Default::default(),
+ pending_writes: Mutex::new(rank::DEVICE_PENDING_WRITES, Some(pending_writes)),
+ deferred_destroy: Mutex::new(rank::DEVICE_DEFERRED_DESTROY, Vec::new()),
+ usage_scopes: Mutex::new(rank::DEVICE_USAGE_SCOPES, Default::default()),
})
}
@@ -379,7 +385,7 @@ impl<A: HalApi> Device<A> {
/// Check this device for completed commands.
///
- /// The `maintain` argument tells how the maintence function should behave, either
+ /// The `maintain` argument tells how the maintenance function should behave, either
/// blocking or just polling the current state of the gpu.
///
/// Return a pair `(closures, queue_empty)`, where:
@@ -392,11 +398,12 @@ impl<A: HalApi> Device<A> {
/// return it to our callers.)
pub(crate) fn maintain<'this>(
&'this self,
- fence: &A::Fence,
+ fence_guard: crate::lock::RwLockReadGuard<Option<A::Fence>>,
maintain: wgt::Maintain<queue::WrappedSubmissionIndex>,
snatch_guard: SnatchGuard,
) -> Result<(UserClosures, bool), WaitIdleError> {
profiling::scope!("Device::maintain");
+ let fence = fence_guard.as_ref().unwrap();
let last_done_index = if maintain.is_wait() {
let index_to_wait_for = match maintain {
wgt::Maintain::WaitForSubmissionIndex(submission_index) => {
@@ -425,28 +432,12 @@ impl<A: HalApi> Device<A> {
};
let mut life_tracker = self.lock_life();
- let submission_closures = life_tracker.triage_submissions(
- last_done_index,
- self.command_allocator.lock().as_mut().unwrap(),
- );
-
- {
- // Normally, `temp_suspected` exists only to save heap
- // allocations: it's cleared at the start of the function
- // call, and cleared by the end. But `Global::queue_submit` is
- // fallible; if it exits early, it may leave some resources in
- // `temp_suspected`.
- let temp_suspected = self
- .temp_suspected
- .lock()
- .replace(ResourceMaps::new())
- .unwrap();
+ let submission_closures =
+ life_tracker.triage_submissions(last_done_index, &self.command_allocator);
- life_tracker.suspected_resources.extend(temp_suspected);
+ life_tracker.triage_suspected(&self.trackers);
- life_tracker.triage_suspected(&self.trackers);
- life_tracker.triage_mapped();
- }
+ life_tracker.triage_mapped();
let mapping_closures =
life_tracker.handle_mapping(self.raw(), &self.trackers, &snatch_guard);
@@ -478,6 +469,7 @@ impl<A: HalApi> Device<A> {
// Don't hold the locks while calling release_gpu_resources.
drop(life_tracker);
+ drop(fence_guard);
drop(snatch_guard);
if should_release_gpu_resource {
@@ -493,12 +485,14 @@ impl<A: HalApi> Device<A> {
}
pub(crate) fn untrack(&self, trackers: &Tracker<A>) {
+ // If we have a previously allocated `ResourceMap`, just use that.
let mut temp_suspected = self
.temp_suspected
.lock()
- .replace(ResourceMaps::new())
- .unwrap();
+ .take()
+ .unwrap_or_else(|| ResourceMaps::new());
temp_suspected.clear();
+
// As the tracker is cleared/dropped, we need to consider all the resources
// that it references for destruction in the next GC pass.
{
@@ -559,7 +553,11 @@ impl<A: HalApi> Device<A> {
}
}
}
- self.lock_life().suspected_resources.extend(temp_suspected);
+ self.lock_life()
+ .suspected_resources
+ .extend(&mut temp_suspected);
+ // Save this resource map for later reuse.
+ *self.temp_suspected.lock() = Some(temp_suspected);
}
pub(crate) fn create_buffer(
@@ -653,14 +651,17 @@ impl<A: HalApi> Device<A> {
device: self.clone(),
usage: desc.usage,
size: desc.size,
- initialization_status: RwLock::new(BufferInitTracker::new(aligned_size)),
- sync_mapped_writes: Mutex::new(None),
- map_state: Mutex::new(resource::BufferMapState::Idle),
+ initialization_status: RwLock::new(
+ rank::BUFFER_INITIALIZATION_STATUS,
+ BufferInitTracker::new(aligned_size),
+ ),
+ sync_mapped_writes: Mutex::new(rank::BUFFER_SYNC_MAPPED_WRITES, None),
+ map_state: Mutex::new(rank::BUFFER_MAP_STATE, resource::BufferMapState::Idle),
info: ResourceInfo::new(
desc.label.borrow_or_default(),
Some(self.tracker_indices.buffers.clone()),
),
- bind_groups: Mutex::new(Vec::new()),
+ bind_groups: Mutex::new(rank::BUFFER_BIND_GROUPS, Vec::new()),
})
}
@@ -680,10 +681,10 @@ impl<A: HalApi> Device<A> {
desc: desc.map_label(|_| ()),
hal_usage,
format_features,
- initialization_status: RwLock::new(TextureInitTracker::new(
- desc.mip_level_count,
- desc.array_layer_count(),
- )),
+ initialization_status: RwLock::new(
+ rank::TEXTURE_INITIALIZATION_STATUS,
+ TextureInitTracker::new(desc.mip_level_count, desc.array_layer_count()),
+ ),
full_range: TextureSelector {
mips: 0..desc.mip_level_count,
layers: 0..desc.array_layer_count(),
@@ -692,9 +693,9 @@ impl<A: HalApi> Device<A> {
desc.label.borrow_or_default(),
Some(self.tracker_indices.textures.clone()),
),
- clear_mode: RwLock::new(clear_mode),
- views: Mutex::new(Vec::new()),
- bind_groups: Mutex::new(Vec::new()),
+ clear_mode: RwLock::new(rank::TEXTURE_CLEAR_MODE, clear_mode),
+ views: Mutex::new(rank::TEXTURE_VIEWS, Vec::new()),
+ bind_groups: Mutex::new(rank::TEXTURE_BIND_GROUPS, Vec::new()),
}
}
@@ -710,14 +711,17 @@ impl<A: HalApi> Device<A> {
device: self.clone(),
usage: desc.usage,
size: desc.size,
- initialization_status: RwLock::new(BufferInitTracker::new(0)),
- sync_mapped_writes: Mutex::new(None),
- map_state: Mutex::new(resource::BufferMapState::Idle),
+ initialization_status: RwLock::new(
+ rank::BUFFER_INITIALIZATION_STATUS,
+ BufferInitTracker::new(0),
+ ),
+ sync_mapped_writes: Mutex::new(rank::BUFFER_SYNC_MAPPED_WRITES, None),
+ map_state: Mutex::new(rank::BUFFER_MAP_STATE, resource::BufferMapState::Idle),
info: ResourceInfo::new(
desc.label.borrow_or_default(),
Some(self.tracker_indices.buffers.clone()),
),
- bind_groups: Mutex::new(Vec::new()),
+ bind_groups: Mutex::new(rank::BUFFER_BIND_GROUPS, Vec::new()),
}
}
@@ -1421,7 +1425,7 @@ impl<A: HalApi> Device<A> {
pipeline::ShaderModuleSource::Wgsl(code) => {
profiling::scope!("naga::front::wgsl::parse_str");
let module = naga::front::wgsl::parse_str(&code).map_err(|inner| {
- pipeline::CreateShaderModuleError::Parsing(pipeline::ShaderError {
+ pipeline::CreateShaderModuleError::Parsing(naga::error::ShaderError {
source: code.to_string(),
label: desc.label.as_ref().map(|l| l.to_string()),
inner: Box::new(inner),
@@ -1434,7 +1438,7 @@ impl<A: HalApi> Device<A> {
let parser = naga::front::spv::Frontend::new(spv.iter().cloned(), &options);
profiling::scope!("naga::front::spv::Frontend");
let module = parser.parse().map_err(|inner| {
- pipeline::CreateShaderModuleError::ParsingSpirV(pipeline::ShaderError {
+ pipeline::CreateShaderModuleError::ParsingSpirV(naga::error::ShaderError {
source: String::new(),
label: desc.label.as_ref().map(|l| l.to_string()),
inner: Box::new(inner),
@@ -1447,7 +1451,7 @@ impl<A: HalApi> Device<A> {
let mut parser = naga::front::glsl::Frontend::default();
profiling::scope!("naga::front::glsl::Frontend.parse");
let module = parser.parse(&options, &code).map_err(|inner| {
- pipeline::CreateShaderModuleError::ParsingGlsl(pipeline::ShaderError {
+ pipeline::CreateShaderModuleError::ParsingGlsl(naga::error::ShaderError {
source: code.to_string(),
label: desc.label.as_ref().map(|l| l.to_string()),
inner: Box::new(inner),
@@ -1471,9 +1475,78 @@ impl<A: HalApi> Device<A> {
};
}
- use naga::valid::Capabilities as Caps;
profiling::scope!("naga::validate");
+ let debug_source =
+ if self.instance_flags.contains(wgt::InstanceFlags::DEBUG) && !source.is_empty() {
+ Some(hal::DebugSource {
+ file_name: Cow::Owned(
+ desc.label
+ .as_ref()
+ .map_or("shader".to_string(), |l| l.to_string()),
+ ),
+ source_code: Cow::Owned(source.clone()),
+ })
+ } else {
+ None
+ };
+
+ let info = self
+ .create_validator(naga::valid::ValidationFlags::all())
+ .validate(&module)
+ .map_err(|inner| {
+ pipeline::CreateShaderModuleError::Validation(naga::error::ShaderError {
+ source,
+ label: desc.label.as_ref().map(|l| l.to_string()),
+ inner: Box::new(inner),
+ })
+ })?;
+
+ let interface =
+ validation::Interface::new(&module, &info, self.limits.clone(), self.features);
+ let hal_shader = hal::ShaderInput::Naga(hal::NagaShader {
+ module,
+ info,
+ debug_source,
+ });
+ let hal_desc = hal::ShaderModuleDescriptor {
+ label: desc.label.to_hal(self.instance_flags),
+ runtime_checks: desc.shader_bound_checks.runtime_checks(),
+ };
+ let raw = match unsafe {
+ self.raw
+ .as_ref()
+ .unwrap()
+ .create_shader_module(&hal_desc, hal_shader)
+ } {
+ Ok(raw) => raw,
+ Err(error) => {
+ return Err(match error {
+ hal::ShaderError::Device(error) => {
+ pipeline::CreateShaderModuleError::Device(error.into())
+ }
+ hal::ShaderError::Compilation(ref msg) => {
+ log::error!("Shader error: {}", msg);
+ pipeline::CreateShaderModuleError::Generation
+ }
+ })
+ }
+ };
+
+ Ok(pipeline::ShaderModule {
+ raw: Some(raw),
+ device: self.clone(),
+ interface: Some(interface),
+ info: ResourceInfo::new(desc.label.borrow_or_default(), None),
+ label: desc.label.borrow_or_default().to_string(),
+ })
+ }
+ /// Create a validator with the given validation flags.
+ pub fn create_validator(
+ self: &Arc<Self>,
+ flags: naga::valid::ValidationFlags,
+ ) -> naga::valid::Validator {
+ use naga::valid::Capabilities as Caps;
let mut caps = Caps::empty();
caps.set(
Caps::PUSH_CONSTANT,
@@ -1541,69 +1614,36 @@ impl<A: HalApi> Device<A> {
.flags
.contains(wgt::DownlevelFlags::CUBE_ARRAY_TEXTURES),
);
+ caps.set(
+ Caps::SUBGROUP,
+ self.features
+ .intersects(wgt::Features::SUBGROUP | wgt::Features::SUBGROUP_VERTEX),
+ );
+ caps.set(
+ Caps::SUBGROUP_BARRIER,
+ self.features.intersects(wgt::Features::SUBGROUP_BARRIER),
+ );
- let debug_source =
- if self.instance_flags.contains(wgt::InstanceFlags::DEBUG) && !source.is_empty() {
- Some(hal::DebugSource {
- file_name: Cow::Owned(
- desc.label
- .as_ref()
- .map_or("shader".to_string(), |l| l.to_string()),
- ),
- source_code: Cow::Owned(source.clone()),
- })
- } else {
- None
- };
-
- let info = naga::valid::Validator::new(naga::valid::ValidationFlags::all(), caps)
- .validate(&module)
- .map_err(|inner| {
- pipeline::CreateShaderModuleError::Validation(pipeline::ShaderError {
- source,
- label: desc.label.as_ref().map(|l| l.to_string()),
- inner: Box::new(inner),
- })
- })?;
+ let mut subgroup_stages = naga::valid::ShaderStages::empty();
+ subgroup_stages.set(
+ naga::valid::ShaderStages::COMPUTE | naga::valid::ShaderStages::FRAGMENT,
+ self.features.contains(wgt::Features::SUBGROUP),
+ );
+ subgroup_stages.set(
+ naga::valid::ShaderStages::VERTEX,
+ self.features.contains(wgt::Features::SUBGROUP_VERTEX),
+ );
- let interface =
- validation::Interface::new(&module, &info, self.limits.clone(), self.features);
- let hal_shader = hal::ShaderInput::Naga(hal::NagaShader {
- module,
- info,
- debug_source,
- });
- let hal_desc = hal::ShaderModuleDescriptor {
- label: desc.label.to_hal(self.instance_flags),
- runtime_checks: desc.shader_bound_checks.runtime_checks(),
- };
- let raw = match unsafe {
- self.raw
- .as_ref()
- .unwrap()
- .create_shader_module(&hal_desc, hal_shader)
- } {
- Ok(raw) => raw,
- Err(error) => {
- return Err(match error {
- hal::ShaderError::Device(error) => {
- pipeline::CreateShaderModuleError::Device(error.into())
- }
- hal::ShaderError::Compilation(ref msg) => {
- log::error!("Shader error: {}", msg);
- pipeline::CreateShaderModuleError::Generation
- }
- })
- }
+ let subgroup_operations = if caps.contains(Caps::SUBGROUP) {
+ use naga::valid::SubgroupOperationSet as S;
+ S::BASIC | S::VOTE | S::ARITHMETIC | S::BALLOT | S::SHUFFLE | S::SHUFFLE_RELATIVE
+ } else {
+ naga::valid::SubgroupOperationSet::empty()
};
-
- Ok(pipeline::ShaderModule {
- raw: Some(raw),
- device: self.clone(),
- interface: Some(interface),
- info: ResourceInfo::new(desc.label.borrow_or_default(), None),
- label: desc.label.borrow_or_default().to_string(),
- })
+ let mut validator = naga::valid::Validator::new(flags, caps);
+ validator.subgroup_stages(subgroup_stages);
+ validator.subgroup_operations(subgroup_operations);
+ validator
}
#[allow(unused_unsafe)]
@@ -1913,6 +1953,7 @@ impl<A: HalApi> Device<A> {
used: &mut BindGroupStates<A>,
storage: &'a Storage<Buffer<A>>,
limits: &wgt::Limits,
+ device_id: id::Id<id::markers::Device>,
snatch_guard: &'a SnatchGuard<'a>,
) -> Result<hal::BufferBinding<'a, A>, binding_model::CreateBindGroupError> {
use crate::binding_model::CreateBindGroupError as Error;
@@ -1931,6 +1972,7 @@ impl<A: HalApi> Device<A> {
})
}
};
+
let (pub_usage, internal_use, range_limit) = match binding_ty {
wgt::BufferBindingType::Uniform => (
wgt::BufferUsages::UNIFORM,
@@ -1963,6 +2005,10 @@ impl<A: HalApi> Device<A> {
.add_single(storage, bb.buffer_id, internal_use)
.ok_or(Error::InvalidBuffer(bb.buffer_id))?;
+ if buffer.device.as_info().id() != device_id {
+ return Err(DeviceError::WrongDevice.into());
+ }
+
check_buffer_usage(bb.buffer_id, buffer.usage, pub_usage)?;
let raw_buffer = buffer
.raw
@@ -2041,13 +2087,53 @@ impl<A: HalApi> Device<A> {
})
}
- pub(crate) fn create_texture_binding(
- view: &TextureView<A>,
- internal_use: hal::TextureUses,
- pub_usage: wgt::TextureUsages,
+ fn create_sampler_binding<'a>(
+ used: &BindGroupStates<A>,
+ storage: &'a Storage<Sampler<A>>,
+ id: id::Id<id::markers::Sampler>,
+ device_id: id::Id<id::markers::Device>,
+ ) -> Result<&'a Sampler<A>, binding_model::CreateBindGroupError> {
+ use crate::binding_model::CreateBindGroupError as Error;
+
+ let sampler = used
+ .samplers
+ .add_single(storage, id)
+ .ok_or(Error::InvalidSampler(id))?;
+
+ if sampler.device.as_info().id() != device_id {
+ return Err(DeviceError::WrongDevice.into());
+ }
+
+ Ok(sampler)
+ }
+
+ pub(crate) fn create_texture_binding<'a>(
+ self: &Arc<Self>,
+ binding: u32,
+ decl: &wgt::BindGroupLayoutEntry,
+ storage: &'a Storage<TextureView<A>>,
+ id: id::Id<id::markers::TextureView>,
used: &mut BindGroupStates<A>,
used_texture_ranges: &mut Vec<TextureInitTrackerAction<A>>,
- ) -> Result<(), binding_model::CreateBindGroupError> {
+ snatch_guard: &'a SnatchGuard<'a>,
+ ) -> Result<hal::TextureBinding<'a, A>, binding_model::CreateBindGroupError> {
+ use crate::binding_model::CreateBindGroupError as Error;
+
+ let view = used
+ .views
+ .add_single(storage, id)
+ .ok_or(Error::InvalidTextureView(id))?;
+
+ if view.device.as_info().id() != self.as_info().id() {
+ return Err(DeviceError::WrongDevice.into());
+ }
+
+ let (pub_usage, internal_use) = self.texture_use_parameters(
+ binding,
+ decl,
+ view,
+ "SampledTexture, ReadonlyStorageTexture or WriteonlyStorageTexture",
+ )?;
let texture = &view.parent;
let texture_id = texture.as_info().id();
// Careful here: the texture may no longer have its own ref count,
@@ -2077,7 +2163,12 @@ impl<A: HalApi> Device<A> {
kind: MemoryInitKind::NeedsInitializedMemory,
});
- Ok(())
+ Ok(hal::TextureBinding {
+ view: view
+ .raw(snatch_guard)
+ .ok_or(Error::InvalidTextureView(id))?,
+ usage: internal_use,
+ })
}
// This function expects the provided bind group layout to be resolved
@@ -2139,6 +2230,7 @@ impl<A: HalApi> Device<A> {
&mut used,
&*buffer_guard,
&self.limits,
+ self.as_info().id(),
&snatch_guard,
)?;
@@ -2162,105 +2254,86 @@ impl<A: HalApi> Device<A> {
&mut used,
&*buffer_guard,
&self.limits,
+ self.as_info().id(),
&snatch_guard,
)?;
hal_buffers.push(bb);
}
(res_index, num_bindings)
}
- Br::Sampler(id) => {
- match decl.ty {
- wgt::BindingType::Sampler(ty) => {
- let sampler = used
- .samplers
- .add_single(&*sampler_guard, id)
- .ok_or(Error::InvalidSampler(id))?;
-
- if sampler.device.as_info().id() != self.as_info().id() {
- return Err(DeviceError::WrongDevice.into());
- }
-
- // Allowed sampler values for filtering and comparison
- let (allowed_filtering, allowed_comparison) = match ty {
- wgt::SamplerBindingType::Filtering => (None, false),
- wgt::SamplerBindingType::NonFiltering => (Some(false), false),
- wgt::SamplerBindingType::Comparison => (None, true),
- };
-
- if let Some(allowed_filtering) = allowed_filtering {
- if allowed_filtering != sampler.filtering {
- return Err(Error::WrongSamplerFiltering {
- binding,
- layout_flt: allowed_filtering,
- sampler_flt: sampler.filtering,
- });
- }
- }
+ Br::Sampler(id) => match decl.ty {
+ wgt::BindingType::Sampler(ty) => {
+ let sampler = Self::create_sampler_binding(
+ &used,
+ &sampler_guard,
+ id,
+ self.as_info().id(),
+ )?;
- if allowed_comparison != sampler.comparison {
- return Err(Error::WrongSamplerComparison {
+ let (allowed_filtering, allowed_comparison) = match ty {
+ wgt::SamplerBindingType::Filtering => (None, false),
+ wgt::SamplerBindingType::NonFiltering => (Some(false), false),
+ wgt::SamplerBindingType::Comparison => (None, true),
+ };
+ if let Some(allowed_filtering) = allowed_filtering {
+ if allowed_filtering != sampler.filtering {
+ return Err(Error::WrongSamplerFiltering {
binding,
- layout_cmp: allowed_comparison,
- sampler_cmp: sampler.comparison,
+ layout_flt: allowed_filtering,
+ sampler_flt: sampler.filtering,
});
}
-
- let res_index = hal_samplers.len();
- hal_samplers.push(sampler.raw());
- (res_index, 1)
}
- _ => {
- return Err(Error::WrongBindingType {
+ if allowed_comparison != sampler.comparison {
+ return Err(Error::WrongSamplerComparison {
binding,
- actual: decl.ty,
- expected: "Sampler",
- })
+ layout_cmp: allowed_comparison,
+ sampler_cmp: sampler.comparison,
+ });
}
+
+ let res_index = hal_samplers.len();
+ hal_samplers.push(sampler.raw());
+ (res_index, 1)
}
- }
+ _ => {
+ return Err(Error::WrongBindingType {
+ binding,
+ actual: decl.ty,
+ expected: "Sampler",
+ })
+ }
+ },
Br::SamplerArray(ref bindings_array) => {
let num_bindings = bindings_array.len();
Self::check_array_binding(self.features, decl.count, num_bindings)?;
let res_index = hal_samplers.len();
for &id in bindings_array.iter() {
- let sampler = used
- .samplers
- .add_single(&*sampler_guard, id)
- .ok_or(Error::InvalidSampler(id))?;
- if sampler.device.as_info().id() != self.as_info().id() {
- return Err(DeviceError::WrongDevice.into());
- }
+ let sampler = Self::create_sampler_binding(
+ &used,
+ &sampler_guard,
+ id,
+ self.as_info().id(),
+ )?;
+
hal_samplers.push(sampler.raw());
}
(res_index, num_bindings)
}
Br::TextureView(id) => {
- let view = used
- .views
- .add_single(&*texture_view_guard, id)
- .ok_or(Error::InvalidTextureView(id))?;
- let (pub_usage, internal_use) = self.texture_use_parameters(
+ let tb = self.create_texture_binding(
binding,
decl,
- view,
- "SampledTexture, ReadonlyStorageTexture or WriteonlyStorageTexture",
- )?;
- Self::create_texture_binding(
- view,
- internal_use,
- pub_usage,
+ &texture_view_guard,
+ id,
&mut used,
&mut used_texture_ranges,
+ &snatch_guard,
)?;
let res_index = hal_textures.len();
- hal_textures.push(hal::TextureBinding {
- view: view
- .raw(&snatch_guard)
- .ok_or(Error::InvalidTextureView(id))?,
- usage: internal_use,
- });
+ hal_textures.push(tb);
(res_index, 1)
}
Br::TextureViewArray(ref bindings_array) => {
@@ -2269,26 +2342,17 @@ impl<A: HalApi> Device<A> {
let res_index = hal_textures.len();
for &id in bindings_array.iter() {
- let view = used
- .views
- .add_single(&*texture_view_guard, id)
- .ok_or(Error::InvalidTextureView(id))?;
- let (pub_usage, internal_use) =
- self.texture_use_parameters(binding, decl, view,
- "SampledTextureArray, ReadonlyStorageTextureArray or WriteonlyStorageTextureArray")?;
- Self::create_texture_binding(
- view,
- internal_use,
- pub_usage,
+ let tb = self.create_texture_binding(
+ binding,
+ decl,
+ &texture_view_guard,
+ id,
&mut used,
&mut used_texture_ranges,
+ &snatch_guard,
)?;
- hal_textures.push(hal::TextureBinding {
- view: view
- .raw(&snatch_guard)
- .ok_or(Error::InvalidTextureView(id))?,
- usage: internal_use,
- });
+
+ hal_textures.push(tb);
}
(res_index, num_bindings)
@@ -2762,8 +2826,10 @@ impl<A: HalApi> Device<A> {
label: desc.label.to_hal(self.instance_flags),
layout: pipeline_layout.raw(),
stage: hal::ProgrammableStage {
- entry_point: final_entry_point_name.as_ref(),
module: shader_module.raw(),
+ entry_point: final_entry_point_name.as_ref(),
+ constants: desc.stage.constants.as_ref(),
+ zero_initialize_workgroup_memory: desc.stage.zero_initialize_workgroup_memory,
},
};
@@ -3178,6 +3244,8 @@ impl<A: HalApi> Device<A> {
hal::ProgrammableStage {
module: vertex_shader_module.raw(),
entry_point: &vertex_entry_point_name,
+ constants: stage_desc.constants.as_ref(),
+ zero_initialize_workgroup_memory: stage_desc.zero_initialize_workgroup_memory,
}
};
@@ -3237,6 +3305,10 @@ impl<A: HalApi> Device<A> {
Some(hal::ProgrammableStage {
module: shader_module.raw(),
entry_point: &fragment_entry_point_name,
+ constants: fragment_state.stage.constants.as_ref(),
+ zero_initialize_workgroup_memory: fragment_state
+ .stage
+ .zero_initialize_workgroup_memory,
})
}
None => None,
@@ -3482,10 +3554,9 @@ impl<A: HalApi> Device<A> {
.map_err(DeviceError::from)?
};
drop(guard);
- let closures = self.lock_life().triage_submissions(
- submission_index,
- self.command_allocator.lock().as_mut().unwrap(),
- );
+ let closures = self
+ .lock_life()
+ .triage_submissions(submission_index, &self.command_allocator);
assert!(
closures.is_empty(),
"wait_for_submit is not expected to work with closures"
@@ -3613,10 +3684,7 @@ impl<A: HalApi> Device<A> {
log::error!("failed to wait for the device: {error}");
}
let mut life_tracker = self.lock_life();
- let _ = life_tracker.triage_submissions(
- current_index,
- self.command_allocator.lock().as_mut().unwrap(),
- );
+ let _ = life_tracker.triage_submissions(current_index, &self.command_allocator);
if let Some(device_lost_closure) = life_tracker.device_lost_closure.take() {
// It's important to not hold the lock while calling the closure.
drop(life_tracker);