mirror of
https://github.com/gfx-rs/wgpu.git
synced 2024-11-22 06:44:14 +00:00
[core] Use internal locking in CommandAllocator
.
Move the `Mutex` in `Device::command_allocator` inside the `CommandAllocator` type itself, allowing it to be passed by shared reference instead of mutable reference. Passing `CommandAllocator` to functions like `PendingWrites::post_submit` by mutable reference requires the caller to acquire and hold the mutex for the entire time the callee runs, but `CommandAllocator` is just a recycling pool, with very simple invariants; there's no reason to hold the lock for a long time.
This commit is contained in:
parent
b9781ee6e2
commit
bab6f53e86
@ -2,6 +2,8 @@ use crate::hal_api::HalApi;
|
||||
use crate::resource_log;
|
||||
use hal::Device as _;
|
||||
|
||||
use parking_lot::Mutex;
|
||||
|
||||
/// A pool of free [`wgpu_hal::CommandEncoder`]s, owned by a `Device`.
|
||||
///
|
||||
/// Each encoder in this list is in the "closed" state.
|
||||
@ -12,13 +14,13 @@ use hal::Device as _;
|
||||
/// [ce]: wgpu_hal::CommandEncoder
|
||||
/// [cb]: wgpu_hal::Api::CommandBuffer
|
||||
pub(crate) struct CommandAllocator<A: HalApi> {
|
||||
free_encoders: Vec<A::CommandEncoder>,
|
||||
free_encoders: Mutex<Vec<A::CommandEncoder>>,
|
||||
}
|
||||
|
||||
impl<A: HalApi> CommandAllocator<A> {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self {
|
||||
free_encoders: Vec::new(),
|
||||
free_encoders: Mutex::new(Vec::new()),
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,11 +29,12 @@ impl<A: HalApi> CommandAllocator<A> {
|
||||
/// If we have free encoders in the pool, take one of those. Otherwise,
|
||||
/// create a new one on `device`.
|
||||
pub(crate) fn acquire_encoder(
|
||||
&mut self,
|
||||
&self,
|
||||
device: &A::Device,
|
||||
queue: &A::Queue,
|
||||
) -> Result<A::CommandEncoder, hal::DeviceError> {
|
||||
match self.free_encoders.pop() {
|
||||
let mut free_encoders = self.free_encoders.lock();
|
||||
match free_encoders.pop() {
|
||||
Some(encoder) => Ok(encoder),
|
||||
None => unsafe {
|
||||
let hal_desc = hal::CommandEncoderDescriptor { label: None, queue };
|
||||
@ -41,19 +44,18 @@ impl<A: HalApi> CommandAllocator<A> {
|
||||
}
|
||||
|
||||
/// Add `encoder` back to the free pool.
|
||||
pub(crate) fn release_encoder(&mut self, encoder: A::CommandEncoder) {
|
||||
self.free_encoders.push(encoder);
|
||||
pub(crate) fn release_encoder(&self, encoder: A::CommandEncoder) {
|
||||
let mut free_encoders = self.free_encoders.lock();
|
||||
free_encoders.push(encoder);
|
||||
}
|
||||
|
||||
/// Free the pool of command encoders.
|
||||
///
|
||||
/// This is only called when the `Device` is dropped.
|
||||
pub(crate) fn dispose(self, device: &A::Device) {
|
||||
resource_log!(
|
||||
"CommandAllocator::dispose encoders {}",
|
||||
self.free_encoders.len()
|
||||
);
|
||||
for cmd_encoder in self.free_encoders {
|
||||
pub(crate) fn dispose(&self, device: &A::Device) {
|
||||
let mut free_encoders = self.free_encoders.lock();
|
||||
resource_log!("CommandAllocator::dispose encoders {}", free_encoders.len());
|
||||
for cmd_encoder in free_encoders.drain(..) {
|
||||
unsafe {
|
||||
device.destroy_command_encoder(cmd_encoder);
|
||||
}
|
||||
|
@ -1351,9 +1351,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,
|
||||
|
@ -361,7 +361,7 @@ impl<A: HalApi> LifetimeTracker<A> {
|
||||
pub fn triage_submissions(
|
||||
&mut self,
|
||||
last_done: SubmissionIndex,
|
||||
command_allocator: &mut crate::command::CommandAllocator<A>,
|
||||
command_allocator: &crate::command::CommandAllocator<A>,
|
||||
) -> SmallVec<[SubmittedWorkDoneClosure; 1]> {
|
||||
profiling::scope!("triage_submissions");
|
||||
|
||||
|
@ -258,7 +258,7 @@ impl<A: HalApi> PendingWrites<A> {
|
||||
#[must_use]
|
||||
fn post_submit(
|
||||
&mut self,
|
||||
command_allocator: &mut CommandAllocator<A>,
|
||||
command_allocator: &CommandAllocator<A>,
|
||||
device: &A::Device,
|
||||
queue: &A::Queue,
|
||||
) -> Option<EncoderInFlight<A>> {
|
||||
@ -1530,7 +1530,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(),
|
||||
) {
|
||||
|
@ -97,7 +97,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<command::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
|
||||
@ -165,7 +165,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,7 +223,7 @@ impl<A: HalApi> Device<A> {
|
||||
let fence =
|
||||
unsafe { raw_device.create_fence() }.map_err(|_| CreateDeviceError::OutOfMemory)?;
|
||||
|
||||
let mut com_alloc = command::CommandAllocator::new();
|
||||
let com_alloc = command::CommandAllocator::new();
|
||||
let pending_encoder = com_alloc
|
||||
.acquire_encoder(&raw_device, raw_queue)
|
||||
.map_err(|_| CreateDeviceError::OutOfMemory)?;
|
||||
@ -269,7 +269,7 @@ 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: com_alloc,
|
||||
active_submission_index: AtomicU64::new(0),
|
||||
fence: RwLock::new(Some(fence)),
|
||||
snatchable_lock: unsafe { SnatchLock::new() },
|
||||
@ -423,10 +423,8 @@ 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(),
|
||||
);
|
||||
let submission_closures =
|
||||
life_tracker.triage_submissions(last_done_index, &self.command_allocator);
|
||||
|
||||
{
|
||||
// Normally, `temp_suspected` exists only to save heap
|
||||
@ -3483,10 +3481,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"
|
||||
@ -3614,10 +3611,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);
|
||||
|
Loading…
Reference in New Issue
Block a user