From 9bd347b1e4e1c4c36a9a65b68e5eda3e18b7423a Mon Sep 17 00:00:00 2001 From: i509VCB Date: Mon, 12 Sep 2022 00:51:49 -0500 Subject: [PATCH] tracking for EXTERNAL texture use --- wgpu-core/src/device/mod.rs | 20 +++++++---- wgpu-core/src/device/queue.rs | 63 +++++++++++++++++++++++++++++++-- wgpu-core/src/track/mod.rs | 2 +- wgpu-core/src/track/texture.rs | 5 +++ wgpu-hal/src/dx11/device.rs | 6 ++++ wgpu-hal/src/dx12/mod.rs | 6 ++++ wgpu-hal/src/empty.rs | 6 ++++ wgpu-hal/src/gles/device.rs | 6 ++++ wgpu-hal/src/lib.rs | 18 ++++++++-- wgpu-hal/src/metal/mod.rs | 6 ++++ wgpu-hal/src/vulkan/command.rs | 34 ++++++++++++------ wgpu-hal/src/vulkan/device.rs | 12 +++++++ wgpu-hal/src/vulkan/instance.rs | 1 + wgpu-hal/src/vulkan/mod.rs | 20 +++++++++++ 14 files changed, 183 insertions(+), 22 deletions(-) diff --git a/wgpu-core/src/device/mod.rs b/wgpu-core/src/device/mod.rs index a5c5cbe51ca..63dccfc0f7a 100644 --- a/wgpu-core/src/device/mod.rs +++ b/wgpu-core/src/device/mod.rs @@ -18,7 +18,7 @@ use crate::{ use arrayvec::ArrayVec; use copyless::VecHelper as _; -use hal::{CommandEncoder as _, Device as _}; +use hal::{CommandEncoder as _, Device as _, Texture}; use parking_lot::{Mutex, MutexGuard}; use smallvec::SmallVec; use thiserror::Error; @@ -3741,6 +3741,8 @@ impl Global { Err(error) => break error, }; + let is_external = hal_texture.is_external(); + let mut texture = device.create_texture_from_hal( hal_texture, conv::map_texture_usage(desc.usage, desc.format.into()), @@ -3760,11 +3762,17 @@ impl Global { let id = fid.assign(texture, &mut token); log::info!("Created texture {:?} with {:?}", id, desc); - device.trackers.lock().textures.insert_single( - id.0, - ref_count, - hal::TextureUses::UNINITIALIZED, - ); + let mut uses = hal::TextureUses::UNINITIALIZED; + + if is_external { + uses |= hal::TextureUses::EXTERNAL; + } + + device + .trackers + .lock() + .textures + .insert_single(id.0, ref_count, uses); return (id.0, None); }; diff --git a/wgpu-core/src/device/queue.rs b/wgpu-core/src/device/queue.rs index 748f1057da1..ae81be54474 100644 --- a/wgpu-core/src/device/queue.rs +++ b/wgpu-core/src/device/queue.rs @@ -9,13 +9,14 @@ use crate::{ device::{DeviceError, WaitIdleError}, get_lowest_common_denom, hub::{Global, GlobalIdentityHandlerFactory, HalApi, Input, Token}, - id, + id::{self, TypedId}, init_tracker::{has_copy_partial_init_tracker_coverage, TextureInitRange}, resource::{BufferAccessError, BufferMapState, StagingBuffer, TextureInner}, - track, FastHashSet, SubmissionIndex, + track::{self, TextureUsageScope}, + FastHashSet, SubmissionIndex, }; -use hal::{CommandEncoder as _, Device as _, Queue as _}; +use hal::{CommandEncoder as _, Device as _, Queue as _, Texture as _}; use parking_lot::Mutex; use smallvec::SmallVec; use std::{iter, mem, num::NonZeroU32, ptr}; @@ -978,6 +979,62 @@ impl Global { baked .initialize_texture_memory(&mut *trackers, &mut *texture_guard, device) .map_err(|err| QueueSubmitError::DestroyedTexture(err.0))?; + + // Insert synthetic barriers to insert EXTERNAL barriers for any used external textures. + { + let mut used_external_textures = TextureUsageScope::new(); + + let external_textures = baked + .trackers + .textures + .used() + .filter(|id| { + texture_guard[*id] + .inner + .as_raw() + .map(|inner| !unsafe { inner.is_external() }) + .unwrap_or(false) + }) + .collect::>(); + + for id in external_textures { + // Get the last transition state for the texture. + if let Some(last_transition) = baked + .trackers + .textures + .pending() + .filter(|pending| { + let (index, _, _) = id.0.unzip(); + index == pending.id + }) + .last() + .cloned() + { + // Create and record a synthetic transition state to EXTERNAL based on the last usage. + unsafe { + let ref_count = baked.trackers.textures.get_ref_count(id); + used_external_textures + .merge_single( + &*texture_guard, + id, + Some(last_transition.selector.clone()), + ref_count, + last_transition.usage.end + | hal::TextureUses::EXTERNAL, + ) + .unwrap(); + } + } + } + + if !used_external_textures.is_empty() { + baked + .trackers + .textures + .set_from_usage_scope(&*texture_guard, &used_external_textures); + } + } + //Note: stateless trackers are not merged: // device already knows these resources exist. CommandBuffer::insert_barriers_from_tracker( diff --git a/wgpu-core/src/track/mod.rs b/wgpu-core/src/track/mod.rs index 7cf822e4cfe..37c884b77aa 100644 --- a/wgpu-core/src/track/mod.rs +++ b/wgpu-core/src/track/mod.rs @@ -113,7 +113,7 @@ pub(crate) use texture::{ /// A structure containing all the information about a particular resource /// transition. User code should be able to generate a pipeline barrier /// based on the contents. -#[derive(Debug, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub(crate) struct PendingTransition { pub id: u32, pub selector: S::Selector, diff --git a/wgpu-core/src/track/texture.rs b/wgpu-core/src/track/texture.rs index 4ad833dc052..a9acd0afe5b 100644 --- a/wgpu-core/src/track/texture.rs +++ b/wgpu-core/src/track/texture.rs @@ -446,6 +446,11 @@ impl TextureTracker { self.metadata.used() } + /// Returns all currently pending transitions. + pub fn pending(&self) -> impl Iterator> + '_ { + self.temp.iter() + } + /// Drains all currently pending transitions. pub fn drain(&mut self) -> Drain> { self.temp.drain(..) diff --git a/wgpu-hal/src/dx11/device.rs b/wgpu-hal/src/dx11/device.rs index 7b095ba1df3..cab16c62c58 100644 --- a/wgpu-hal/src/dx11/device.rs +++ b/wgpu-hal/src/dx11/device.rs @@ -224,6 +224,12 @@ impl crate::Queue for super::Queue { } } +impl crate::Texture for super::Texture { + unsafe fn is_external(&self) -> bool { + false + } +} + impl super::D3D11Device { #[allow(trivial_casts)] // come on pub unsafe fn check_feature_support(&self, feature: d3d11::D3D11_FEATURE) -> T { diff --git a/wgpu-hal/src/dx12/mod.rs b/wgpu-hal/src/dx12/mod.rs index 61d2ad95761..9d5c2b1bac8 100644 --- a/wgpu-hal/src/dx12/mod.rs +++ b/wgpu-hal/src/dx12/mod.rs @@ -793,3 +793,9 @@ impl crate::Queue for Queue { (1_000_000_000.0 / frequency as f64) as f32 } } + +impl crate::Texture for Texture { + unsafe fn is_external(&self) -> bool { + false + } +} diff --git a/wgpu-hal/src/empty.rs b/wgpu-hal/src/empty.rs index 0c546469b22..1c38ea80e8f 100644 --- a/wgpu-hal/src/empty.rs +++ b/wgpu-hal/src/empty.rs @@ -392,3 +392,9 @@ impl crate::CommandEncoder for Encoder { unsafe fn dispatch(&mut self, count: [u32; 3]) {} unsafe fn dispatch_indirect(&mut self, buffer: &Resource, offset: wgt::BufferAddress) {} } + +impl crate::Texture for Resource { + unsafe fn is_external(&self) -> bool { + false + } +} diff --git a/wgpu-hal/src/gles/device.rs b/wgpu-hal/src/gles/device.rs index 04ecdffe02d..d27bce7ebb6 100644 --- a/wgpu-hal/src/gles/device.rs +++ b/wgpu-hal/src/gles/device.rs @@ -1163,6 +1163,12 @@ impl crate::Device for super::Device { } } +impl crate::Texture for super::Texture { + unsafe fn is_external(&self) -> bool { + false + } +} + // SAFE: WASM doesn't have threads #[cfg(target_arch = "wasm32")] unsafe impl Sync for super::Device {} diff --git a/wgpu-hal/src/lib.rs b/wgpu-hal/src/lib.rs index 02d3c13af1f..b2e281d5877 100644 --- a/wgpu-hal/src/lib.rs +++ b/wgpu-hal/src/lib.rs @@ -159,7 +159,7 @@ pub trait Api: Clone + Sized { type CommandBuffer: Send + Sync; type Buffer: fmt::Debug + Send + Sync + 'static; - type Texture: fmt::Debug + Send + Sync + 'static; + type Texture: Texture + 'static; type SurfaceTexture: fmt::Debug + Send + Sync + Borrow; type TextureView: fmt::Debug + Send + Sync; type Sampler: fmt::Debug + Send + Sync; @@ -525,6 +525,13 @@ pub trait CommandEncoder: Send + Sync { unsafe fn dispatch_indirect(&mut self, buffer: &A::Buffer, offset: wgt::BufferAddress); } +pub trait Texture: fmt::Debug + Send + Sync { + /// Whether this texture originates from external memory. + /// + /// This indicates whether the texture may have the `EXTERNAL` usage. + unsafe fn is_external(&self) -> bool; +} + bitflags!( /// Instance initialization flags. pub struct InstanceFlags: u32 { @@ -706,9 +713,16 @@ bitflags::bitflags! { /// Flag used by the wgpu-core texture tracker to say a texture is in different states for every sub-resource const COMPLEX = 1 << 10; + + /// Flag used by the wgpu-core texture tracker to say a texture was imported from external memory. + /// + /// In the Vulkan backend, this indicates the texture needs to be transferred from an external queue + /// family to the graphics queue family. + const EXTERNAL = 1 << 11; + /// Flag used by the wgpu-core texture tracker to say that the tracker does not know the state of the sub-resource. /// This is different from UNINITIALIZED as that says the tracker does know, but the texture has not been initialized. - const UNKNOWN = 1 << 11; + const UNKNOWN = 1 << 12; } } diff --git a/wgpu-hal/src/metal/mod.rs b/wgpu-hal/src/metal/mod.rs index dee9467e74d..9ce3fa92c50 100644 --- a/wgpu-hal/src/metal/mod.rs +++ b/wgpu-hal/src/metal/mod.rs @@ -394,6 +394,12 @@ impl crate::Queue for Queue { } } +impl crate::Texture for Texture { + unsafe fn is_external(&self) -> bool { + false + } +} + #[derive(Debug)] pub struct Buffer { raw: mtl::Buffer, diff --git a/wgpu-hal/src/vulkan/command.rs b/wgpu-hal/src/vulkan/command.rs index e225ca83569..a900d9d08cb 100644 --- a/wgpu-hal/src/vulkan/command.rs +++ b/wgpu-hal/src/vulkan/command.rs @@ -158,16 +158,30 @@ impl crate::CommandEncoder for super::CommandEncoder { let dst_layout = conv::derive_image_layout(bar.usage.end, bar.texture.aspects); dst_stages |= dst_stage; - vk_barriers.push( - vk::ImageMemoryBarrier::builder() - .image(bar.texture.raw) - .subresource_range(range) - .src_access_mask(src_access) - .dst_access_mask(dst_access) - .old_layout(src_layout) - .new_layout(dst_layout) - .build(), - ); + let mut barrier = vk::ImageMemoryBarrier::builder() + .image(bar.texture.raw) + .subresource_range(range) + .src_access_mask(src_access) + .dst_access_mask(dst_access) + .old_layout(src_layout) + .new_layout(dst_layout); + + // If the texture is external, we need to specify a queue family ownership transfer. + if bar.usage.start.contains(crate::TextureUses::EXTERNAL) { + barrier = barrier + .src_queue_family_index(bar.texture.external_queue_family_index.unwrap()) + .dst_queue_family_index(self.device.queue_index); + } + + // If this is the last usage of the texture during this command submission, return the queue to + // it's sentinel queue family. + if bar.usage.end.contains(crate::TextureUses::EXTERNAL) { + barrier = barrier + .src_queue_family_index(self.device.queue_index) + .dst_queue_family_index(bar.texture.external_queue_family_index.unwrap()); + } + + vk_barriers.push(barrier.build()); } if !vk_barriers.is_empty() { diff --git a/wgpu-hal/src/vulkan/device.rs b/wgpu-hal/src/vulkan/device.rs index b9d74e36e35..3f58efe210f 100644 --- a/wgpu-hal/src/vulkan/device.rs +++ b/wgpu-hal/src/vulkan/device.rs @@ -604,13 +604,22 @@ impl super::Device { /// # Safety /// /// - `vk_image` must be created respecting `desc` + /// - If [`TextureUses::EXTERNAL`](crate::TextureUses::EXTERNAL) is set, then `external_queue_family_index` must be set. /// - If `drop_guard` is `Some`, the application must manually destroy the image handle. This /// can be done inside the `Drop` impl of `drop_guard`. pub unsafe fn texture_from_raw( vk_image: vk::Image, desc: &crate::TextureDescriptor, + external_queue_family_index: Option, drop_guard: Option, ) -> super::Texture { + #[cfg(debug_assertions)] + { + if desc.usage.contains(crate::TextureUses::EXTERNAL) { + assert!(external_queue_family_index.is_some(), "Texture has TextureUse::EXTERNAL, but does not specify the owning queue family"); + } + } + super::Texture { raw: vk_image, drop_guard, @@ -620,6 +629,7 @@ impl super::Device { format_info: desc.format.describe(), raw_flags: vk::ImageCreateFlags::empty(), copy_size: conv::map_extent_to_copy_size(&desc.size, desc.dimension), + external_queue_family_index, } } @@ -927,6 +937,8 @@ impl crate::Device for super::Device { format_info: desc.format.describe(), raw_flags, copy_size, + // wgpu's own textures use the exclusive sharing mode. + external_queue_family_index: None, }) } unsafe fn destroy_texture(&self, texture: super::Texture) { diff --git a/wgpu-hal/src/vulkan/instance.rs b/wgpu-hal/src/vulkan/instance.rs index bd3a8848790..cfad6cddbbe 100644 --- a/wgpu-hal/src/vulkan/instance.rs +++ b/wgpu-hal/src/vulkan/instance.rs @@ -779,6 +779,7 @@ impl crate::Surface for super::Surface { &sc.config.extent, wgt::TextureDimension::D2, ), + external_queue_family_index: None, }, }; Ok(Some(crate::AcquiredSurfaceTexture { diff --git a/wgpu-hal/src/vulkan/mod.rs b/wgpu-hal/src/vulkan/mod.rs index d3416a50ed9..147b47a3485 100644 --- a/wgpu-hal/src/vulkan/mod.rs +++ b/wgpu-hal/src/vulkan/mod.rs @@ -354,6 +354,19 @@ pub struct Texture { format_info: wgt::TextureFormatInfo, raw_flags: vk::ImageCreateFlags, copy_size: crate::CopyExtent, + /// The index of the external queue family which owns the image contents. + /// + /// When using images imported from external memory in Vulkan, the images belong to a sentinel "external" + /// queue family. In order to use these textures, the texture must be transferred to the graphics queue + /// family using a memory barrier before the texture used, and then returned to the sentinel queue at the + /// end of command execution. + /// + /// If this is [`Some`], the value is typically [`QUEUE_FAMILY_EXTERNAL`](ash::vk::QUEUE_FAMILY_EXTERNAL) + /// or [`QUEUE_FAMILY_FOREIGN_EXT`](ash::vk::QUEUE_FAMILY_FOREIGN_EXT) depending on imported memory object + /// and or the type of memory object. + /// + /// The value will be [`None`] if the texture was not imported using external memory. + external_queue_family_index: Option, } impl Texture { @@ -657,6 +670,13 @@ impl crate::Queue for Queue { } } +impl crate::Texture for Texture { + unsafe fn is_external(&self) -> bool { + self.usage.contains(crate::TextureUses::EXTERNAL) + && self.external_queue_family_index.is_some() + } +} + impl From for crate::DeviceError { fn from(result: vk::Result) -> Self { match result {