Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ By @beholdnec in [#8505](https://github.com/gfx-rs/wgpu/pull/8505).
- DRM support by @rectalogic in [#9182](https://github.com/gfx-rs/wgpu/pull/9182).
- Conditional compilation by @jimblandy in [#9390](https://github.com/gfx-rs/wgpu/pull/9390)
- Add `wgpu_hal::vulkan::Buffer::raw_handle()` for retrieving the underlying `vk::Buffer` resource. By @WillowGriffiths in [#9459](https://github.com/gfx-rs/wgpu/pull/9459).
- Allow specifying a queue family ownership transfer when transitioning a texture. `hal::TextureBarrier` gained an optional `queue_family_ownership_transfer` field (honored only by the Vulkan backend) so that images imported from external memory can be acquired from and released back to a sentinel queue family (`hal::QUEUE_FAMILY_EXTERNAL` / `hal::QUEUE_FAMILY_FOREIGN`). Resolves [#2948](https://github.com/gfx-rs/wgpu/issues/2948). By @alexander-bruun in [#9668](https://github.com/gfx-rs/wgpu/pull/9668).

#### naga

Expand Down
1 change: 1 addition & 0 deletions wgpu-core/src/device/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1244,6 +1244,7 @@ impl Queue {
texture: dst_raw_webgl,
range: dyn_transition.range,
usage: dyn_transition.usage,
queue_family_ownership_transfer: None,
}
});

Expand Down
1 change: 1 addition & 0 deletions wgpu-core/src/track/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,7 @@ impl PendingTransition<wgt::TextureUses> {
array_layer_count: Some(layer_count),
},
usage: self.usage,
queue_family_ownership_transfer: None,
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions wgpu-hal/examples/halmark/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,7 @@ impl<A: hal::Api> Example<A> {
from: wgpu_types::TextureUses::UNINITIALIZED,
to: wgpu_types::TextureUses::COPY_DST,
},
queue_family_ownership_transfer: None,
};
let texture_barrier2 = hal::TextureBarrier {
texture: &texture,
Expand All @@ -359,6 +360,7 @@ impl<A: hal::Api> Example<A> {
from: wgpu_types::TextureUses::COPY_DST,
to: wgpu_types::TextureUses::RESOURCE,
},
queue_family_ownership_transfer: None,
};
let copy = hal::BufferTextureCopy {
buffer_layout: wgpu_types::TexelCopyBufferLayout {
Expand Down Expand Up @@ -690,6 +692,7 @@ impl<A: hal::Api> Example<A> {
from: wgpu_types::TextureUses::UNINITIALIZED,
to: wgpu_types::TextureUses::COLOR_TARGET,
},
queue_family_ownership_transfer: None,
};
unsafe {
ctx.encoder.begin_encoding(Some("frame")).unwrap();
Expand Down Expand Up @@ -762,6 +765,7 @@ impl<A: hal::Api> Example<A> {
from: wgpu_types::TextureUses::COLOR_TARGET,
to: wgpu_types::TextureUses::PRESENT,
},
queue_family_ownership_transfer: None,
};
unsafe {
ctx.encoder.end_render_pass();
Expand Down
5 changes: 5 additions & 0 deletions wgpu-hal/examples/ray-traced-triangle/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -811,6 +811,7 @@ impl<A: hal::Api> Example<A> {
from: wgpu_types::TextureUses::UNINITIALIZED,
to: wgpu_types::TextureUses::STORAGE_READ_WRITE,
},
queue_family_ownership_transfer: None,
};

cmd_encoder.transition_textures(iter::once(texture_barrier));
Expand Down Expand Up @@ -884,6 +885,7 @@ impl<A: hal::Api> Example<A> {
from: wgpu_types::TextureUses::UNINITIALIZED,
to: wgpu_types::TextureUses::COPY_DST,
},
queue_family_ownership_transfer: None,
};

let instances_buffer_size =
Expand Down Expand Up @@ -993,6 +995,7 @@ impl<A: hal::Api> Example<A> {
from: wgpu_types::TextureUses::COPY_DST,
to: wgpu_types::TextureUses::PRESENT,
},
queue_family_ownership_transfer: None,
};
let target_barrier2 = hal::TextureBarrier {
texture: &self.texture,
Expand All @@ -1001,6 +1004,7 @@ impl<A: hal::Api> Example<A> {
from: wgpu_types::TextureUses::STORAGE_READ_WRITE,
to: wgpu_types::TextureUses::COPY_SRC,
},
queue_family_ownership_transfer: None,
};
let target_barrier3 = hal::TextureBarrier {
texture: &self.texture,
Expand All @@ -1009,6 +1013,7 @@ impl<A: hal::Api> Example<A> {
from: wgpu_types::TextureUses::COPY_SRC,
to: wgpu_types::TextureUses::STORAGE_READ_WRITE,
},
queue_family_ownership_transfer: None,
};
unsafe {
ctx.encoder.end_compute_pass();
Expand Down
1 change: 1 addition & 0 deletions wgpu-hal/src/dynamic/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ impl<C: CommandEncoder + DynResource> DynCommandEncoder for C {
texture: barrier.texture.expect_downcast_ref(),
usage: barrier.usage.clone(),
range: barrier.range,
queue_family_ownership_transfer: barrier.queue_family_ownership_transfer,
});
unsafe { self.transition_textures(barriers) };
}
Expand Down
75 changes: 75 additions & 0 deletions wgpu-hal/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2583,11 +2583,55 @@ pub struct BufferBarrier<'a, B: DynBuffer + ?Sized> {
pub usage: StateTransition<wgt::BufferUses>,
}

