diff --git a/wgpu-core/src/device/global.rs b/wgpu-core/src/device/global.rs index 288e90aa338..f58a772bb9f 100644 --- a/wgpu-core/src/device/global.rs +++ b/wgpu-core/src/device/global.rs @@ -2064,7 +2064,7 @@ impl Global { self.hub.queues.remove(queue_id); } - /// `op.callback` is guaranteed to be called. + /// `op.callback` is always called, even in case of errors. pub fn buffer_map_async( &self, buffer_id: id::BufferId, @@ -2077,20 +2077,9 @@ impl Global { let hub = &self.hub; - let map_result = match hub.buffers.get(buffer_id).get() { - Ok(buffer) => buffer.map_async(offset, size, op), - Err(e) => Err((op, e.into())), - }; + let buffer = hub.buffers.get(buffer_id).get()?; - match map_result { - Ok(submission_index) => Ok(submission_index), - Err((mut operation, err)) => { - if let Some(callback) = operation.callback.take() { - callback(Err(err.clone())); - } - Err(err) - } - } + buffer.map_async(offset, size, op) } pub fn buffer_get_mapped_range( diff --git a/wgpu-core/src/device/life.rs b/wgpu-core/src/device/life.rs index aef58019f8a..b3d91855bb0 100644 --- a/wgpu-core/src/device/life.rs +++ b/wgpu-core/src/device/life.rs @@ -219,8 +219,11 @@ impl LifetimeTracker { }); } + /// Schedule a buffer for mapping. + /// + /// The buffer will be added either to a pending submission, or to `self.ready_to_map`. + /// If it is added to a pending submission, returns the index of that submission. pub(crate) fn map(&mut self, buffer: &Arc) -> Option { - // Determine which buffers are ready to map, and which must wait for the GPU. let submission = self .active .iter_mut() diff --git a/wgpu-core/src/resource.rs b/wgpu-core/src/resource.rs index be5096dc052..476a5f8d9a6 100644 --- a/wgpu-core/src/resource.rs +++ b/wgpu-core/src/resource.rs @@ -591,13 +591,52 @@ impl Buffer { )) } - /// Returns the mapping callback in case of error so that the callback can be fired outside - /// of the locks that are held in this function. + /// Schedule buffer mapping. + /// + /// `op.callback` is guaranteed to be called, regardless of the outcome. pub fn map_async( self: &Arc, offset: wgt::BufferAddress, size: Option, op: BufferMapOperation, + ) -> Result { + self.try_map_async(offset, size, op) + .map_err(|(mut operation, err)| { + if let Some(callback) = operation.callback.take() { + callback(Err(err.clone())); + } + err + }) + } + + /// Try to schedule buffer mapping. + /// + /// The outcome of this function is one of the following: + /// - If there is a queue, and nothing pending in the queue that uses the + /// buffer in question, the buffer is added to `Queue::ready_to_map`, and + /// will be mapped the next time `Device::maintain` is called. The + /// queue assumes responsibility for calling the callback, and this + /// function returns `Ok(0)`, but the buffer has not yet been mapped. + /// - If there is a queue, and something is pending in the queue that uses + /// the buffer in question, the buffer is scheduled for mapping after that + /// submission completes. The queue assumes responsibility for calling the + /// callback, and this function returns `Ok(index)` with the index of the + /// submission that must complete. The buffer has not yet been mapped. + /// - If there is no queue, the buffer is mapped and the callback is called + /// immediately. The return value is `Ok(0)`. + /// - Regardless of the queue state, if there is an error that terminates + /// the buffer mapping attempt, this function returns the callback along + /// with the error, and the caller is responsible for calling the + /// callback. + /// + /// A return value of `Ok(0)` roughly means that no wait is necessary, + /// but it does not necessarily mean that the buffer has already been + /// mapped. + fn try_map_async( + self: &Arc, + offset: wgt::BufferAddress, + size: Option, + op: BufferMapOperation, ) -> Result { let range_size = if let Some(size) = size { size