diff --git a/deno_webgpu/src/buffer.rs b/deno_webgpu/src/buffer.rs index 845f218372..45eab483d1 100644 --- a/deno_webgpu/src/buffer.rs +++ b/deno_webgpu/src/buffer.rs @@ -128,7 +128,7 @@ pub async fn op_webgpu_buffer_get_map_async( { let state = state.borrow(); let instance = state.borrow::(); - gfx_select!(device => instance.device_poll(device, false)).unwrap() + gfx_select!(device => instance.device_poll(device, false)).unwrap(); } tokio::time::sleep(Duration::from_millis(10)).await; } diff --git a/wgpu-core/src/device/mod.rs b/wgpu-core/src/device/mod.rs index 760de1195c..c6d1583ce1 100644 --- a/wgpu-core/src/device/mod.rs +++ b/wgpu-core/src/device/mod.rs @@ -4949,12 +4949,14 @@ impl Global { } /// Check `device_id` for freeable resources and completed buffer mappings. + /// + /// Return `queue_empty` indicating whether there are more queue submissions still in flight. pub fn device_poll( &self, device_id: id::DeviceId, force_wait: bool, - ) -> Result<(), WaitIdleError> { - let (closures, _) = { + ) -> Result { + let (closures, queue_empty) = { let hub = A::hub(self); let mut token = Token::root(); let (device_guard, mut token) = hub.devices.read(&mut token); @@ -4966,27 +4968,31 @@ impl Global { unsafe { closures.fire(); } - Ok(()) + Ok(queue_empty) } /// Poll all devices belonging to the backend `A`. /// /// 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_devices( &self, force_wait: bool, closures: &mut UserClosures, - ) -> Result<(), WaitIdleError> { + ) -> Result { profiling::scope!("poll_devices"); let hub = A::hub(self); let mut devices_to_drop = vec![]; + let mut all_queue_empty = true; { let mut token = Token::root(); let (device_guard, mut token) = hub.devices.read(&mut token); for (id, device) in device_guard.iter(A::VARIANT) { let (cbs, queue_empty) = device.maintain(hub, force_wait, &mut token)?; + all_queue_empty = all_queue_empty && queue_empty; // If the device's own `RefCount` clone is the only one left, and // its submission queue is empty, then it can be freed. @@ -5001,41 +5007,49 @@ impl Global { self.exit_device::(device_id); } - Ok(()) + Ok(all_queue_empty) } /// Poll all devices on all backends. /// /// This is the implementation of `wgpu::Instance::poll_all`. - pub fn poll_all_devices(&self, force_wait: bool) -> Result<(), WaitIdleError> { + /// + /// Return `all_queue_empty` indicating whether there are more queue submissions still in flight. + pub fn poll_all_devices(&self, force_wait: bool) -> Result { let mut closures = UserClosures::default(); + let mut all_queue_empty = true; #[cfg(vulkan)] { - self.poll_devices::(force_wait, &mut closures)?; + all_queue_empty = self.poll_devices::(force_wait, &mut closures)? + && all_queue_empty; } #[cfg(metal)] { - self.poll_devices::(force_wait, &mut closures)?; + all_queue_empty = + self.poll_devices::(force_wait, &mut closures)? && all_queue_empty; } #[cfg(dx12)] { - self.poll_devices::(force_wait, &mut closures)?; + all_queue_empty = + self.poll_devices::(force_wait, &mut closures)? && all_queue_empty; } #[cfg(dx11)] { - self.poll_devices::(force_wait, &mut closures)?; + all_queue_empty = + self.poll_devices::(force_wait, &mut closures)? && all_queue_empty; } #[cfg(gl)] { - self.poll_devices::(force_wait, &mut closures)?; + all_queue_empty = + self.poll_devices::(force_wait, &mut closures)? && all_queue_empty; } unsafe { closures.fire(); } - Ok(()) + Ok(all_queue_empty) } pub fn device_label(&self, id: id::DeviceId) -> String { diff --git a/wgpu/src/backend/direct.rs b/wgpu/src/backend/direct.rs index 753b08d942..64c567b975 100644 --- a/wgpu/src/backend/direct.rs +++ b/wgpu/src/backend/direct.rs @@ -824,10 +824,10 @@ impl crate::Context for Context { ready(id.ok()) } - fn instance_poll_all_devices(&self, force_wait: bool) { + fn instance_poll_all_devices(&self, force_wait: bool) -> bool { let global = &self.0; match global.poll_all_devices(force_wait) { - Ok(()) => (), + Ok(all_queue_empty) => all_queue_empty, Err(err) => self.handle_error_fatal(err, "Device::poll"), } } @@ -1554,7 +1554,7 @@ impl crate::Context for Context { #[cfg(any(not(target_arch = "wasm32"), feature = "emscripten"))] { match wgc::gfx_select!(device.id => global.device_poll(device.id, true)) { - Ok(()) => (), + Ok(_) => (), Err(err) => self.handle_error_fatal(err, "Device::drop"), } } @@ -1562,7 +1562,7 @@ impl crate::Context for Context { wgc::gfx_select!(device.id => global.device_drop(device.id)); } - fn device_poll(&self, device: &Self::DeviceId, maintain: crate::Maintain) { + fn device_poll(&self, device: &Self::DeviceId, maintain: crate::Maintain) -> bool { let global = &self.0; match wgc::gfx_select!(device.id => global.device_poll( device.id, @@ -1571,7 +1571,7 @@ impl crate::Context for Context { crate::Maintain::Wait => true, } )) { - Ok(()) => (), + Ok(queue_empty) => queue_empty, Err(err) => self.handle_error_fatal(err, "Device::poll"), } } diff --git a/wgpu/src/backend/web.rs b/wgpu/src/backend/web.rs index d45d491f41..2696d158a6 100644 --- a/wgpu/src/backend/web.rs +++ b/wgpu/src/backend/web.rs @@ -1066,8 +1066,9 @@ impl crate::Context for Context { ) } - fn instance_poll_all_devices(&self, _force_wait: bool) { + fn instance_poll_all_devices(&self, _force_wait: bool) -> bool { // Devices are automatically polled. + true } fn adapter_request_device( @@ -1691,8 +1692,9 @@ impl crate::Context for Context { // Device is dropped automatically } - fn device_poll(&self, _device: &Self::DeviceId, _maintain: crate::Maintain) { + fn device_poll(&self, _device: &Self::DeviceId, _maintain: crate::Maintain) -> bool { // Device is polled automatically + true } fn device_on_uncaptured_error( diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index afe3e7ac8a..2928c6bb91 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -210,7 +210,7 @@ trait Context: Debug + Send + Sized + Sync { desc: &DeviceDescriptor, trace_dir: Option<&std::path::Path>, ) -> Self::RequestDeviceFuture; - fn instance_poll_all_devices(&self, force_wait: bool); + fn instance_poll_all_devices(&self, force_wait: bool) -> bool; fn adapter_is_surface_supported( &self, adapter: &Self::AdapterId, @@ -322,7 +322,7 @@ trait Context: Debug + Send + Sized + Sync { desc: &RenderBundleEncoderDescriptor, ) -> Self::RenderBundleEncoderId; fn device_drop(&self, device: &Self::DeviceId); - fn device_poll(&self, device: &Self::DeviceId, maintain: Maintain); + fn device_poll(&self, device: &Self::DeviceId, maintain: Maintain) -> bool; fn device_on_uncaptured_error( &self, device: &Self::DeviceId, @@ -1561,10 +1561,23 @@ impl Instance { } /// Polls all devices. - /// If `force_wait` is true and this is not running on the web, - /// then this function will block until all in-flight buffers have been mapped. - pub fn poll_all(&self, force_wait: bool) { - self.context.instance_poll_all_devices(force_wait); + /// + /// If `force_wait` is true and this is not running on the web, then this + /// function will block until all in-flight buffers have been mapped and + /// all submitted commands have finished execution. + /// + /// Return `true` if all devices' queues are empty, or `false` if there are + /// queue submissions still in flight. (Note that, unless access to all + /// [`Queue`s] associated with this [`Instance`] is coordinated somehow, + /// this information could be out of date by the time the caller receives + /// it. `Queue`s can be shared between threads, and other threads could + /// submit new work at any time.) + /// + /// On the web, this is a no-op. `Device`s are automatically polled. + /// + /// [`Queue`s]: Queue + pub fn poll_all(&self, force_wait: bool) -> bool { + self.context.instance_poll_all_devices(force_wait) } /// Generates memory report. @@ -1687,9 +1700,15 @@ impl Adapter { impl Device { /// Check for resource cleanups and mapping callbacks. /// - /// no-op on the web, device is automatically polled. - pub fn poll(&self, maintain: Maintain) { - Context::device_poll(&*self.context, &self.id, maintain); + /// Return `true` if the queue is empty, or `false` if there are more queue + /// submissions still in flight. (Note that, unless access to the [`Queue`] is + /// coordinated somehow, this information could be out of date by the time + /// the caller receives it. `Queue`s can be shared between threads, so + /// other threads could submit new work at any time.) + /// + /// On the web, this is a no-op. `Device`s are automatically polled. + pub fn poll(&self, maintain: Maintain) -> bool { + Context::device_poll(&*self.context, &self.id, maintain) } /// List all features that may be used with this device.