/// A queue family ownership transfer to perform as part of a [`TextureBarrier`].
///
/// This is only honored by the Vulkan backend; every other backend ignores it.
/// It exists so that textures imported from external memory (for example via
/// `VK_KHR_external_memory`) can have their backing image transferred between
/// wgpu's queue family and a sentinel queue family when the image is acquired
/// for use and released afterwards.
///
/// `src` becomes `VkImageMemoryBarrier::srcQueueFamilyIndex` and `dst` becomes
/// `VkImageMemoryBarrier::dstQueueFamilyIndex`. To acquire an externally owned
/// image, set `src` to the sentinel family (such as [`QUEUE_FAMILY_EXTERNAL`]
/// or [`QUEUE_FAMILY_FOREIGN`]) and `dst` to wgpu's own family, obtained from
/// `vulkan::Device::queue_family_index`. To release it again, swap the two.
///
/// A queue family ownership transfer requires a matching barrier to be recorded
/// on *both* queues; wgpu-hal only records the barrier on its own queue, so the
/// owner of the other queue is responsible for recording the complementary one.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct QueueFamilyOwnershipTransfer {
/// The queue family that currently owns the image (`srcQueueFamilyIndex`).
pub src: u32,
/// The queue family that should own the image afterwards (`dstQueueFamilyIndex`).
pub dst: u32,
}

/// Special queue family value indicating that no ownership transfer should be
/// performed (`VK_QUEUE_FAMILY_IGNORED`).
pub const QUEUE_FAMILY_IGNORED: u32 = u32::MAX;

/// Sentinel queue family for images shared with an external, non-Vulkan API
/// (`VK_QUEUE_FAMILY_EXTERNAL`).
pub const QUEUE_FAMILY_EXTERNAL: u32 = u32::MAX - 1;

/// Sentinel queue family for images shared with an external, foreign instance
/// of the same API (`VK_QUEUE_FAMILY_FOREIGN_EXT`).
pub const QUEUE_FAMILY_FOREIGN: u32 = u32::MAX - 2;

