Skip to content

Commit

Permalink
tracking for EXTERNAL texture use
Browse files Browse the repository at this point in the history
  • Loading branch information
i509VCB committed Sep 16, 2022
1 parent 94ce763 commit 9bd347b
Show file tree
Hide file tree
Showing 14 changed files with 183 additions and 22 deletions.
20 changes: 14 additions & 6 deletions wgpu-core/src/device/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -3741,6 +3741,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
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()),
Expand All @@ -3760,11 +3762,17 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
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);
};
Expand Down
63 changes: 60 additions & 3 deletions wgpu-core/src/device/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -978,6 +979,62 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
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::<Vec<_>>();

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(
Expand Down
2 changes: 1 addition & 1 deletion wgpu-core/src/track/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<S: ResourceUses> {
pub id: u32,
pub selector: S::Selector,
Expand Down
5 changes: 5 additions & 0 deletions wgpu-core/src/track/texture.rs
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,11 @@ impl<A: hub::HalApi> TextureTracker<A> {
self.metadata.used()
}

/// Returns all currently pending transitions.
pub fn pending(&self) -> impl Iterator<Item = &PendingTransition<TextureUses>> + '_ {
self.temp.iter()
}

/// Drains all currently pending transitions.
pub fn drain(&mut self) -> Drain<PendingTransition<TextureUses>> {
self.temp.drain(..)
Expand Down
6 changes: 6 additions & 0 deletions wgpu-hal/src/dx11/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,12 @@ impl crate::Queue<super::Api> for super::Queue {
}
}

impl crate::Texture<super::Api> for super::Texture {
unsafe fn is_external(&self) -> bool {
false
}
}

impl super::D3D11Device {
#[allow(trivial_casts)] // come on
pub unsafe fn check_feature_support<T>(&self, feature: d3d11::D3D11_FEATURE) -> T {
Expand Down
6 changes: 6 additions & 0 deletions wgpu-hal/src/dx12/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -793,3 +793,9 @@ impl crate::Queue<Api> for Queue {
(1_000_000_000.0 / frequency as f64) as f32
}
}

impl crate::Texture<Api> for Texture {
unsafe fn is_external(&self) -> bool {
false
}
}
6 changes: 6 additions & 0 deletions wgpu-hal/src/empty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -392,3 +392,9 @@ impl crate::CommandEncoder<Api> for Encoder {
unsafe fn dispatch(&mut self, count: [u32; 3]) {}
unsafe fn dispatch_indirect(&mut self, buffer: &Resource, offset: wgt::BufferAddress) {}
}

impl crate::Texture<Api> for Resource {
unsafe fn is_external(&self) -> bool {
false
}
}
6 changes: 6 additions & 0 deletions wgpu-hal/src/gles/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1163,6 +1163,12 @@ impl crate::Device<super::Api> for super::Device {
}
}

impl crate::Texture<super::Api> 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 {}
Expand Down
18 changes: 16 additions & 2 deletions wgpu-hal/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Self> + 'static;
type SurfaceTexture: fmt::Debug + Send + Sync + Borrow<Self::Texture>;
type TextureView: fmt::Debug + Send + Sync;
type Sampler: fmt::Debug + Send + Sync;
Expand Down Expand Up @@ -525,6 +525,13 @@ pub trait CommandEncoder<A: Api>: Send + Sync {
unsafe fn dispatch_indirect(&mut self, buffer: &A::Buffer, offset: wgt::BufferAddress);
}

pub trait Texture<A: Api>: 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 {
Expand Down Expand Up @@ -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;
}
}

Expand Down
6 changes: 6 additions & 0 deletions wgpu-hal/src/metal/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,12 @@ impl crate::Queue<Api> for Queue {
}
}

impl crate::Texture<Api> for Texture {
unsafe fn is_external(&self) -> bool {
false
}
}

#[derive(Debug)]
pub struct Buffer {
raw: mtl::Buffer,
Expand Down
34 changes: 24 additions & 10 deletions wgpu-hal/src/vulkan/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,16 +158,30 @@ impl crate::CommandEncoder<super::Api> 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() {
Expand Down
12 changes: 12 additions & 0 deletions wgpu-hal/src/vulkan/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u32>,
drop_guard: Option<super::DropGuard>,
) -> 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,
Expand All @@ -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,
}
}

Expand Down Expand Up @@ -927,6 +937,8 @@ impl crate::Device<super::Api> 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) {
Expand Down
1 change: 1 addition & 0 deletions wgpu-hal/src/vulkan/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -779,6 +779,7 @@ impl crate::Surface<super::Api> for super::Surface {
&sc.config.extent,
wgt::TextureDimension::D2,
),
external_queue_family_index: None,
},
};
Ok(Some(crate::AcquiredSurfaceTexture {
Expand Down
20 changes: 20 additions & 0 deletions wgpu-hal/src/vulkan/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u32>,
}

impl Texture {
Expand Down Expand Up @@ -657,6 +670,13 @@ impl crate::Queue<Api> for Queue {
}
}

impl crate::Texture<Api> for Texture {
unsafe fn is_external(&self) -> bool {
self.usage.contains(crate::TextureUses::EXTERNAL)
&& self.external_queue_family_index.is_some()
}
}

impl From<vk::Result> for crate::DeviceError {
fn from(result: vk::Result) -> Self {
match result {
Expand Down

0 comments on commit 9bd347b

Please sign in to comment.