From f13f17813277c48babd7bb9692f634b4650c0e1c 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/hub.rs | 10 ++++++ 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 | 20 +++++++++++ wgpu-hal/src/vulkan/instance.rs | 1 + wgpu-hal/src/vulkan/mod.rs | 20 +++++++++++ 15 files changed, 201 insertions(+), 22 deletions(-) diff --git a/wgpu-core/src/device/mod.rs b/wgpu-core/src/device/mod.rs index 9e311d1019..a16485fb8a 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 hal::{CommandEncoder as _, Device as _}; +use hal::{CommandEncoder as _, Device as _, Texture}; use parking_lot::{Mutex, MutexGuard}; use smallvec::SmallVec; use thiserror::Error; @@ -4043,6 +4043,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()), @@ -4062,11 +4064,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 8ee687269d..b4d5056aee 100644 --- a/wgpu-core/src/device/queue.rs +++ b/wgpu-core/src/device/queue.rs @@ -12,13 +12,14 @@ use crate::{ id, 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, ptr}; +use std::{collections::HashSet, iter, mem, ptr}; use thiserror::Error; /// Number of command buffers that we generate from the same pool @@ -1218,6 +1219,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 mut visited_ids = HashSet::new(); + + let external_textures = baked + .trackers + .textures + .pending() + // Iterate in reverse to find the last transition state. + .rev() + // We only care about external textures + .filter(|transition| { + // SAFETY: The texture must be known by the tracker if it was used during + // command submission or is pending. + let texture = + unsafe { texture_guard.get_unchecked(transition.id) }; + + texture + .inner + .as_raw() + .map(::is_external) + .unwrap_or(false) + }) + .filter(|transition| { + // Insert returns false if the element was already added. + visited_ids.insert(&transition.id) + }); + + external_textures.for_each(|transition| { + // Create and record a synthetic transition state to EXTERNAL based on the last usage. + unsafe { + let id = texture_guard + .get_valid_unchecked(transition.id, A::VARIANT); + let ref_count = baked.trackers.textures.get_ref_count(id); + used_external_textures + .merge_single( + &*texture_guard, + id, + Some(transition.selector.clone()), + ref_count, + 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/hub.rs b/wgpu-core/src/hub.rs index 0a3b5d954e..b6235a3f61 100644 --- a/wgpu-core/src/hub.rs +++ b/wgpu-core/src/hub.rs @@ -378,6 +378,16 @@ impl Storage { } } + pub(crate) unsafe fn get_valid_unchecked(&self, id: u32, backend: Backend) -> id::Valid { + let epoch = match self.map[id as usize] { + Element::Occupied(_, epoch) => epoch, + Element::Vacant => panic!("{}[{}] does not exist", self.kind, id), + Element::Error(_, _) => panic!(""), + }; + + id::Valid(I::zip(id, epoch, backend)) + } + pub(crate) fn label_for_invalid_id(&self, id: I) -> &str { let (index, _, _) = id.unzip(); match self.map.get(index as usize) { diff --git a/wgpu-core/src/track/mod.rs b/wgpu-core/src/track/mod.rs index b2548f08ae..3f1566837a 100644 --- a/wgpu-core/src/track/mod.rs +++ b/wgpu-core/src/track/mod.rs @@ -119,7 +119,7 @@ use wgt::strict_assert_ne; /// 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 6db2bab725..a1c832ee2e 100644 --- a/wgpu-core/src/track/texture.rs +++ b/wgpu-core/src/track/texture.rs @@ -451,6 +451,11 @@ impl TextureTracker { self.metadata.owned_ids() } + /// Returns all currently pending transitions. + pub fn pending(&self) -> impl DoubleEndedIterator> + '_ { + 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 3b087c4311..57229d42a0 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 { + 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 6cdf3ffe64..eab3d58862 100644 --- a/wgpu-hal/src/dx12/mod.rs +++ b/wgpu-hal/src/dx12/mod.rs @@ -900,3 +900,9 @@ impl crate::Queue for Queue { (1_000_000_000.0 / frequency as f64) as f32 } } + +impl crate::Texture for Texture { + fn is_external(&self) -> bool { + false + } +} diff --git a/wgpu-hal/src/empty.rs b/wgpu-hal/src/empty.rs index 1497acad91..d0356a756e 100644 --- a/wgpu-hal/src/empty.rs +++ b/wgpu-hal/src/empty.rs @@ -411,3 +411,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 { + fn is_external(&self) -> bool { + false + } +} diff --git a/wgpu-hal/src/gles/device.rs b/wgpu-hal/src/gles/device.rs index 0a1cfaf241..4d613067ff 100644 --- a/wgpu-hal/src/gles/device.rs +++ b/wgpu-hal/src/gles/device.rs @@ -1321,6 +1321,12 @@ impl crate::Device for super::Device { } } +impl crate::Texture for super::Texture { + 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 1758149380..c8aea7b5fb 100644 --- a/wgpu-hal/src/lib.rs +++ b/wgpu-hal/src/lib.rs @@ -164,7 +164,7 @@ pub trait Api: Clone + Sized { type CommandBuffer: Send + Sync + fmt::Debug; 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; @@ -550,6 +550,13 @@ pub trait CommandEncoder: Send + Sync + fmt::Debug { 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. + fn is_external(&self) -> bool; +} + bitflags!( /// Instance initialization flags. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] @@ -765,9 +772,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 b77685bd94..acf088154f 100644 --- a/wgpu-hal/src/metal/mod.rs +++ b/wgpu-hal/src/metal/mod.rs @@ -406,6 +406,12 @@ impl crate::Queue for Queue { } } +impl crate::Texture for Texture { + fn is_external(&self) -> bool { + false + } +} + #[derive(Debug)] pub struct Buffer { raw: metal::Buffer, diff --git a/wgpu-hal/src/vulkan/command.rs b/wgpu-hal/src/vulkan/command.rs index f6c871026c..8be43c3e67 100644 --- a/wgpu-hal/src/vulkan/command.rs +++ b/wgpu-hal/src/vulkan/command.rs @@ -165,16 +165,30 @@ impl crate::CommandEncoder for super::CommandEncoder { let dst_layout = conv::derive_image_layout(bar.usage.end, bar.texture.format); 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 09b887772c..970bfd7080 100644 --- a/wgpu-hal/src/vulkan/device.rs +++ b/wgpu-hal/src/vulkan/device.rs @@ -646,12 +646,15 @@ 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 `external_queue_family_index` is set, then [`TextureUses::EXTERNAL`](crate::TextureUses::EXTERNAL) 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`. /// - If the `ImageCreateFlags` does not contain `MUTABLE_FORMAT`, the `view_formats` of `desc` must be empty. pub unsafe fn texture_from_raw( vk_image: vk::Image, desc: &crate::TextureDescriptor, + external_queue_family_index: Option, drop_guard: Option, ) -> super::Texture { let mut raw_flags = vk::ImageCreateFlags::empty(); @@ -668,6 +671,20 @@ impl super::Device { view_formats.push(desc.format) } + if desc.usage.contains(crate::TextureUses::EXTERNAL) { + wgt::strict_assert!( + external_queue_family_index.is_none(), + "Texture has TextureUse::EXTERNAL, but does not specify the owning queue family" + ); + } + + if external_queue_family_index.is_none() { + wgt::strict_assert!( + desc.usage.contains(crate::TextureUses::EXTERNAL), + "Texture specifies external queue family ownership but does not have TextureUse::EXTERNAL" + ); + } + super::Texture { raw: vk_image, drop_guard, @@ -677,6 +694,7 @@ impl super::Device { raw_flags: vk::ImageCreateFlags::empty(), copy_size: desc.copy_extent(), view_formats, + external_queue_family_index, } } @@ -1018,6 +1036,8 @@ impl crate::Device for super::Device { raw_flags, copy_size, view_formats: wgt_view_formats, + // 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 5fbdf42f44..d2053478dd 100644 --- a/wgpu-hal/src/vulkan/instance.rs +++ b/wgpu-hal/src/vulkan/instance.rs @@ -820,6 +820,7 @@ impl crate::Surface for super::Surface { depth: 1, }, view_formats: sc.view_formats.clone(), + 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 27200dc4e0..72973513b9 100644 --- a/wgpu-hal/src/vulkan/mod.rs +++ b/wgpu-hal/src/vulkan/mod.rs @@ -297,6 +297,19 @@ pub struct Texture { raw_flags: vk::ImageCreateFlags, copy_size: crate::CopyExtent, view_formats: Vec, + /// 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 { @@ -610,6 +623,13 @@ impl crate::Queue for Queue { } } +impl crate::Texture for Texture { + 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 {