Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Transition resources #6678

Merged
merged 31 commits into from
Jan 24, 2025
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
cb8acef
WIP
JMS55 Dec 6, 2024
fd4b993
Fix typo
JMS55 Dec 6, 2024
8000e37
WIP: Implement structure of command_encoder_transition_resources
JMS55 Dec 6, 2024
a441f81
WIP
JMS55 Dec 7, 2024
e19769c
More work
JMS55 Dec 7, 2024
efbb336
Clippy
JMS55 Dec 7, 2024
1897bca
Fix web build
JMS55 Dec 7, 2024
328028b
Use new types for API, more docs
JMS55 Dec 7, 2024
8fc87bb
Add very basic test
JMS55 Dec 7, 2024
8d711c9
Try to fix test cfg
JMS55 Dec 7, 2024
fea7b90
Merge commit 'a8a91737b2d2f378976e292074c75817593a0224' into transiti…
JMS55 Jan 7, 2025
dacb259
Fix merge
JMS55 Jan 8, 2025
27497ce
Merge commit '05e62f96f972981da6a5919609cc08b1c9ac8f54' into transiti…
JMS55 Jan 12, 2025
03084df
Missed commit
JMS55 Jan 12, 2025
1590a1d
Use wgt types instead of hal types
JMS55 Jan 13, 2025
fb3cb54
Implement `Clone` for `ShaderModule` (#6939)
a1phyr Jan 17, 2025
1c13ef9
Move to dispatch trait, move more things to wgt
JMS55 Jan 18, 2025
4e46c96
Move existing code to use new wgt types
JMS55 Jan 18, 2025
0e98d93
Fixes
JMS55 Jan 18, 2025
3dd0525
Merge commit 'fb3cb547fdd989f9c85b94d305fa5b6925c7529b' into transiti…
JMS55 Jan 18, 2025
08807c3
Format import
JMS55 Jan 18, 2025
8e3b178
Format another file
JMS55 Jan 18, 2025
b81c1c9
Fixes
JMS55 Jan 18, 2025
2be3395
Merge branch 'trunk' into transition_resources
cwfitzgerald Jan 23, 2025
cefa9ba
Make module private
JMS55 Jan 23, 2025
66aacba
Merge commit 'd8e7ab1ad13f2bf2f9702401d1bc625e26b1c2e6' into transiti…
JMS55 Jan 24, 2025
4e49e5d
Fix imports
JMS55 Jan 24, 2025
2f2e9dd
Fix test imports
JMS55 Jan 24, 2025
11a6d37
Rexport types
JMS55 Jan 24, 2025
a90542e
Fix imports
JMS55 Jan 24, 2025
4483082
Fix import
JMS55 Jan 24, 2025
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 @@ -120,6 +120,7 @@ By @ErichDonGubler in [#6456](https://github.com/gfx-rs/wgpu/pull/6456), [#6148]
- Return submission index in `map_async` and `on_submitted_work_done` to track down completion of async callbacks. By @eliemichel in [#6360](https://github.com/gfx-rs/wgpu/pull/6360).
- Move raytracing alignments into HAL instead of in core. By @Vecvec in [#6563](https://github.com/gfx-rs/wgpu/pull/6563).
- Allow for statically linking DXC rather than including separate `.dll` files. By @DouglasDwyer in [#6574](https://github.com/gfx-rs/wgpu/pull/6574).
- Added `CommandEncoder::transition_resources()` for native API interop, and allowing users to slightly optimize barriers. By @JMS55 in [6678](https://github.com/gfx-rs/wgpu/pull/6678).

### Changes

Expand Down
2 changes: 2 additions & 0 deletions tests/tests/root.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ mod subgroup_operations;
mod texture_bounds;
mod texture_view_creation;
mod transfer;
#[cfg(all(feature = "wgc", feature = "hal"))]
mod transition_resources;
mod vertex_formats;
mod vertex_indices;
mod write_texture;
Expand Down
39 changes: 39 additions & 0 deletions tests/tests/transition_resources.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use wgpu::{hal::TextureUses, TextureTransition};
use wgpu_test::{gpu_test, GpuTestConfiguration};
use wgt::{
CommandEncoderDescriptor, Extent3d, TextureDescriptor, TextureDimension, TextureFormat,
TextureUsages,
};

#[gpu_test]
static TRANSITION_RESOURCES: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| {
let texture = ctx.device.create_texture(&TextureDescriptor {
label: None,
size: Extent3d {
width: 32,
height: 32,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: TextureDimension::D2,
format: TextureFormat::Rgba8Unorm,
usage: TextureUsages::RENDER_ATTACHMENT | TextureUsages::TEXTURE_BINDING,
view_formats: &[],
});

let mut encoder = ctx
.device
.create_command_encoder(&CommandEncoderDescriptor { label: None });

encoder.transition_resources(
&[],
&[TextureTransition {
texture: &texture,
selector: None,
state: TextureUses::COLOR_TARGET,
}],
);

ctx.queue.submit([encoder.finish()]);
});
1 change: 1 addition & 0 deletions wgpu-core/src/command/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ mod render;
mod render_command;
mod timestamp_writes;
mod transfer;
mod transition_resources;

use std::mem::{self, ManuallyDrop};
use std::sync::Arc;
Expand Down
87 changes: 87 additions & 0 deletions wgpu-core/src/command/transition_resources.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
use hal::{BufferUses, TextureUses};
use thiserror::Error;

use crate::{
command::CommandBuffer,
device::DeviceError,
global::Global,
id::{BufferId, CommandEncoderId, TextureId},
resource::{InvalidResourceError, ParentDevice},
track::{ResourceUsageCompatibilityError, TextureSelector},
};

use super::CommandEncoderError;

impl Global {
pub fn command_encoder_transition_resources(
&self,
command_encoder_id: CommandEncoderId,
buffer_transitions: impl Iterator<Item = (BufferId, BufferUses)>,
texture_transitions: impl Iterator<Item = (TextureId, Option<TextureSelector>, TextureUses)>,
) -> Result<(), TransitionResourcesError> {
profiling::scope!("CommandEncoder::transition_resources");

let hub = &self.hub;

// Lock command encoder for recording
let cmd_buf = hub
.command_buffers
.get(command_encoder_id.into_command_buffer_id());
let mut cmd_buf_data = cmd_buf.data.lock();
let mut cmd_buf_data_guard = cmd_buf_data.record()?;
let cmd_buf_data = &mut *cmd_buf_data_guard;

// Get and lock device
let device = &cmd_buf.device;
device.check_is_valid()?;
let snatch_guard = &device.snatchable_lock.read();

let mut usage_scope = device.new_usage_scope();

// Process buffer transitions
for (buffer_id, state) in buffer_transitions {
let buffer = hub.buffers.get(buffer_id).get()?;
buffer.same_device_as(cmd_buf.as_ref())?;

usage_scope.buffers.merge_single(&buffer, state)?;
}

// Process texture transitions
for (texture_id, selector, state) in texture_transitions {
let texture = hub.textures.get(texture_id).get()?;
texture.same_device_as(cmd_buf.as_ref())?;

unsafe {
usage_scope
.textures
.merge_single(&texture, selector.clone(), state)
}?;
}
JMS55 marked this conversation as resolved.
Show resolved Hide resolved

// Record any needed barriers based on tracker data
let cmd_buf_raw = cmd_buf_data.encoder.open(device)?;
CommandBuffer::insert_barriers_from_scope(
cmd_buf_raw,
&mut cmd_buf_data.trackers,
&usage_scope,
snatch_guard,
);
cmd_buf_data_guard.mark_successful();

Ok(())
}
}

/// Error encountered while attempting to perform [`Global::command_encoder_transition_resources`].
#[derive(Clone, Debug, Error)]
#[non_exhaustive]
pub enum TransitionResourcesError {
#[error(transparent)]
Device(#[from] DeviceError),
#[error(transparent)]
Encoder(#[from] CommandEncoderError),
#[error(transparent)]
InvalidResource(#[from] InvalidResourceError),
#[error(transparent)]
ResourceUsage(#[from] ResourceUsageCompatibilityError),
}
1 change: 1 addition & 0 deletions wgpu-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ pub mod validation;

pub use hal::{api, MAX_BIND_GROUPS, MAX_COLOR_ATTACHMENTS, MAX_VERTEX_BUFFERS};
pub use naga;
pub use track::texture::TextureSelector;

use std::{borrow::Cow, os::raw::c_char};

Expand Down
4 changes: 2 additions & 2 deletions wgpu-core/src/track/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ mod metadata;
mod range;
mod ray_tracing;
mod stateless;
mod texture;
pub(crate) mod texture;
JMS55 marked this conversation as resolved.
Show resolved Hide resolved

use crate::{
binding_model, command,
Expand Down Expand Up @@ -643,7 +643,7 @@ impl Tracker {
/// bind group as a source of which IDs to look at. The bind groups
/// must have first been added to the usage scope.
///
/// Only stateful things are merged in herell other resources are owned
/// Only stateful things are merged in here, all other resources are owned
/// indirectly by the bind group.
///
/// # Safety
Expand Down
82 changes: 82 additions & 0 deletions wgpu/src/api/command_encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -346,4 +346,86 @@ impl CommandEncoder {
&mut tlas.into_iter(),
);
}

/// Transition resources to an underlying hal resource state.
///
/// This is an advanced, native-only API that has two main use cases:
///
/// # Batching Barriers
///
/// Wgpu does not have a global view of the frame when recording command buffers. When you submit multiple command buffers in a single queue submission, wgpu may need to record and
/// insert new command buffers (holding 1 or more barrier commands) in between the user-supplied command buffers in order to ensure that resources are transitioned to the correct state
/// for the start of the next user-supplied command buffer.
///
/// Wgpu does not currently attempt to batch multiple of these generated command buffers/barriers together, which may lead to suboptimal barrier placement.
///
/// Consider the following scenario, where the user does `queue.submit(&[a, b, c])`:
/// * CommandBuffer A: Use resource X as a render pass attachment
/// * CommandBuffer B: Use resource Y as a render pass attachment
/// * CommandBuffer C: Use resources X and Y in a bind group
///
/// At submission time, wgpu will record and insert some new command buffers, resulting in a submission that looks like `queue.submit(&[0, a, 1, b, 2, c])`:
/// * CommandBuffer 0: Barrier to transition resource X from TextureUses::RESOURCE (from last frame) to TextureUses::COLOR_TARGET
/// * CommandBuffer A: Use resource X as a render pass attachment
/// * CommandBuffer 1: Barrier to transition resource Y from TextureUses::RESOURCE (from last frame) to TextureUses::COLOR_TARGET
/// * CommandBuffer B: Use resource Y as a render pass attachment
/// * CommandBuffer 2: Barrier to transition resources X and Y from TextureUses::COLOR_TARGET to TextureUses::RESOURCE
/// * CommandBuffer C: Use resources X and Y in a bind group
///
/// To prevent this, after profiling their app, an advanced user might choose to instead do `queue.submit(&[a, b, c])`:
/// * CommandBuffer A:
/// * Use [`CommandEncoder::transition_resources`] to transition resources X and Y from TextureUses::RESOURCE (from last frame) to TextureUses::COLOR_TARGET
/// * Use resource X as a render pass attachment
/// * CommandBuffer B: Use resource Y as a render pass attachment
/// * CommandBuffer C:
/// * Use [`CommandEncoder::transition_resources`] to transition resources X and Y from TextureUses::COLOR_TARGET to TextureUses::RESOURCE
/// * Use resources X and Y in a bind group
///
/// At submission time, wgpu will record and insert some new command buffers, resulting in a submission that looks like `queue.submit(&[0, a, b, 1, c])`:
/// * CommandBuffer 0: Barrier to transition resources X and Y from TextureUses::RESOURCE (from last frame) to TextureUses::COLOR_TARGET
/// * CommandBuffer A: Use resource X as a render pass attachment
/// * CommandBuffer B: Use resource Y as a render pass attachment
/// * CommandBuffer 1: Barrier to transition resources X and Y from TextureUses::COLOR_TARGET to TextureUses::RESOURCE
/// * CommandBuffer C: Use resources X and Y in a bind group
Comment on lines +379 to +387
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is CommandBuffer 1 still separated out? This was described as adding the resource transition to RESOURCE manually as part of CommandBuffer C, therefore wgpu should see COLOR_TARGET as expected start state of C.

If this is another limitation we have today it would be good to capture this here. Also, the transition_resources call in C is not doing anything then.

Copy link
Collaborator Author

@JMS55 JMS55 Dec 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh whoops, I think that's a mistake. I don't actually know what will happen with the second transition_resources() call.

Tbh how wgpu's barrier tracking system works still confuses me. Like the fact that it's still inserting CommandBuffer 0 to begin with. I was hoping it could just be a part of A directly.

Can you adjust the docs for me to match how this is supposed to work?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm yeah actually CommandBuffer 0 shouldn't be there either 🤔
Let's wait for @cwfitzgerald to have a look - my knowledge of the tracker is quite shaky as well.

///
/// Which eliminates the extra command buffer and barrier between command buffers A and B.
///
/// # Native Interoperability
///
/// A user wanting to interoperate with the underlying native graphics APIs (Vulkan, DirectX12, Metal, etc) can use this API to generate barriers between wgpu commands and
/// the native API commands, for synchronization and resource state transition purposes.
#[cfg(wgpu_core)]
pub fn transition_resources(
&mut self,
buffer_transitions: &[BufferTransition<'_>],
texture_transitions: &[TextureTransition<'_>],
) {
if let Some(encoder) = self.inner.as_core_mut_opt() {
encoder.transition_resources(buffer_transitions, texture_transitions);
}
}
}

/// A buffer transition for use with [`CommandEncoder::transition_resources`].
#[cfg(wgpu_core)]
#[derive(Debug)]
pub struct BufferTransition<'a> {
/// The buffer to transition.
pub buffer: &'a Buffer,
/// The new state to transition to.
pub state: hal::BufferUses,
}

/// A texture transition for use with [`CommandEncoder::transition_resources`].
#[cfg(wgpu_core)]
#[derive(Debug)]
pub struct TextureTransition<'a> {
/// The texture to transition.
pub texture: &'a Texture,
/// An optional selector to transition only part of the texture.
///
/// If None, the entire texture will be transitioned.
pub selector: Option<wgc::TextureSelector>,
/// The new state to transition to.
pub state: hal::TextureUses,
JMS55 marked this conversation as resolved.
Show resolved Hide resolved
}
26 changes: 26 additions & 0 deletions wgpu/src/backend/wgpu_core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2551,6 +2551,32 @@ impl dispatch::CommandEncoderInterface for CoreCommandEncoder {
}
}

impl CoreCommandEncoder {
pub fn transition_resources(
&mut self,
buffer_transitions: &[crate::BufferTransition<'_>],
texture_transitions: &[crate::TextureTransition<'_>],
) {
let result = self.context.0.command_encoder_transition_resources(
self.id,
buffer_transitions
.iter()
.map(|t| (t.buffer.inner.as_core().id, t.state)),
texture_transitions
.iter()
.map(|t| (t.texture.inner.as_core().id, t.selector.clone(), t.state)),
);

if let Err(cause) = result {
self.context.handle_error_nolabel(
&self.error_sink,
cause,
"CommandEncoder::transition_resources",
);
}
}
}

impl Drop for CoreCommandEncoder {
fn drop(&mut self) {
if self.open {
Expand Down
Loading