mirror of
https://github.com/gfx-rs/wgpu.git
synced 2024-11-25 00:03:29 +00:00
[wgpu-core] Return submission index for map_async
and on_submitted_work_done
to track down completion of async callbacks (#6360)
This commit is contained in:
parent
f3db183f1b
commit
6a75a73300
@ -47,6 +47,10 @@ Bottom level categories:
|
|||||||
- Parse `diagnostic(…)` directives, but don't implement any triggering rules yet. By @ErichDonGubler in [#6456](https://github.com/gfx-rs/wgpu/pull/6456).
|
- Parse `diagnostic(…)` directives, but don't implement any triggering rules yet. By @ErichDonGubler in [#6456](https://github.com/gfx-rs/wgpu/pull/6456).
|
||||||
- Fix an issue where `naga` CLI would incorrectly skip the first positional argument when `--stdin-file-path` was specified. By @ErichDonGubler in [#6480](https://github.com/gfx-rs/wgpu/pull/6480).
|
- Fix an issue where `naga` CLI would incorrectly skip the first positional argument when `--stdin-file-path` was specified. By @ErichDonGubler in [#6480](https://github.com/gfx-rs/wgpu/pull/6480).
|
||||||
|
|
||||||
|
#### General
|
||||||
|
|
||||||
|
- Return submission index in `map_async` and `on_submitted_work_done` to track down completion of async callbacks. By @eliemichel in [#6360](https://github.com/gfx-rs/wgpu/pull/6360).
|
||||||
|
|
||||||
### Changes
|
### Changes
|
||||||
|
|
||||||
#### HAL
|
#### HAL
|
||||||
|
@ -2147,33 +2147,27 @@ impl Global {
|
|||||||
offset: BufferAddress,
|
offset: BufferAddress,
|
||||||
size: Option<BufferAddress>,
|
size: Option<BufferAddress>,
|
||||||
op: BufferMapOperation,
|
op: BufferMapOperation,
|
||||||
) -> BufferAccessResult {
|
) -> Result<crate::SubmissionIndex, BufferAccessError> {
|
||||||
profiling::scope!("Buffer::map_async");
|
profiling::scope!("Buffer::map_async");
|
||||||
api_log!("Buffer::map_async {buffer_id:?} offset {offset:?} size {size:?} op: {op:?}");
|
api_log!("Buffer::map_async {buffer_id:?} offset {offset:?} size {size:?} op: {op:?}");
|
||||||
|
|
||||||
let hub = &self.hub;
|
let hub = &self.hub;
|
||||||
|
|
||||||
let op_and_err = 'error: {
|
let map_result = match hub.buffers.get(buffer_id).get() {
|
||||||
let buffer = match hub.buffers.get(buffer_id).get() {
|
Ok(buffer) => buffer.map_async(offset, size, op),
|
||||||
Ok(buffer) => buffer,
|
Err(e) => Err((op, e.into())),
|
||||||
Err(e) => break 'error Some((op, e.into())),
|
|
||||||
};
|
|
||||||
|
|
||||||
buffer.map_async(offset, size, op).err()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// User callbacks must not be called while holding `buffer.map_async`'s locks, so we
|
match map_result {
|
||||||
// defer the error callback if it needs to be called immediately (typically when running
|
Ok(submission_index) => Ok(submission_index),
|
||||||
// into errors).
|
Err((mut operation, err)) => {
|
||||||
if let Some((mut operation, err)) = op_and_err {
|
if let Some(callback) = operation.callback.take() {
|
||||||
if let Some(callback) = operation.callback.take() {
|
callback.call(Err(err.clone()));
|
||||||
callback.call(Err(err.clone()));
|
}
|
||||||
|
log::error!("Buffer::map_async error: {err}");
|
||||||
|
Err(err)
|
||||||
}
|
}
|
||||||
log::error!("Buffer::map_async error: {err}");
|
|
||||||
return Err(err);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn buffer_get_mapped_range(
|
pub fn buffer_get_mapped_range(
|
||||||
|
@ -126,35 +126,20 @@ pub enum WaitIdleError {
|
|||||||
/// - Each buffer's `ResourceInfo::submission_index` records the index of the
|
/// - Each buffer's `ResourceInfo::submission_index` records the index of the
|
||||||
/// most recent queue submission that uses that buffer.
|
/// most recent queue submission that uses that buffer.
|
||||||
///
|
///
|
||||||
/// - Calling `Global::buffer_map_async` adds the buffer to
|
|
||||||
/// `self.mapped`, and changes `Buffer::map_state` to prevent it
|
|
||||||
/// from being used in any new submissions.
|
|
||||||
///
|
|
||||||
/// - When the device is polled, the following `LifetimeTracker` methods decide
|
/// - When the device is polled, the following `LifetimeTracker` methods decide
|
||||||
/// what should happen next:
|
/// what should happen next:
|
||||||
///
|
///
|
||||||
/// 1) `triage_mapped` drains `self.mapped`, checking the submission index
|
/// 1) `triage_submissions` moves entries in `self.active[i]` for completed
|
||||||
/// of each buffer against the queue submissions that have finished
|
|
||||||
/// execution. Buffers used by submissions still in flight go in
|
|
||||||
/// `self.active[index].mapped`, and the rest go into
|
|
||||||
/// `self.ready_to_map`.
|
|
||||||
///
|
|
||||||
/// 2) `triage_submissions` moves entries in `self.active[i]` for completed
|
|
||||||
/// submissions to `self.ready_to_map`. At this point, both
|
/// submissions to `self.ready_to_map`. At this point, both
|
||||||
/// `self.active` and `self.ready_to_map` are up to date with the given
|
/// `self.active` and `self.ready_to_map` are up to date with the given
|
||||||
/// submission index.
|
/// submission index.
|
||||||
///
|
///
|
||||||
/// 3) `handle_mapping` drains `self.ready_to_map` and actually maps the
|
/// 2) `handle_mapping` drains `self.ready_to_map` and actually maps the
|
||||||
/// buffers, collecting a list of notification closures to call.
|
/// buffers, collecting a list of notification closures to call.
|
||||||
///
|
///
|
||||||
/// Only calling `Global::buffer_map_async` clones a new `Arc` for the
|
/// Only calling `Global::buffer_map_async` clones a new `Arc` for the
|
||||||
/// buffer. This new `Arc` is only dropped by `handle_mapping`.
|
/// buffer. This new `Arc` is only dropped by `handle_mapping`.
|
||||||
pub(crate) struct LifetimeTracker {
|
pub(crate) struct LifetimeTracker {
|
||||||
/// Buffers for which a call to [`Buffer::map_async`] has succeeded, but
|
|
||||||
/// which haven't been examined by `triage_mapped` yet to decide when they
|
|
||||||
/// can be mapped.
|
|
||||||
mapped: Vec<Arc<Buffer>>,
|
|
||||||
|
|
||||||
/// Resources used by queue submissions still in flight. One entry per
|
/// Resources used by queue submissions still in flight. One entry per
|
||||||
/// submission, with older submissions appearing before younger.
|
/// submission, with older submissions appearing before younger.
|
||||||
///
|
///
|
||||||
@ -182,7 +167,6 @@ pub(crate) struct LifetimeTracker {
|
|||||||
impl LifetimeTracker {
|
impl LifetimeTracker {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
mapped: Vec::new(),
|
|
||||||
active: Vec::new(),
|
active: Vec::new(),
|
||||||
ready_to_map: Vec::new(),
|
ready_to_map: Vec::new(),
|
||||||
work_done_closures: SmallVec::new(),
|
work_done_closures: SmallVec::new(),
|
||||||
@ -211,8 +195,21 @@ impl LifetimeTracker {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn map(&mut self, value: &Arc<Buffer>) {
|
pub(crate) fn map(&mut self, buffer: &Arc<Buffer>) -> Option<SubmissionIndex> {
|
||||||
self.mapped.push(value.clone());
|
// Determine which buffers are ready to map, and which must wait for the GPU.
|
||||||
|
let submission = self
|
||||||
|
.active
|
||||||
|
.iter_mut()
|
||||||
|
.rev()
|
||||||
|
.find(|a| a.contains_buffer(buffer));
|
||||||
|
|
||||||
|
let maybe_submission_index = submission.as_ref().map(|s| s.index);
|
||||||
|
|
||||||
|
submission
|
||||||
|
.map_or(&mut self.ready_to_map, |a| &mut a.mapped)
|
||||||
|
.push(buffer.clone());
|
||||||
|
|
||||||
|
maybe_submission_index
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the submission index of the most recent submission that uses the
|
/// Returns the submission index of the most recent submission that uses the
|
||||||
@ -304,41 +301,24 @@ impl LifetimeTracker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_work_done_closure(&mut self, closure: SubmittedWorkDoneClosure) {
|
pub fn add_work_done_closure(
|
||||||
|
&mut self,
|
||||||
|
closure: SubmittedWorkDoneClosure,
|
||||||
|
) -> Option<SubmissionIndex> {
|
||||||
match self.active.last_mut() {
|
match self.active.last_mut() {
|
||||||
Some(active) => {
|
Some(active) => {
|
||||||
active.work_done_closures.push(closure);
|
active.work_done_closures.push(closure);
|
||||||
|
Some(active.index)
|
||||||
}
|
}
|
||||||
// We must defer the closure until all previously occurring map_async closures
|
// We must defer the closure until all previously occurring map_async closures
|
||||||
// have fired. This is required by the spec.
|
// have fired. This is required by the spec.
|
||||||
None => {
|
None => {
|
||||||
self.work_done_closures.push(closure);
|
self.work_done_closures.push(closure);
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determine which buffers are ready to map, and which must wait for the
|
|
||||||
/// GPU.
|
|
||||||
///
|
|
||||||
/// See the documentation for [`LifetimeTracker`] for details.
|
|
||||||
pub(crate) fn triage_mapped(&mut self) {
|
|
||||||
if self.mapped.is_empty() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for buffer in self.mapped.drain(..) {
|
|
||||||
let submission = self
|
|
||||||
.active
|
|
||||||
.iter_mut()
|
|
||||||
.rev()
|
|
||||||
.find(|a| a.contains_buffer(&buffer));
|
|
||||||
|
|
||||||
submission
|
|
||||||
.map_or(&mut self.ready_to_map, |a| &mut a.mapped)
|
|
||||||
.push(buffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Map the buffers in `self.ready_to_map`.
|
/// Map the buffers in `self.ready_to_map`.
|
||||||
///
|
///
|
||||||
/// Return a list of mapping notifications to send.
|
/// Return a list of mapping notifications to send.
|
||||||
|
@ -1256,10 +1256,13 @@ impl Queue {
|
|||||||
unsafe { self.raw().get_timestamp_period() }
|
unsafe { self.raw().get_timestamp_period() }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_submitted_work_done(&self, closure: SubmittedWorkDoneClosure) {
|
pub fn on_submitted_work_done(
|
||||||
|
&self,
|
||||||
|
closure: SubmittedWorkDoneClosure,
|
||||||
|
) -> Option<SubmissionIndex> {
|
||||||
api_log!("Queue::on_submitted_work_done");
|
api_log!("Queue::on_submitted_work_done");
|
||||||
//TODO: flush pending writes
|
//TODO: flush pending writes
|
||||||
self.device.lock_life().add_work_done_closure(closure);
|
self.device.lock_life().add_work_done_closure(closure)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1402,9 +1405,13 @@ impl Global {
|
|||||||
&self,
|
&self,
|
||||||
queue_id: QueueId,
|
queue_id: QueueId,
|
||||||
closure: SubmittedWorkDoneClosure,
|
closure: SubmittedWorkDoneClosure,
|
||||||
) {
|
) -> SubmissionIndex {
|
||||||
|
api_log!("Queue::on_submitted_work_done {queue_id:?}");
|
||||||
|
|
||||||
|
//TODO: flush pending writes
|
||||||
let queue = self.hub.queues.get(queue_id);
|
let queue = self.hub.queues.get(queue_id);
|
||||||
queue.on_submitted_work_done(closure);
|
let result = queue.on_submitted_work_done(closure);
|
||||||
|
result.unwrap_or(0) // '0' means no wait is necessary
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -493,8 +493,6 @@ impl Device {
|
|||||||
let submission_closures =
|
let submission_closures =
|
||||||
life_tracker.triage_submissions(submission_index, &self.command_allocator);
|
life_tracker.triage_submissions(submission_index, &self.command_allocator);
|
||||||
|
|
||||||
life_tracker.triage_mapped();
|
|
||||||
|
|
||||||
let mapping_closures = life_tracker.handle_mapping(self.raw(), &snatch_guard);
|
let mapping_closures = life_tracker.handle_mapping(self.raw(), &snatch_guard);
|
||||||
|
|
||||||
let queue_empty = life_tracker.queue_empty();
|
let queue_empty = life_tracker.queue_empty();
|
||||||
|
@ -15,7 +15,7 @@ use crate::{
|
|||||||
snatch::{SnatchGuard, Snatchable},
|
snatch::{SnatchGuard, Snatchable},
|
||||||
track::{SharedTrackerIndexAllocator, TextureSelector, TrackerIndex},
|
track::{SharedTrackerIndexAllocator, TextureSelector, TrackerIndex},
|
||||||
weak_vec::WeakVec,
|
weak_vec::WeakVec,
|
||||||
Label, LabelHelpers,
|
Label, LabelHelpers, SubmissionIndex,
|
||||||
};
|
};
|
||||||
|
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
@ -304,7 +304,7 @@ impl BufferMapCallback {
|
|||||||
// SAFETY: the contract of the call to from_c says that this unsafe is sound.
|
// SAFETY: the contract of the call to from_c says that this unsafe is sound.
|
||||||
BufferMapCallbackInner::C { inner } => unsafe {
|
BufferMapCallbackInner::C { inner } => unsafe {
|
||||||
let status = match result {
|
let status = match result {
|
||||||
Ok(()) => BufferMapAsyncStatus::Success,
|
Ok(_) => BufferMapAsyncStatus::Success,
|
||||||
Err(BufferAccessError::Device(_)) => BufferMapAsyncStatus::ContextLost,
|
Err(BufferAccessError::Device(_)) => BufferMapAsyncStatus::ContextLost,
|
||||||
Err(BufferAccessError::InvalidResource(_))
|
Err(BufferAccessError::InvalidResource(_))
|
||||||
| Err(BufferAccessError::DestroyedResource(_)) => BufferMapAsyncStatus::Invalid,
|
| Err(BufferAccessError::DestroyedResource(_)) => BufferMapAsyncStatus::Invalid,
|
||||||
@ -546,7 +546,7 @@ impl Buffer {
|
|||||||
offset: wgt::BufferAddress,
|
offset: wgt::BufferAddress,
|
||||||
size: Option<wgt::BufferAddress>,
|
size: Option<wgt::BufferAddress>,
|
||||||
op: BufferMapOperation,
|
op: BufferMapOperation,
|
||||||
) -> Result<(), (BufferMapOperation, BufferAccessError)> {
|
) -> Result<SubmissionIndex, (BufferMapOperation, BufferAccessError)> {
|
||||||
let range_size = if let Some(size) = size {
|
let range_size = if let Some(size) = size {
|
||||||
size
|
size
|
||||||
} else if offset > self.size {
|
} else if offset > self.size {
|
||||||
@ -633,9 +633,9 @@ impl Buffer {
|
|||||||
.buffers
|
.buffers
|
||||||
.set_single(self, internal_use);
|
.set_single(self, internal_use);
|
||||||
|
|
||||||
device.lock_life().map(self);
|
let submit_index = device.lock_life().map(self).unwrap_or(0); // '0' means no wait is necessary
|
||||||
|
|
||||||
Ok(())
|
Ok(submit_index)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: This must not be called while holding a lock.
|
// Note: This must not be called while holding a lock.
|
||||||
|
@ -1411,7 +1411,7 @@ impl crate::Context for ContextWgpuCore {
|
|||||||
Some(range.end - range.start),
|
Some(range.end - range.start),
|
||||||
operation,
|
operation,
|
||||||
) {
|
) {
|
||||||
Ok(()) => (),
|
Ok(_) => (),
|
||||||
Err(cause) => {
|
Err(cause) => {
|
||||||
self.handle_error_nolabel(&buffer_data.error_sink, cause, "Buffer::map_async")
|
self.handle_error_nolabel(&buffer_data.error_sink, cause, "Buffer::map_async")
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user