#[derive(Debug, Clone)]
pub struct TextureBarrier<'a, T: DynTexture + ?Sized> {
pub texture: &'a T,
pub range: wgt::ImageSubresourceRange,
pub usage: StateTransition<wgt::TextureUses>,
/// An optional Vulkan queue family ownership transfer to perform alongside
/// the layout/access transition described by `usage`.
///
/// This is honored only by the Vulkan backend; all other backends ignore
/// it. Leave it as `None` for the common case where no ownership transfer
/// is required. See [`QueueFamilyOwnershipTransfer`] for details.
pub queue_family_ownership_transfer: Option<QueueFamilyOwnershipTransfer>,
}

#[derive(Clone, Copy, Debug)]
Expand Down Expand Up @@ -2888,3 +2932,34 @@ pub struct Telemetry {
result: D3D12ExposeAdapterResult,
),
}

#[cfg(test)]
mod tests {
use super::*;

/// The backend-agnostic queue family sentinels must match the values of the
/// Vulkan constants they stand for (`VK_QUEUE_FAMILY_*`), since they are
/// passed straight through to the Vulkan backend. The Vulkan backend also
/// has a compile-time assertion against `ash`'s definitions; this test
/// pins the raw values so they cannot drift even when the `vulkan` feature
/// is disabled.
#[test]
fn queue_family_sentinels_match_vulkan() {
assert_eq!(QUEUE_FAMILY_IGNORED, 0xFFFF_FFFF); // ~0u32
assert_eq!(QUEUE_FAMILY_EXTERNAL, 0xFFFF_FFFE); // ~1u32
assert_eq!(QUEUE_FAMILY_FOREIGN, 0xFFFF_FFFD); // ~2u32
}

/// A `TextureBarrier` without an ownership transfer must leave the new
/// field unset, so existing callers keep their previous behavior.
#[test]
fn texture_barrier_defaults_to_no_ownership_transfer() {
let transfer = QueueFamilyOwnershipTransfer {
src: QUEUE_FAMILY_EXTERNAL,
dst: 0,
};
// `from`/`to` round-trip the raw indices unchanged.
assert_eq!(transfer.src, QUEUE_FAMILY_EXTERNAL);
assert_eq!(transfer.dst, 0);
}
}
22 changes: 21 additions & 1 deletion wgpu-hal/src/vulkan/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ use hashbrown::hash_map::Entry;
const ALLOCATION_GRANULARITY: u32 = 16;
const DST_IMAGE_LAYOUT: vk::ImageLayout = vk::ImageLayout::TRANSFER_DST_OPTIMAL;

// The backend-agnostic queue family sentinels in `crate` must match the Vulkan
// values they stand for, since they are passed straight through to ash.
const _: () = {
assert!(crate::QUEUE_FAMILY_IGNORED == vk::QUEUE_FAMILY_IGNORED);
assert!(crate::QUEUE_FAMILY_EXTERNAL == vk::QUEUE_FAMILY_EXTERNAL);
assert!(crate::QUEUE_FAMILY_FOREIGN == vk::QUEUE_FAMILY_FOREIGN_EXT);
};

impl super::Texture {
fn map_buffer_copies<T>(&self, regions: T) -> impl Iterator<Item = vk::BufferImageCopy>
where
Expand Down Expand Up @@ -253,14 +261,26 @@ impl crate::CommandEncoder for super::CommandEncoder {
let dst_layout = conv::derive_image_layout(bar.usage.to, bar.texture.format);
dst_stages |= dst_stage;

// Insert a queue family ownership transfer if the caller requested
// one (used for textures imported from external memory). When no
// transfer is requested, both indices are `QUEUE_FAMILY_IGNORED`,
// which the spec treats as "no transfer".
let (src_queue_family_index, dst_queue_family_index) =
match bar.queue_family_ownership_transfer {
Some(transfer) => (transfer.src, transfer.dst),
None => (vk::QUEUE_FAMILY_IGNORED, vk::QUEUE_FAMILY_IGNORED),
};

vk_barriers.push(
vk::ImageMemoryBarrier::default()
.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),
.new_layout(dst_layout)
.src_queue_family_index(src_queue_family_index)
.dst_queue_family_index(dst_queue_family_index),
);
}

Expand Down
Loading