Encode commands on finish (#8220)

Co-authored-by: Andreas Reich <r_andreas2@web.de>
This commit is contained in:
Andy Leiserson 2025-09-25 11:22:51 -07:00 committed by GitHub
parent 990aef977e
commit 1967900565
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 1373 additions and 1001 deletions

View File

@ -170,6 +170,7 @@ By @cwfitzgerald in [#8162](https://github.com/gfx-rs/wgpu/pull/8162).
#### General
- Command encoding now happens when `CommandEncoder::finish` is called, not when the individual operations are requested. This does not affect the API, but may affect performance characteristics. By @andyleiserson in [#8220](https://github.com/gfx-rs/wgpu/pull/8220).
- Prevent resources for acceleration structures being created if acceleration structures are not enabled. By @Vecvec in [#8036](https://github.com/gfx-rs/wgpu/pull/8036).
- Validate that each `push_debug_group` pairs with exactly one `pop_debug_group`. By @andyleiserson in [#8048](https://github.com/gfx-rs/wgpu/pull/8048).
- `set_viewport` now requires that the supplied minimum depth value is less than the maximum depth value. By @andyleiserson in [#8040](https://github.com/gfx-rs/wgpu/pull/8040).

View File

@ -140,7 +140,7 @@ impl GPUQueue {
#[webidl] data_layout: GPUTexelCopyBufferLayout,
#[webidl] size: GPUExtent3D,
) {
let destination = wgpu_core::command::TexelCopyTextureInfo {
let destination = wgpu_types::TexelCopyTextureInfo {
texture: destination.texture.id,
mip_level: destination.mip_level,
origin: destination.origin.into(),

View File

@ -1,7 +1,5 @@
use wgpu::{util::DeviceExt, Backends};
use wgpu_test::{
fail, gpu_test, FailureCase, GpuTestConfiguration, GpuTestInitializer, TestParameters,
};
use wgpu::util::DeviceExt;
use wgpu_test::{fail, gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters};
pub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {
vec.extend([
@ -118,12 +116,7 @@ static TEXTURE_DESTROY: GpuTestConfiguration = GpuTestConfiguration::new()
// submission fails gracefully.
#[gpu_test]
static BUFFER_DESTROY_BEFORE_SUBMIT: GpuTestConfiguration = GpuTestConfiguration::new()
.parameters(
// https://github.com/gfx-rs/wgpu/issues/7854
TestParameters::default()
.skip(FailureCase::backend_adapter(Backends::VULKAN, "llvmpipe"))
.enable_noop(),
)
.parameters(TestParameters::default().enable_noop())
.run_sync(|ctx| {
let buffer_source = ctx
.device
@ -160,12 +153,7 @@ static BUFFER_DESTROY_BEFORE_SUBMIT: GpuTestConfiguration = GpuTestConfiguration
// submission fails gracefully.
#[gpu_test]
static TEXTURE_DESTROY_BEFORE_SUBMIT: GpuTestConfiguration = GpuTestConfiguration::new()
.parameters(
// https://github.com/gfx-rs/wgpu/issues/7854
TestParameters::default()
.skip(FailureCase::backend_adapter(Backends::VULKAN, "llvmpipe"))
.enable_noop(),
)
.parameters(TestParameters::default().enable_noop())
.run_sync(|ctx| {
let descriptor = wgpu::TextureDescriptor {
label: None,

View File

@ -5,7 +5,7 @@ use core::ops::Range;
use crate::command::Command as TraceCommand;
use crate::{
api_log,
command::{CommandBufferMutable, CommandEncoder, EncoderStateError},
command::{encoder::EncodingState, ArcCommand, EncoderStateError},
device::{DeviceError, MissingFeatures},
get_lowest_common_denom,
global::Global,
@ -13,7 +13,7 @@ use crate::{
id::{BufferId, CommandEncoderId, TextureId},
init_tracker::{MemoryInitKind, TextureInitRange},
resource::{
DestroyedResourceError, InvalidResourceError, Labeled, MissingBufferUsageError,
Buffer, DestroyedResourceError, InvalidResourceError, Labeled, MissingBufferUsageError,
ParentDevice, RawResourceAccess, ResourceErrorIdent, Texture, TextureClearMode,
},
snatch::SnatchGuard,
@ -118,8 +118,18 @@ impl Global {
let cmd_enc = hub.command_encoders.get(command_encoder_id);
let mut cmd_buf_data = cmd_enc.data.lock();
cmd_buf_data.record_with(|cmd_buf_data| -> Result<(), ClearError> {
clear_buffer(cmd_buf_data, hub, &cmd_enc, dst, offset, size)
#[cfg(feature = "trace")]
if let Some(ref mut list) = cmd_buf_data.trace() {
list.push(TraceCommand::ClearBuffer { dst, offset, size });
}
cmd_buf_data.push_with(|| -> Result<_, ClearError> {
Ok(ArcCommand::ClearBuffer {
dst: self.resolve_buffer_id(dst)?,
offset,
size,
})
})
}
@ -136,38 +146,38 @@ impl Global {
let cmd_enc = hub.command_encoders.get(command_encoder_id);
let mut cmd_buf_data = cmd_enc.data.lock();
cmd_buf_data.record_with(|cmd_buf_data| -> Result<(), ClearError> {
clear_texture_cmd(cmd_buf_data, hub, &cmd_enc, dst, subresource_range)
#[cfg(feature = "trace")]
if let Some(ref mut list) = cmd_buf_data.trace() {
list.push(TraceCommand::ClearTexture {
dst,
subresource_range: *subresource_range,
});
}
cmd_buf_data.push_with(|| -> Result<_, ClearError> {
Ok(ArcCommand::ClearTexture {
dst: self.resolve_texture_id(dst)?,
subresource_range: *subresource_range,
})
})
}
}
pub(super) fn clear_buffer(
cmd_buf_data: &mut CommandBufferMutable,
hub: &crate::hub::Hub,
cmd_enc: &Arc<CommandEncoder>,
dst: BufferId,
state: &mut EncodingState,
dst_buffer: Arc<Buffer>,
offset: BufferAddress,
size: Option<BufferAddress>,
) -> Result<(), ClearError> {
#[cfg(feature = "trace")]
if let Some(ref mut list) = cmd_buf_data.trace_commands {
list.push(TraceCommand::ClearBuffer { dst, offset, size });
}
dst_buffer.same_device(state.device)?;
cmd_enc.device.check_is_valid()?;
let dst_buffer = hub.buffers.get(dst).get()?;
dst_buffer.same_device_as(cmd_enc.as_ref())?;
let dst_pending = cmd_buf_data
.trackers
let dst_pending = state
.tracker
.buffers
.set_single(&dst_buffer, wgt::BufferUses::COPY_DST);
let snatch_guard = dst_buffer.device.snatchable_lock.read();
let dst_raw = dst_buffer.try_raw(&snatch_guard)?;
let dst_raw = dst_buffer.try_raw(state.snatch_guard)?;
dst_buffer.check_usage(BufferUsages::COPY_DST)?;
// Check if offset & size are valid.
@ -200,20 +210,19 @@ pub(super) fn clear_buffer(
}
// Mark dest as initialized.
cmd_buf_data.buffer_memory_init_actions.extend(
dst_buffer.initialization_status.read().create_action(
state
.buffer_memory_init_actions
.extend(dst_buffer.initialization_status.read().create_action(
&dst_buffer,
offset..end_offset,
MemoryInitKind::ImplicitlyInitialized,
),
);
));
// actual hal barrier & operation
let dst_barrier = dst_pending.map(|pending| pending.into_hal(&dst_buffer, &snatch_guard));
let cmd_buf_raw = cmd_buf_data.encoder.open()?;
let dst_barrier = dst_pending.map(|pending| pending.into_hal(&dst_buffer, state.snatch_guard));
unsafe {
cmd_buf_raw.transition_buffers(dst_barrier.as_slice());
cmd_buf_raw.clear_buffer(dst_raw, offset..end_offset);
state.raw_encoder.transition_buffers(dst_barrier.as_slice());
state.raw_encoder.clear_buffer(dst_raw, offset..end_offset);
}
Ok(())
@ -227,30 +236,15 @@ pub(super) fn clear_buffer(
/// this function, is a lower-level function that encodes a texture clear
/// operation without validating it.
pub(super) fn clear_texture_cmd(
cmd_buf_data: &mut CommandBufferMutable,
hub: &crate::hub::Hub,
cmd_enc: &Arc<CommandEncoder>,
dst: TextureId,
state: &mut EncodingState,
dst_texture: Arc<Texture>,
subresource_range: &ImageSubresourceRange,
) -> Result<(), ClearError> {
#[cfg(feature = "trace")]
if let Some(ref mut list) = cmd_buf_data.trace_commands {
list.push(TraceCommand::ClearTexture {
dst,
subresource_range: *subresource_range,
});
}
cmd_enc.device.check_is_valid()?;
cmd_enc
dst_texture.same_device(state.device)?;
state
.device
.require_features(wgt::Features::CLEAR_TEXTURE)?;
let dst_texture = hub.textures.get(dst).get()?;
dst_texture.same_device_as(cmd_enc.as_ref())?;
// Check if subresource aspects are valid.
let clear_aspects = hal::FormatAspects::new(dst_texture.desc.format, subresource_range.aspect);
if clear_aspects.is_empty() {
@ -283,23 +277,18 @@ pub(super) fn clear_texture_cmd(
});
}
let device = &cmd_enc.device;
device.check_is_valid()?;
let (encoder, tracker) = cmd_buf_data.open_encoder_and_tracker()?;
let snatch_guard = device.snatchable_lock.read();
clear_texture(
&dst_texture,
TextureInitRange {
mip_range: subresource_mip_range,
layer_range: subresource_layer_range,
},
encoder,
&mut tracker.textures,
&device.alignments,
device.zero_buffer.as_ref(),
&snatch_guard,
device.instance_flags,
state.raw_encoder,
&mut state.tracker.textures,
&state.device.alignments,
state.device.zero_buffer.as_ref(),
state.snatch_guard,
state.device.instance_flags,
)?;
Ok(())

View File

@ -5,13 +5,8 @@ use wgt::{
};
use alloc::{borrow::Cow, boxed::Box, sync::Arc, vec::Vec};
use core::{fmt, str};
use core::{convert::Infallible, fmt, str};
use crate::command::{
encoder::EncodingState, pass, CommandBufferMutable, CommandEncoder, DebugGroupError,
EncoderStateError, PassStateError, TimestampWritesError,
};
use crate::resource::DestroyedResourceError;
use crate::{binding_model::BindError, resource::RawResourceAccess};
use crate::{
binding_model::{LateMinBufferBindingSizeMismatch, PushConstantUploadError},
@ -35,6 +30,14 @@ use crate::{
track::{ResourceUsageCompatibilityError, Tracker, TrackerIndex},
Label,
};
use crate::{command::InnerCommandEncoder, resource::DestroyedResourceError};
use crate::{
command::{
encoder::EncodingState, pass, ArcCommand, CommandEncoder, DebugGroupError,
EncoderStateError, PassStateError, TimestampWritesError,
},
device::Device,
};
pub type ComputeBasePass = BasePass<ArcComputeCommand, ComputePassError>;
@ -254,10 +257,10 @@ impl WebGpuError for ComputePassError {
}
}
struct State<'scope, 'snatch_guard, 'cmd_enc, 'raw_encoder> {
struct State<'scope, 'snatch_guard, 'cmd_enc> {
pipeline: Option<Arc<ComputePipeline>>,
pass: pass::PassState<'scope, 'snatch_guard, 'cmd_enc, 'raw_encoder>,
pass: pass::PassState<'scope, 'snatch_guard, 'cmd_enc>,
active_query: Option<(Arc<resource::QuerySet>, u32)>,
@ -266,9 +269,7 @@ struct State<'scope, 'snatch_guard, 'cmd_enc, 'raw_encoder> {
intermediate_trackers: Tracker,
}
impl<'scope, 'snatch_guard, 'cmd_enc, 'raw_encoder>
State<'scope, 'snatch_guard, 'cmd_enc, 'raw_encoder>
{
impl<'scope, 'snatch_guard, 'cmd_enc> State<'scope, 'snatch_guard, 'cmd_enc> {
fn is_ready(&self) -> Result<(), DispatchError> {
if let Some(pipeline) = self.pipeline.as_ref() {
self.pass.binder.check_compatibility(pipeline.as_ref())?;
@ -428,7 +429,7 @@ impl Global {
pub fn compute_pass_end_with_unresolved_commands(
&self,
encoder_id: id::CommandEncoderId,
base: BasePass<super::ComputeCommand, core::convert::Infallible>,
base: BasePass<super::ComputeCommand, Infallible>,
timestamp_writes: Option<&PassTimestampWrites>,
) {
#[cfg(feature = "trace")]
@ -494,57 +495,58 @@ impl Global {
let cmd_enc = pass.parent.take().ok_or(EncoderStateError::Ended)?;
let mut cmd_buf_data = cmd_enc.data.lock();
if let Some(err) = pass.base.error.take() {
if matches!(
err,
ComputePassError {
inner: ComputePassErrorInner::EncoderState(EncoderStateError::Ended),
scope: _,
}
) {
// If the encoder was already finished at time of pass creation,
// then it was not put in the locked state, so we need to
// generate a validation error here due to the encoder not being
// locked. The encoder already has a copy of the error.
return Err(EncoderStateError::Ended);
} else {
// If the pass is invalid, invalidate the parent encoder and return.
// Since we do not track the state of an invalid encoder, it is not
// necessary to unlock it.
cmd_buf_data.invalidate(err);
return Ok(());
}
cmd_buf_data.unlock_encoder()?;
let base = pass.base.take();
if matches!(
base,
Err(ComputePassError {
inner: ComputePassErrorInner::EncoderState(EncoderStateError::Ended),
scope: _,
})
) {
// If the encoder was already finished at time of pass creation,
// then it was not put in the locked state, so we need to
// generate a validation error here and now due to the encoder not
// being locked. The encoder already holds an error from when the
// pass was opened, or earlier.
//
// All other errors are propagated to the encoder within `push_with`,
// and will be reported later.
return Err(EncoderStateError::Ended);
}
cmd_buf_data.unlock_and_record(|cmd_buf_data| -> Result<(), ComputePassError> {
encode_compute_pass(cmd_buf_data, &cmd_enc, pass)
cmd_buf_data.push_with(|| -> Result<_, ComputePassError> {
Ok(ArcCommand::RunComputePass {
pass: base?,
timestamp_writes: pass.timestamp_writes.take(),
})
})
}
}
fn encode_compute_pass(
cmd_buf_data: &mut CommandBufferMutable,
cmd_enc: &Arc<CommandEncoder>,
pass: &mut ComputePass,
pub(super) fn encode_compute_pass(
parent_state: &mut EncodingState<InnerCommandEncoder>,
mut base: BasePass<ArcComputeCommand, Infallible>,
mut timestamp_writes: Option<ArcPassTimestampWrites>,
) -> Result<(), ComputePassError> {
let pass_scope = PassErrorScope::Pass;
let device = &cmd_enc.device;
device.check_is_valid().map_pass_err(pass_scope)?;
let base = &mut pass.base;
let encoder = &mut cmd_buf_data.encoder;
let device = parent_state.device;
// We automatically keep extending command buffers over time, and because
// we want to insert a command buffer _before_ what we're about to record,
// we need to make sure to close the previous one.
encoder.close_if_open().map_pass_err(pass_scope)?;
let raw_encoder = encoder
parent_state
.raw_encoder
.close_if_open()
.map_pass_err(pass_scope)?;
let raw_encoder = parent_state
.raw_encoder
.open_pass(base.label.as_deref())
.map_pass_err(pass_scope)?;
let snatch_guard = device.snatchable_lock.read();
let mut debug_scope_depth = 0;
let mut state = State {
@ -554,23 +556,20 @@ fn encode_compute_pass(
base: EncodingState {
device,
raw_encoder,
tracker: &mut cmd_buf_data.trackers,
buffer_memory_init_actions: &mut cmd_buf_data.buffer_memory_init_actions,
texture_memory_actions: &mut cmd_buf_data.texture_memory_actions,
as_actions: &mut cmd_buf_data.as_actions,
indirect_draw_validation_resources: &mut cmd_buf_data
.indirect_draw_validation_resources,
snatch_guard: &snatch_guard,
tracker: parent_state.tracker,
buffer_memory_init_actions: parent_state.buffer_memory_init_actions,
texture_memory_actions: parent_state.texture_memory_actions,
as_actions: parent_state.as_actions,
temp_resources: parent_state.temp_resources,
indirect_draw_validation_resources: parent_state.indirect_draw_validation_resources,
snatch_guard: parent_state.snatch_guard,
debug_scope_depth: &mut debug_scope_depth,
},
binder: Binder::new(),
temp_offsets: Vec::new(),
dynamic_offset_count: 0,
pending_discard_init_fixups: SurfacesInDiscardState::new(),
scope: device.new_usage_scope(),
string_offset: 0,
},
active_query: None,
@ -580,7 +579,7 @@ fn encode_compute_pass(
intermediate_trackers: Tracker::new(),
};
let indices = &state.pass.base.device.tracker_indices;
let indices = &device.tracker_indices;
state
.pass
.base
@ -595,10 +594,8 @@ fn encode_compute_pass(
.set_size(indices.textures.size());
let timestamp_writes: Option<hal::PassTimestampWrites<'_, dyn hal::DynQuerySet>> =
if let Some(tw) = pass.timestamp_writes.take() {
tw.query_set
.same_device_as(cmd_enc.as_ref())
.map_pass_err(pass_scope)?;
if let Some(tw) = timestamp_writes.take() {
tw.query_set.same_device(device).map_pass_err(pass_scope)?;
let query_set = state
.pass
@ -658,7 +655,7 @@ fn encode_compute_pass(
let scope = PassErrorScope::SetBindGroup;
pass::set_bind_group::<ComputePassErrorInner>(
&mut state.pass,
cmd_enc.as_ref(),
device,
&base.dynamic_offsets,
index,
num_dynamic_offsets,
@ -669,7 +666,7 @@ fn encode_compute_pass(
}
ArcComputeCommand::SetPipeline(pipeline) => {
let scope = PassErrorScope::SetPipelineCompute;
set_pipeline(&mut state, cmd_enc.as_ref(), pipeline).map_pass_err(scope)?;
set_pipeline(&mut state, device, pipeline).map_pass_err(scope)?;
}
ArcComputeCommand::SetPushConstant {
offset,
@ -699,8 +696,7 @@ fn encode_compute_pass(
}
ArcComputeCommand::DispatchIndirect { buffer, offset } => {
let scope = PassErrorScope::Dispatch { indirect: true };
dispatch_indirect(&mut state, cmd_enc.as_ref(), buffer, offset)
.map_pass_err(scope)?;
dispatch_indirect(&mut state, device, buffer, offset).map_pass_err(scope)?;
}
ArcComputeCommand::PushDebugGroup { color: _, len } => {
pass::push_debug_group(&mut state.pass, &base.string_data, len);
@ -720,8 +716,8 @@ fn encode_compute_pass(
let scope = PassErrorScope::WriteTimestamp;
pass::write_timestamp::<ComputePassErrorInner>(
&mut state.pass,
cmd_enc.as_ref(),
None,
device,
None, // compute passes do not attempt to coalesce query resets
query_set,
query_index,
)
@ -736,7 +732,7 @@ fn encode_compute_pass(
query_set,
state.pass.base.raw_encoder,
&mut state.pass.base.tracker.query_sets,
cmd_enc.as_ref(),
device,
query_index,
None,
&mut state.active_query,
@ -763,23 +759,22 @@ fn encode_compute_pass(
}
let State {
pass:
pass::PassState {
base: EncodingState { tracker, .. },
pending_discard_init_fixups,
..
},
pass: pass::PassState {
pending_discard_init_fixups,
..
},
intermediate_trackers,
..
} = state;
// Stop the current command encoder.
encoder.close().map_pass_err(pass_scope)?;
parent_state.raw_encoder.close().map_pass_err(pass_scope)?;
// Create a new command encoder, which we will insert _before_ the body of the compute pass.
//
// Use that buffer to insert barriers and clear discarded images.
let transit = encoder
let transit = parent_state
.raw_encoder
.open_pass(hal_label(
Some("(wgpu internal) Pre Pass"),
device.instance_flags,
@ -788,28 +783,31 @@ fn encode_compute_pass(
fixup_discarded_surfaces(
pending_discard_init_fixups.into_iter(),
transit,
&mut tracker.textures,
&mut parent_state.tracker.textures,
device,
&snatch_guard,
parent_state.snatch_guard,
);
CommandEncoder::insert_barriers_from_tracker(
transit,
tracker,
parent_state.tracker,
&intermediate_trackers,
&snatch_guard,
parent_state.snatch_guard,
);
// Close the command encoder, and swap it with the previous.
encoder.close_and_swap().map_pass_err(pass_scope)?;
parent_state
.raw_encoder
.close_and_swap()
.map_pass_err(pass_scope)?;
Ok(())
}
fn set_pipeline(
state: &mut State,
cmd_enc: &CommandEncoder,
device: &Arc<Device>,
pipeline: Arc<ComputePipeline>,
) -> Result<(), ComputePassErrorInner> {
pipeline.same_device_as(cmd_enc)?;
pipeline.same_device(device)?;
state.pipeline = Some(pipeline.clone());
@ -886,11 +884,11 @@ fn dispatch(state: &mut State, groups: [u32; 3]) -> Result<(), ComputePassErrorI
fn dispatch_indirect(
state: &mut State,
cmd_enc: &CommandEncoder,
device: &Arc<Device>,
buffer: Arc<Buffer>,
offset: u64,
) -> Result<(), ComputePassErrorInner> {
buffer.same_device_as(cmd_enc)?;
buffer.same_device(device)?;
state.is_ready()?;

View File

@ -1,22 +1,45 @@
use alloc::{sync::Arc, vec::Vec};
use crate::{
command::memory_init::CommandBufferTextureMemoryActions, device::Device,
init_tracker::BufferInitTrackerAction, ray_tracing::AsAction, snatch::SnatchGuard,
command::memory_init::CommandBufferTextureMemoryActions,
device::{queue::TempResource, Device},
init_tracker::BufferInitTrackerAction,
ray_tracing::AsAction,
snatch::SnatchGuard,
track::Tracker,
};
/// State applicable when encoding commands onto a compute pass, or onto a
/// render pass, or directly with a command encoder.
pub(crate) struct EncodingState<'snatch_guard, 'cmd_enc, 'raw_encoder> {
/// State applicable when encoding commands onto a compute pass, render pass, or
/// directly to a command encoder.
///
/// Most encoding routines just want to receive an open encoder, write
/// command(s) to it, and leave it open for whatever is next. In this case the
/// `E` type parameter has the default value of `dyn hal::DynCommandEncoder`. To
/// avoid confusion about encoder state, we set the convention that _the encoder
/// in an `EncodingState` holding a bare HAL reference must always be open_.
///
/// Compute and render passes are more complicated. Because they record a
/// command buffer for a housekeeping pre-pass which is inserted before the pass
/// itself, the first thing they will do is close and reopen the encoder if it
/// is already open. Unnecessary empty HAL passes can be avoided by passing them
/// the encoder in whatever state it happens to be. In this case, `E` is
/// `InnerCommandEncoder`, which tracks the state of the encoder. The callee
/// (the render or compute pass) will open and close the encoder as necessary.
///
/// This structure is not supported by cbindgen because it contains a trait
/// object reference.
///
/// cbindgen:ignore
pub(crate) struct EncodingState<'snatch_guard, 'cmd_enc, E: ?Sized = dyn hal::DynCommandEncoder> {
pub(crate) device: &'cmd_enc Arc<Device>,
pub(crate) raw_encoder: &'raw_encoder mut dyn hal::DynCommandEncoder,
pub(crate) raw_encoder: &'cmd_enc mut E,
pub(crate) tracker: &'cmd_enc mut Tracker,
pub(crate) buffer_memory_init_actions: &'cmd_enc mut Vec<BufferInitTrackerAction>,
pub(crate) texture_memory_actions: &'cmd_enc mut CommandBufferTextureMemoryActions,
pub(crate) as_actions: &'cmd_enc mut Vec<AsAction>,
pub(crate) temp_resources: &'cmd_enc mut Vec<TempResource>,
pub(crate) indirect_draw_validation_resources:
&'cmd_enc mut crate::indirect_validation::DrawResources,

View File

@ -1,8 +1,11 @@
use core::convert::Infallible;
use alloc::{string::String, vec::Vec};
use alloc::{string::String, sync::Arc, vec::Vec};
use crate::id;
use crate::{
id,
resource::{Buffer, QuerySet, Texture},
};
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
@ -68,3 +71,71 @@ pub enum Command {
tlas: Vec<crate::ray_tracing::TraceTlasPackage>,
},
}
#[derive(Clone, Debug)]
pub enum ArcCommand {
CopyBufferToBuffer {
src: Arc<Buffer>,
src_offset: wgt::BufferAddress,
dst: Arc<Buffer>,
dst_offset: wgt::BufferAddress,
size: Option<wgt::BufferAddress>,
},
CopyBufferToTexture {
src: wgt::TexelCopyBufferInfo<Arc<Buffer>>,
dst: wgt::TexelCopyTextureInfo<Arc<Texture>>,
size: wgt::Extent3d,
},
CopyTextureToBuffer {
src: wgt::TexelCopyTextureInfo<Arc<Texture>>,
dst: wgt::TexelCopyBufferInfo<Arc<Buffer>>,
size: wgt::Extent3d,
},
CopyTextureToTexture {
src: wgt::TexelCopyTextureInfo<Arc<Texture>>,
dst: wgt::TexelCopyTextureInfo<Arc<Texture>>,
size: wgt::Extent3d,
},
ClearBuffer {
dst: Arc<Buffer>,
offset: wgt::BufferAddress,
size: Option<wgt::BufferAddress>,
},
ClearTexture {
dst: Arc<Texture>,
subresource_range: wgt::ImageSubresourceRange,
},
WriteTimestamp {
query_set: Arc<QuerySet>,
query_index: u32,
},
ResolveQuerySet {
query_set: Arc<QuerySet>,
start_query: u32,
query_count: u32,
destination: Arc<Buffer>,
destination_offset: wgt::BufferAddress,
},
PushDebugGroup(String),
PopDebugGroup,
InsertDebugMarker(String),
RunComputePass {
pass: super::BasePass<super::ArcComputeCommand, Infallible>,
timestamp_writes: Option<super::ArcPassTimestampWrites>,
},
RunRenderPass {
pass: super::BasePass<super::ArcRenderCommand, Infallible>,
color_attachments: super::ArcRenderPassColorAttachmentArray,
depth_stencil_attachment: Option<super::ArcRenderPassDepthStencilAttachment>,
timestamp_writes: Option<super::ArcPassTimestampWrites>,
occlusion_query_set: Option<Arc<QuerySet>>,
},
BuildAccelerationStructures {
blas: Vec<crate::ray_tracing::ArcBlasBuildEntry>,
tlas: Vec<crate::ray_tracing::ArcTlasPackage>,
},
TransitionResources {
buffer_transitions: Vec<wgt::BufferTransition<Arc<Buffer>>>,
texture_transitions: Vec<wgt::TextureTransition<Arc<Texture>>>,
},
}

View File

@ -1,3 +1,13 @@
//! # Command Encoding
//!
//! TODO: High-level description of command encoding.
//!
//! The convention in this module is that functions accepting a [`&mut dyn
//! hal::DynCommandEncoder`] are low-level helpers and may assume the encoder is
//! in the open state, ready to encode commands. Encoders that are not open
//! should be nested within some other container that provides additional
//! state tracking, like [`InnerCommandEncoder`].
mod allocator;
mod bind;
mod bundle;
@ -19,22 +29,37 @@ mod transfer;
mod transition_resources;
use alloc::{borrow::ToOwned as _, boxed::Box, string::String, sync::Arc, vec::Vec};
use core::convert::Infallible;
use core::mem::{self, ManuallyDrop};
use core::ops;
pub(crate) use self::clear::clear_texture;
pub use self::{
bundle::*, clear::ClearError, compute::*, compute_command::ComputeCommand, draw::*,
encoder_command::Command, query::*, render::*, render_command::RenderCommand, transfer::*,
bundle::*,
clear::ClearError,
compute::*,
compute_command::{ArcComputeCommand, ComputeCommand},
draw::*,
encoder_command::{ArcCommand, Command},
query::*,
render::*,
render_command::{ArcRenderCommand, RenderCommand},
transfer::*,
};
pub(crate) use allocator::CommandAllocator;
pub(crate) use timestamp_writes::ArcPassTimestampWrites;
pub use timestamp_writes::PassTimestampWrites;
use self::memory_init::CommandBufferTextureMemoryActions;
use self::{
clear::{clear_buffer, clear_texture_cmd},
memory_init::CommandBufferTextureMemoryActions,
ray_tracing::build_acceleration_structures,
transition_resources::transition_resources,
};
use crate::binding_model::BindingError;
use crate::command::encoder::EncodingState;
use crate::command::transition_resources::TransitionResourcesError;
use crate::device::queue::TempResource;
use crate::device::{Device, DeviceError, MissingFeatures};
@ -123,12 +148,21 @@ pub(crate) enum CommandEncoderStatus {
}
impl CommandEncoderStatus {
/// Record commands using the supplied closure.
#[cfg(feature = "trace")]
fn trace(&mut self) -> Option<&mut Vec<TraceCommand>> {
match self {
Self::Recording(cmd_buf_data) => cmd_buf_data.trace_commands.as_mut(),
_ => None,
}
}
/// Push a command provided by a closure onto the encoder.
///
/// If the encoder is in the [`Self::Recording`] state, calls the closure to
/// record commands. If the closure returns an error, stores that error in
/// the encoder for later reporting when `finish()` is called. Returns
/// `Ok(())` even if the closure returned an error.
/// obtain a command, and pushes it onto the encoder. If the closure returns
/// an error, stores that error in the encoder for later reporting when
/// `finish()` is called. Returns `Ok(())` even if the closure returned an
/// error.
///
/// If the encoder is not in the [`Self::Recording`] state, the closure will
/// not be called and nothing will be recorded. The encoder will be
@ -137,7 +171,51 @@ impl CommandEncoderStatus {
/// returns `Ok(())`.
///
/// [ves]: https://www.w3.org/TR/webgpu/#abstract-opdef-validate-the-encoder-state
fn record_with<
fn push_with<F: FnOnce() -> Result<ArcCommand, E>, E: Clone + Into<CommandEncoderError>>(
&mut self,
f: F,
) -> Result<(), EncoderStateError> {
match self {
Self::Recording(cmd_buf_data) => {
match f() {
Ok(cmd) => cmd_buf_data.commands.push(cmd),
Err(err) => {
self.invalidate(err);
}
}
Ok(())
}
Self::Locked(_) => {
// Invalidate the encoder and do not record anything, but do not
// return an immediate validation error.
self.invalidate(EncoderStateError::Locked);
Ok(())
}
// Encoder is ended. Invalidate the encoder, do not record anything,
// and return an immediate validation error.
Self::Finished(_) => Err(self.invalidate(EncoderStateError::Ended)),
Self::Consumed => Err(EncoderStateError::Ended),
// Encoder is already invalid. Do not record anything, but do not
// return an immediate validation error.
Self::Error(_) => Ok(()),
Self::Transitioning => unreachable!(),
}
}
/// Call a closure with the inner command buffer structure.
///
/// If the encoder is in the [`Self::Recording`] state, calls the provided
/// closure. If the closure returns an error, stores that error in the
/// encoder for later reporting when `finish()` is called. Returns `Ok(())`
/// even if the closure returned an error.
///
/// If the encoder is not in the [`Self::Recording`] state, the closure will
/// not be called. The encoder will be invalidated (if it is not already).
/// If the error is a [validation error that should be raised
/// immediately][ves], returns it in `Err`, otherwise, returns `Ok(())`.
///
/// [ves]: https://www.w3.org/TR/webgpu/#abstract-opdef-validate-the-encoder-state
fn with_buffer<
F: FnOnce(&mut CommandBufferMutable) -> Result<(), E>,
E: Clone + Into<CommandEncoderError>,
>(
@ -193,7 +271,7 @@ impl CommandEncoderStatus {
}
}
#[cfg(feature = "trace")]
#[cfg(all(feature = "trace", any(feature = "serde", feature = "replay")))]
fn get_inner(&mut self) -> &mut CommandBufferMutable {
match self {
Self::Locked(inner) | Self::Finished(inner) | Self::Recording(inner) => inner,
@ -210,7 +288,7 @@ impl CommandEncoderStatus {
/// Locks the encoder by putting it in the [`Self::Locked`] state.
///
/// Render or compute passes call this on start. At the end of the pass,
/// they call [`Self::unlock_and_record`] to put the [`CommandBuffer`] back
/// they call [`Self::unlock_encoder`] to put the [`CommandBuffer`] back
/// into the [`Self::Recording`] state.
fn lock_encoder(&mut self) -> Result<(), EncoderStateError> {
match mem::replace(self, Self::Transitioning) {
@ -238,32 +316,19 @@ impl CommandEncoderStatus {
}
}
/// Unlocks the [`CommandBuffer`] and puts it back into the
/// [`Self::Recording`] state, then records commands using the supplied
/// closure.
/// Unlocks the encoder and puts it back into the [`Self::Recording`] state.
///
/// This function is the unlocking counterpart to [`Self::lock_encoder`]. It
/// is only valid to call this function if the encoder is in the
/// [`Self::Locked`] state.
///
/// If the closure returns an error, stores that error in the encoder for
/// later reporting when `finish()` is called. Returns `Ok(())` even if the
/// closure returned an error.
///
/// If the encoder is not in the [`Self::Locked`] state, the closure will
/// not be called and nothing will be recorded. If a validation error should
/// be raised immediately, returns it in `Err`, otherwise, returns `Ok(())`.
fn unlock_and_record<
F: FnOnce(&mut CommandBufferMutable) -> Result<(), E>,
E: Clone + Into<CommandEncoderError>,
>(
&mut self,
f: F,
) -> Result<(), EncoderStateError> {
/// If the encoder is in a state other than [`Self::Locked`] and a
/// validation error should be raised immediately, returns it in `Err`,
/// otherwise, stores the error in the encoder and returns `Ok(())`.
fn unlock_encoder(&mut self) -> Result<(), EncoderStateError> {
match mem::replace(self, Self::Transitioning) {
Self::Locked(inner) => {
*self = Self::Recording(inner);
RecordingGuard { inner: self }.record(f);
Ok(())
}
st @ Self::Finished(_) => {
@ -279,8 +344,8 @@ impl CommandEncoderStatus {
Err(EncoderStateError::Ended)
}
st @ Self::Error(_) => {
// Encoder is invalid. Do not record anything, but do not
// return an immediate validation error.
// Encoder is already invalid. The error will be reported by
// `CommandEncoder.finish`.
*self = st;
Ok(())
}
@ -292,18 +357,10 @@ impl CommandEncoderStatus {
// Replace our state with `Consumed`, and return either the inner
// state or an error, to be transferred to the command buffer.
match mem::replace(self, Self::Consumed) {
Self::Recording(mut inner) => {
if let Err(err) = inner.encoder.close_if_open() {
Self::Error(err.into())
} else if inner.debug_scope_depth > 0 {
Self::Error(CommandEncoderError::DebugGroupError(
DebugGroupError::MissingPop,
))
} else {
// Note: if we want to stop tracking the swapchain texture view,
// this is the place to do it.
Self::Finished(inner)
}
Self::Recording(inner) => {
// Nothing should have opened the encoder yet.
assert!(!inner.encoder.is_open);
Self::Finished(inner)
}
Self::Consumed | Self::Finished(_) => Self::Error(EncoderStateError::Ended.into()),
Self::Locked(_) => Self::Error(EncoderStateError::Locked.into()),
@ -574,6 +631,22 @@ impl InnerCommandEncoder {
Ok(())
}
/// If the command encoder is not open, begin recording a new command buffer.
///
/// If the command encoder was already open, does nothing.
///
/// In both cases, returns a reference to the raw encoder.
fn open_if_closed(&mut self) -> Result<&mut dyn hal::DynCommandEncoder, DeviceError> {
if !self.is_open {
self.is_open = true;
let hal_label = hal_label(Some(self.label.as_str()), self.device.instance_flags);
unsafe { self.raw.begin_encoding(hal_label) }
.map_err(|e| self.device.handle_hal_error(e))?;
}
Ok(self.raw.as_mut())
}
/// Begin recording a new command buffer, if we haven't already.
///
/// The underlying hal encoder is put in the "recording" state.
@ -588,7 +661,7 @@ impl InnerCommandEncoder {
Ok(self.raw.as_mut())
}
/// Begin recording a new command buffer for a render pass, with
/// Begin recording a new command buffer for a render or compute pass, with
/// its own label.
///
/// The underlying hal encoder is put in the "recording" state.
@ -661,22 +734,13 @@ pub struct CommandBufferMutable {
indirect_draw_validation_resources: crate::indirect_validation::DrawResources,
debug_scope_depth: u32,
pub(crate) commands: Vec<ArcCommand>,
#[cfg(feature = "trace")]
pub(crate) trace_commands: Option<Vec<TraceCommand>>,
}
impl CommandBufferMutable {
pub(crate) fn open_encoder_and_tracker(
&mut self,
) -> Result<(&mut dyn hal::DynCommandEncoder, &mut Tracker), DeviceError> {
let encoder = self.encoder.open()?;
let tracker = &mut self.trackers;
Ok((encoder, tracker))
}
pub(crate) fn into_baked_commands(self) -> BakedCommands {
BakedCommands {
encoder: self.encoder,
@ -735,7 +799,7 @@ impl CommandEncoder {
temp_resources: Default::default(),
indirect_draw_validation_resources:
crate::indirect_validation::DrawResources::new(device.clone()),
debug_scope_depth: 0,
commands: Vec::new(),
#[cfg(feature = "trace")]
trace_commands: if device.trace.lock().is_some() {
Some(Vec::new())
@ -883,6 +947,10 @@ pub struct BasePass<C, E> {
pub error: Option<E>,
/// The stream of commands.
///
/// The commands are moved out of this vector when the pass is ended (i.e.
/// at the same time that `parent` is taken out of the
/// `ComputePass`/`RenderPass`).
pub commands: Vec<C>,
/// Dynamic offsets consumed by [`SetBindGroup`] commands in `commands`.
@ -926,6 +994,26 @@ impl<C: Clone, E: Clone> BasePass<C, E> {
push_constant_data: Vec::new(),
}
}
/// Takes the commands from the pass, or returns an error if the pass is
/// invalid.
///
/// This is called when the pass is ended, at the same time that the
/// `parent` member of the `ComputePass` or `RenderPass` containing the pass
/// is taken.
fn take(&mut self) -> Result<BasePass<C, Infallible>, E> {
match self.error.as_ref() {
Some(err) => Err(err.clone()),
None => Ok(BasePass {
label: self.label.clone(),
error: None,
commands: mem::take(&mut self.commands),
dynamic_offsets: mem::take(&mut self.dynamic_offsets),
string_data: mem::take(&mut self.string_data),
push_constant_data: mem::take(&mut self.push_constant_data),
}),
}
}
}
/// Checks the state of a [`compute::ComputePass`] or [`render::RenderPass`] and
@ -1169,20 +1257,21 @@ impl Global {
&self,
buffer_id: Id<id::markers::Buffer>,
) -> Result<Arc<crate::resource::Buffer>, InvalidResourceError> {
let hub = &self.hub;
let buffer = hub.buffers.get(buffer_id).get()?;
self.hub.buffers.get(buffer_id).get()
}
Ok(buffer)
fn resolve_texture_id(
&self,
texture_id: Id<id::markers::Texture>,
) -> Result<Arc<crate::resource::Texture>, InvalidResourceError> {
self.hub.textures.get(texture_id).get()
}
fn resolve_query_set(
&self,
query_set_id: Id<id::markers::QuerySet>,
) -> Result<Arc<QuerySet>, InvalidResourceError> {
let hub = &self.hub;
let query_set = hub.query_sets.get(query_set_id).get()?;
Ok(query_set)
self.hub.query_sets.get(query_set_id).get()
}
pub fn command_encoder_finish(
@ -1196,14 +1285,196 @@ impl Global {
let hub = &self.hub;
let cmd_enc = hub.command_encoders.get(encoder_id);
let mut cmd_enc_status = cmd_enc.data.lock();
let data = cmd_enc.data.lock().finish();
let res = match cmd_enc_status.finish() {
CommandEncoderStatus::Finished(cmd_buf_data) => Ok(cmd_buf_data),
CommandEncoderStatus::Error(err) => Err(err),
_ => unreachable!(),
};
// Errors related to destroyed resources are not reported until the
// command buffer is submitted.
let error = match data {
CommandEncoderStatus::Error(ref e) if !e.is_destroyed_error() => Some(e.clone()),
_ => None,
let res = res.and_then(|mut cmd_buf_data| {
cmd_enc.device.check_is_valid()?;
let snatch_guard = cmd_enc.device.snatchable_lock.read();
let mut debug_scope_depth = 0;
let mut commands = mem::take(&mut cmd_buf_data.commands);
for command in commands.drain(..) {
if matches!(
command,
ArcCommand::RunRenderPass { .. } | ArcCommand::RunComputePass { .. }
) {
// Compute passes and render passes can accept either an
// open or closed encoder. This state object holds an
// `InnerCommandEncoder`. See the documentation of
// [`EncodingState`].
let mut state = EncodingState {
device: &cmd_enc.device,
raw_encoder: &mut cmd_buf_data.encoder,
tracker: &mut cmd_buf_data.trackers,
buffer_memory_init_actions: &mut cmd_buf_data.buffer_memory_init_actions,
texture_memory_actions: &mut cmd_buf_data.texture_memory_actions,
as_actions: &mut cmd_buf_data.as_actions,
temp_resources: &mut cmd_buf_data.temp_resources,
indirect_draw_validation_resources: &mut cmd_buf_data
.indirect_draw_validation_resources,
snatch_guard: &snatch_guard,
debug_scope_depth: &mut debug_scope_depth,
};
match command {
ArcCommand::RunRenderPass {
pass,
color_attachments,
depth_stencil_attachment,
timestamp_writes,
occlusion_query_set,
} => {
encode_render_pass(
&mut state,
pass,
color_attachments,
depth_stencil_attachment,
timestamp_writes,
occlusion_query_set,
)?;
}
ArcCommand::RunComputePass {
pass,
timestamp_writes,
} => {
encode_compute_pass(&mut state, pass, timestamp_writes)?;
}
_ => unreachable!(),
}
} else {
// All the other non-pass encoding routines assume the
// encoder is open, so open it if necessary. This state
// object holds an `&mut dyn hal::DynCommandEncoder`. By
// convention, a bare HAL encoder reference in
// [`EncodingState`] must always be an open encoder.
let raw_encoder = cmd_buf_data.encoder.open_if_closed()?;
let mut state = EncodingState {
device: &cmd_enc.device,
raw_encoder,
tracker: &mut cmd_buf_data.trackers,
buffer_memory_init_actions: &mut cmd_buf_data.buffer_memory_init_actions,
texture_memory_actions: &mut cmd_buf_data.texture_memory_actions,
as_actions: &mut cmd_buf_data.as_actions,
temp_resources: &mut cmd_buf_data.temp_resources,
indirect_draw_validation_resources: &mut cmd_buf_data
.indirect_draw_validation_resources,
snatch_guard: &snatch_guard,
debug_scope_depth: &mut debug_scope_depth,
};
match command {
ArcCommand::CopyBufferToBuffer {
src,
src_offset,
dst,
dst_offset,
size,
} => {
copy_buffer_to_buffer(
&mut state, &src, src_offset, &dst, dst_offset, size,
)?;
}
ArcCommand::CopyBufferToTexture { src, dst, size } => {
copy_buffer_to_texture(&mut state, &src, &dst, &size)?;
}
ArcCommand::CopyTextureToBuffer { src, dst, size } => {
copy_texture_to_buffer(&mut state, &src, &dst, &size)?;
}
ArcCommand::CopyTextureToTexture { src, dst, size } => {
copy_texture_to_texture(&mut state, &src, &dst, &size)?;
}
ArcCommand::ClearBuffer { dst, offset, size } => {
clear_buffer(&mut state, dst, offset, size)?;
}
ArcCommand::ClearTexture {
dst,
subresource_range,
} => {
clear_texture_cmd(&mut state, dst, &subresource_range)?;
}
ArcCommand::WriteTimestamp {
query_set,
query_index,
} => {
write_timestamp(&mut state, query_set, query_index)?;
}
ArcCommand::ResolveQuerySet {
query_set,
start_query,
query_count,
destination,
destination_offset,
} => {
resolve_query_set(
&mut state,
query_set,
start_query,
query_count,
destination,
destination_offset,
)?;
}
ArcCommand::PushDebugGroup(label) => {
push_debug_group(&mut state, &label)?;
}
ArcCommand::PopDebugGroup => {
pop_debug_group(&mut state)?;
}
ArcCommand::InsertDebugMarker(label) => {
insert_debug_marker(&mut state, &label)?;
}
ArcCommand::BuildAccelerationStructures { blas, tlas } => {
build_acceleration_structures(&mut state, blas, tlas)?;
}
ArcCommand::TransitionResources {
buffer_transitions,
texture_transitions,
} => {
transition_resources(
&mut state,
buffer_transitions,
texture_transitions,
)?;
}
ArcCommand::RunComputePass { .. } | ArcCommand::RunRenderPass { .. } => {
unreachable!()
}
}
}
}
if debug_scope_depth > 0 {
Err(CommandEncoderError::DebugGroupError(
DebugGroupError::MissingPop,
))?;
}
// Close the encoder, unless it was closed already by a render or compute pass.
cmd_buf_data.encoder.close_if_open()?;
// Note: if we want to stop tracking the swapchain texture view,
// this is the place to do it.
Ok(cmd_buf_data)
});
let (data, error) = match res {
Err(e) => {
if e.is_destroyed_error() {
// Errors related to destroyed resources are not reported until the
// command buffer is submitted.
(CommandEncoderStatus::Error(e.clone()), None)
} else {
(CommandEncoderStatus::Error(e.clone()), Some(e))
}
}
Ok(data) => (CommandEncoderStatus::Finished(data), None),
};
let cmd_buf = CommandBuffer {
@ -1229,8 +1500,14 @@ impl Global {
let cmd_enc = hub.command_encoders.get(encoder_id);
let mut cmd_buf_data = cmd_enc.data.lock();
cmd_buf_data.record_with(|cmd_buf_data| -> Result<(), CommandEncoderError> {
push_debug_group(cmd_buf_data, &cmd_enc, label)
#[cfg(feature = "trace")]
if let Some(ref mut list) = cmd_buf_data.trace() {
list.push(TraceCommand::PushDebugGroup(label.to_owned()));
}
cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {
Ok(ArcCommand::PushDebugGroup(label.to_owned()))
})
}
@ -1246,8 +1523,14 @@ impl Global {
let cmd_enc = hub.command_encoders.get(encoder_id);
let mut cmd_buf_data = cmd_enc.data.lock();
cmd_buf_data.record_with(|cmd_buf_data| -> Result<(), CommandEncoderError> {
insert_debug_marker(cmd_buf_data, &cmd_enc, label)
#[cfg(feature = "trace")]
if let Some(ref mut list) = cmd_buf_data.trace() {
list.push(TraceCommand::InsertDebugMarker(label.to_owned()));
}
cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {
Ok(ArcCommand::InsertDebugMarker(label.to_owned()))
})
}
@ -1262,9 +1545,14 @@ impl Global {
let cmd_enc = hub.command_encoders.get(encoder_id);
let mut cmd_buf_data = cmd_enc.data.lock();
cmd_buf_data.record_with(|cmd_buf_data| -> Result<(), CommandEncoderError> {
pop_debug_group(cmd_buf_data, &cmd_enc)
})
#[cfg(feature = "trace")]
if let Some(ref mut list) = cmd_buf_data.trace() {
list.push(TraceCommand::PopDebugGroup);
}
cmd_buf_data
.push_with(|| -> Result<_, CommandEncoderError> { Ok(ArcCommand::PopDebugGroup) })
}
fn validate_pass_timestamp_writes<E>(
@ -1320,84 +1608,49 @@ impl Global {
}
pub(crate) fn push_debug_group(
cmd_buf_data: &mut CommandBufferMutable,
cmd_enc: &Arc<CommandEncoder>,
state: &mut EncodingState,
label: &str,
) -> Result<(), CommandEncoderError> {
cmd_buf_data.debug_scope_depth += 1;
*state.debug_scope_depth += 1;
#[cfg(feature = "trace")]
if let Some(ref mut list) = cmd_buf_data.trace_commands {
list.push(TraceCommand::PushDebugGroup(label.to_owned()));
}
cmd_enc.device.check_is_valid()?;
let cmd_buf_raw = cmd_buf_data.encoder.open()?;
if !cmd_enc
if !state
.device
.instance_flags
.contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
{
unsafe {
cmd_buf_raw.begin_debug_marker(label);
}
unsafe { state.raw_encoder.begin_debug_marker(label) };
}
Ok(())
}
pub(crate) fn insert_debug_marker(
cmd_buf_data: &mut CommandBufferMutable,
cmd_enc: &Arc<CommandEncoder>,
state: &mut EncodingState,
label: &str,
) -> Result<(), CommandEncoderError> {
#[cfg(feature = "trace")]
if let Some(ref mut list) = cmd_buf_data.trace_commands {
list.push(TraceCommand::InsertDebugMarker(label.to_owned()));
}
cmd_enc.device.check_is_valid()?;
if !cmd_enc
if !state
.device
.instance_flags
.contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
{
let cmd_buf_raw = cmd_buf_data.encoder.open()?;
unsafe {
cmd_buf_raw.insert_debug_marker(label);
}
unsafe { state.raw_encoder.insert_debug_marker(label) };
}
Ok(())
}
pub(crate) fn pop_debug_group(
cmd_buf_data: &mut CommandBufferMutable,
cmd_enc: &Arc<CommandEncoder>,
) -> Result<(), CommandEncoderError> {
if cmd_buf_data.debug_scope_depth == 0 {
pub(crate) fn pop_debug_group(state: &mut EncodingState) -> Result<(), CommandEncoderError> {
if *state.debug_scope_depth == 0 {
return Err(DebugGroupError::InvalidPop.into());
}
cmd_buf_data.debug_scope_depth -= 1;
*state.debug_scope_depth -= 1;
#[cfg(feature = "trace")]
if let Some(ref mut list) = cmd_buf_data.trace_commands {
list.push(TraceCommand::PopDebugGroup);
}
cmd_enc.device.check_is_valid()?;
let cmd_buf_raw = cmd_buf_data.encoder.open()?;
if !cmd_enc
if !state
.device
.instance_flags
.contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
{
unsafe {
cmd_buf_raw.end_debug_marker();
}
unsafe { state.raw_encoder.end_debug_marker() };
}
Ok(())

View File

@ -4,8 +4,8 @@ use crate::binding_model::{BindError, BindGroup, PushConstantUploadError};
use crate::command::bind::Binder;
use crate::command::encoder::EncodingState;
use crate::command::memory_init::SurfacesInDiscardState;
use crate::command::{CommandEncoder, DebugGroupError, QueryResetMap, QueryUseError};
use crate::device::{DeviceError, MissingFeatures};
use crate::command::{DebugGroupError, QueryResetMap, QueryUseError};
use crate::device::{Device, DeviceError, MissingFeatures};
use crate::pipeline::LateSizedBufferGroup;
use crate::ray_tracing::AsAction;
use crate::resource::{DestroyedResourceError, Labeled, ParentDevice, QuerySet};
@ -41,8 +41,8 @@ impl WebGpuError for InvalidValuesOffset {
}
}
pub(crate) struct PassState<'scope, 'snatch_guard, 'cmd_enc, 'raw_encoder> {
pub(crate) base: EncodingState<'snatch_guard, 'cmd_enc, 'raw_encoder>,
pub(crate) struct PassState<'scope, 'snatch_guard, 'cmd_enc> {
pub(crate) base: EncodingState<'snatch_guard, 'cmd_enc>,
/// Immediate texture inits required because of prior discards. Need to
/// be inserted before texture reads.
@ -61,7 +61,7 @@ pub(crate) struct PassState<'scope, 'snatch_guard, 'cmd_enc, 'raw_encoder> {
pub(crate) fn set_bind_group<E>(
state: &mut PassState,
cmd_enc: &CommandEncoder,
device: &Arc<Device>,
dynamic_offsets: &[DynamicOffset],
index: u32,
num_dynamic_offsets: usize,
@ -108,7 +108,7 @@ where
let bind_group = bind_group.unwrap();
let bind_group = state.base.tracker.bind_groups.insert_single(bind_group);
bind_group.same_device_as(cmd_enc)?;
bind_group.same_device(device)?;
bind_group.validate_dynamic_bindings(index, &state.temp_offsets)?;
@ -271,7 +271,7 @@ where
pub(crate) fn write_timestamp<E>(
state: &mut PassState,
cmd_enc: &CommandEncoder,
device: &Arc<Device>,
pending_query_resets: Option<&mut QueryResetMap>,
query_set: Arc<QuerySet>,
query_index: u32,
@ -284,7 +284,7 @@ where
query_set.error_ident()
);
query_set.same_device_as(cmd_enc)?;
query_set.same_device(device)?;
state
.base

View File

@ -4,14 +4,14 @@ use core::{iter, mem};
#[cfg(feature = "trace")]
use crate::command::Command as TraceCommand;
use crate::{
command::{CommandBufferMutable, CommandEncoder, EncoderStateError},
device::{DeviceError, MissingFeatures},
command::{encoder::EncodingState, ArcCommand, EncoderStateError},
device::{Device, DeviceError, MissingFeatures},
global::Global,
id,
init_tracker::MemoryInitKind,
resource::{
DestroyedResourceError, InvalidResourceError, MissingBufferUsageError, ParentDevice,
QuerySet, RawResourceAccess, Trackable,
Buffer, DestroyedResourceError, InvalidResourceError, MissingBufferUsageError,
ParentDevice, QuerySet, RawResourceAccess, Trackable,
},
track::{StatelessTracker, TrackerIndex},
FastHashMap,
@ -307,12 +307,12 @@ pub(super) fn validate_and_begin_pipeline_statistics_query(
query_set: Arc<QuerySet>,
raw_encoder: &mut dyn hal::DynCommandEncoder,
tracker: &mut StatelessTracker<QuerySet>,
cmd_enc: &CommandEncoder,
device: &Arc<Device>,
query_index: u32,
reset_state: Option<&mut QueryResetMap>,
active_query: &mut Option<(Arc<QuerySet>, u32)>,
) -> Result<(), QueryUseError> {
query_set.same_device_as(cmd_enc)?;
query_set.same_device(device)?;
let needs_reset = reset_state.is_none();
query_set.validate_query(
@ -365,8 +365,20 @@ impl Global {
let cmd_enc = hub.command_encoders.get(command_encoder_id);
let mut cmd_buf_data = cmd_enc.data.lock();
cmd_buf_data.record_with(|cmd_buf_data| -> Result<(), QueryError> {
write_timestamp(cmd_buf_data, hub, &cmd_enc, query_set_id, query_index)
#[cfg(feature = "trace")]
if let Some(ref mut list) = cmd_buf_data.trace() {
list.push(TraceCommand::WriteTimestamp {
query_set_id,
query_index,
});
}
cmd_buf_data.push_with(|| -> Result<_, QueryError> {
Ok(ArcCommand::WriteTimestamp {
query_set: self.resolve_query_set(query_set_id)?,
query_index,
})
})
}
@ -383,97 +395,70 @@ impl Global {
let cmd_enc = hub.command_encoders.get(command_encoder_id);
let mut cmd_buf_data = cmd_enc.data.lock();
cmd_buf_data.record_with(|cmd_buf_data| -> Result<(), QueryError> {
resolve_query_set(
cmd_buf_data,
hub,
&cmd_enc,
#[cfg(feature = "trace")]
if let Some(ref mut list) = cmd_buf_data.trace() {
list.push(TraceCommand::ResolveQuerySet {
query_set_id,
start_query,
query_count,
destination,
destination_offset,
)
});
}
cmd_buf_data.push_with(|| -> Result<_, QueryError> {
Ok(ArcCommand::ResolveQuerySet {
query_set: self.resolve_query_set(query_set_id)?,
start_query,
query_count,
destination: self.resolve_buffer_id(destination)?,
destination_offset,
})
})
}
}
pub(super) fn write_timestamp(
cmd_buf_data: &mut CommandBufferMutable,
hub: &crate::hub::Hub,
cmd_enc: &Arc<CommandEncoder>,
query_set_id: id::QuerySetId,
state: &mut EncodingState,
query_set: Arc<QuerySet>,
query_index: u32,
) -> Result<(), QueryError> {
#[cfg(feature = "trace")]
if let Some(ref mut list) = cmd_buf_data.trace_commands {
list.push(TraceCommand::WriteTimestamp {
query_set_id,
query_index,
});
}
cmd_enc.device.check_is_valid()?;
cmd_enc
state
.device
.require_features(wgt::Features::TIMESTAMP_QUERY_INSIDE_ENCODERS)?;
let raw_encoder = cmd_buf_data.encoder.open()?;
query_set.same_device(state.device)?;
let query_set = hub.query_sets.get(query_set_id).get()?;
query_set.same_device_as(cmd_enc.as_ref())?;
query_set.validate_and_write_timestamp(state.raw_encoder, query_index, None)?;
query_set.validate_and_write_timestamp(raw_encoder, query_index, None)?;
cmd_buf_data.trackers.query_sets.insert_single(query_set);
state.tracker.query_sets.insert_single(query_set);
Ok(())
}
pub(super) fn resolve_query_set(
cmd_buf_data: &mut CommandBufferMutable,
hub: &crate::hub::Hub,
cmd_enc: &Arc<CommandEncoder>,
query_set_id: id::QuerySetId,
state: &mut EncodingState,
query_set: Arc<QuerySet>,
start_query: u32,
query_count: u32,
destination: id::BufferId,
dst_buffer: Arc<Buffer>,
destination_offset: BufferAddress,
) -> Result<(), QueryError> {
#[cfg(feature = "trace")]
if let Some(ref mut list) = cmd_buf_data.trace_commands {
list.push(TraceCommand::ResolveQuerySet {
query_set_id,
start_query,
query_count,
destination,
destination_offset,
});
}
cmd_enc.device.check_is_valid()?;
if destination_offset % wgt::QUERY_RESOLVE_BUFFER_ALIGNMENT != 0 {
return Err(QueryError::Resolve(ResolveError::BufferOffsetAlignment));
}
let query_set = hub.query_sets.get(query_set_id).get()?;
query_set.same_device(state.device)?;
dst_buffer.same_device(state.device)?;
query_set.same_device_as(cmd_enc.as_ref())?;
dst_buffer.check_destroyed(state.snatch_guard)?;
let dst_buffer = hub.buffers.get(destination).get()?;
dst_buffer.same_device_as(cmd_enc.as_ref())?;
let snatch_guard = dst_buffer.device.snatchable_lock.read();
dst_buffer.check_destroyed(&snatch_guard)?;
let dst_pending = cmd_buf_data
.trackers
let dst_pending = state
.tracker
.buffers
.set_single(&dst_buffer, wgt::BufferUses::COPY_DST);
let dst_barrier = dst_pending.map(|pending| pending.into_hal(&dst_buffer, &snatch_guard));
let dst_barrier = dst_pending.map(|pending| pending.into_hal(&dst_buffer, state.snatch_guard));
dst_buffer
.check_usage(wgt::BufferUsages::QUERY_RESOLVE)
@ -517,19 +502,18 @@ pub(super) fn resolve_query_set(
})?;
// TODO(https://github.com/gfx-rs/wgpu/issues/3993): Need to track initialization state.
cmd_buf_data.buffer_memory_init_actions.extend(
dst_buffer.initialization_status.read().create_action(
state
.buffer_memory_init_actions
.extend(dst_buffer.initialization_status.read().create_action(
&dst_buffer,
buffer_start_offset..buffer_end_offset,
MemoryInitKind::ImplicitlyInitialized,
),
);
));
let raw_dst_buffer = dst_buffer.try_raw(&snatch_guard)?;
let raw_encoder = cmd_buf_data.encoder.open()?;
let raw_dst_buffer = dst_buffer.try_raw(state.snatch_guard)?;
unsafe {
raw_encoder.transition_buffers(dst_barrier.as_slice());
raw_encoder.copy_query_results(
state.raw_encoder.transition_buffers(dst_barrier.as_slice());
state.raw_encoder.copy_query_results(
query_set.raw(),
start_query..end_query,
raw_dst_buffer,
@ -540,26 +524,21 @@ pub(super) fn resolve_query_set(
if matches!(query_set.desc.ty, wgt::QueryType::Timestamp) {
// Timestamp normalization is only needed for timestamps.
cmd_enc
.device
.timestamp_normalizer
.get()
.unwrap()
.normalize(
&snatch_guard,
raw_encoder,
&mut cmd_buf_data.trackers.buffers,
dst_buffer
.timestamp_normalization_bind_group
.get(&snatch_guard)
.unwrap(),
&dst_buffer,
destination_offset,
query_count,
);
state.device.timestamp_normalizer.get().unwrap().normalize(
state.snatch_guard,
state.raw_encoder,
&mut state.tracker.buffers,
dst_buffer
.timestamp_normalization_bind_group
.get(state.snatch_guard)
.unwrap(),
&dst_buffer,
destination_offset,
query_count,
);
}
cmd_buf_data.trackers.query_sets.insert_single(query_set);
state.tracker.query_sets.insert_single(query_set);
Ok(())
}

View File

@ -1,4 +1,4 @@
use alloc::{boxed::Box, sync::Arc, vec::Vec};
use alloc::{sync::Arc, vec::Vec};
use core::{
cmp::max,
num::NonZeroU64,
@ -11,34 +11,39 @@ use crate::{
command::CommandBufferMutable,
device::queue::TempResource,
global::Global,
hub::Hub,
id::CommandEncoderId,
init_tracker::MemoryInitKind,
ray_tracing::{
BlasBuildEntry, BlasGeometries, BlasTriangleGeometry, BuildAccelerationStructureError,
TlasInstance, TlasPackage, TraceBlasBuildEntry, TraceBlasGeometries,
TraceBlasTriangleGeometry, TraceTlasInstance, TraceTlasPackage,
BlasBuildEntry, BlasGeometries, BuildAccelerationStructureError, TlasPackage,
TraceBlasBuildEntry, TraceBlasGeometries, TraceBlasTriangleGeometry, TraceTlasInstance,
TraceTlasPackage,
},
resource::{Blas, BlasCompactState, Buffer, Labeled, StagingBuffer, Tlas},
scratch::ScratchBuffer,
snatch::SnatchGuard,
track::PendingTransition,
};
use crate::{
command::CommandEncoder,
ray_tracing::{AsAction, AsBuild, TlasBuild, ValidateAsActionsError},
};
use crate::{command::EncoderStateError, device::resource::CommandIndices};
use crate::{
command::{encoder::EncodingState, ArcCommand},
ray_tracing::{
ArcBlasBuildEntry, ArcBlasGeometries, ArcBlasTriangleGeometry, ArcTlasInstance,
ArcTlasPackage, AsAction, AsBuild, BlasTriangleGeometryInfo, TlasBuild,
ValidateAsActionsError,
},
resource::InvalidResourceError,
track::Tracker,
};
use crate::{lock::RwLockWriteGuard, resource::RawResourceAccess};
use crate::id::{BlasId, TlasId};
struct TriangleBufferStore<'a> {
struct TriangleBufferStore {
vertex_buffer: Arc<Buffer>,
vertex_transition: Option<PendingTransition<BufferUses>>,
index_buffer_transition: Option<(Arc<Buffer>, Option<PendingTransition<BufferUses>>)>,
transform_buffer_transition: Option<(Arc<Buffer>, Option<PendingTransition<BufferUses>>)>,
geometry: BlasTriangleGeometry<'a>,
geometry: BlasTriangleGeometryInfo,
ending_blas: Option<Arc<Blas>>,
}
@ -60,6 +65,14 @@ struct TlasStore<'a> {
}
impl Global {
fn resolve_blas_id(&self, blas_id: BlasId) -> Result<Arc<Blas>, InvalidResourceError> {
self.hub.blas_s.get(blas_id).get()
}
fn resolve_tlas_id(&self, tlas_id: TlasId) -> Result<Arc<Tlas>, InvalidResourceError> {
self.hub.tlas_s.get(tlas_id).get()
}
pub fn command_encoder_mark_acceleration_structures_built(
&self,
command_encoder_id: CommandEncoderId,
@ -73,7 +86,7 @@ impl Global {
let cmd_enc = hub.command_encoders.get(command_encoder_id);
let mut cmd_buf_data = cmd_enc.data.lock();
cmd_buf_data.record_with(
cmd_buf_data.with_buffer(
|cmd_buf_data| -> Result<(), BuildAccelerationStructureError> {
let device = &cmd_enc.device;
device.check_is_valid()?;
@ -161,115 +174,118 @@ impl Global {
.collect();
let mut cmd_buf_data = cmd_enc.data.lock();
cmd_buf_data.record_with(|cmd_buf_data| {
let blas_iter = trace_blas.iter().map(|blas_entry| {
let geometries = match &blas_entry.geometries {
TraceBlasGeometries::TriangleGeometries(triangle_geometries) => {
let iter = triangle_geometries.iter().map(|tg| BlasTriangleGeometry {
size: &tg.size,
vertex_buffer: tg.vertex_buffer,
index_buffer: tg.index_buffer,
transform_buffer: tg.transform_buffer,
first_vertex: tg.first_vertex,
vertex_stride: tg.vertex_stride,
first_index: tg.first_index,
transform_buffer_offset: tg.transform_buffer_offset,
});
BlasGeometries::TriangleGeometries(Box::new(iter))
}
};
BlasBuildEntry {
blas_id: blas_entry.blas_id,
geometries,
}
});
let tlas_iter = trace_tlas.iter().map(|tlas_package| {
let instances = tlas_package.instances.iter().map(|instance| {
instance.as_ref().map(|instance| TlasInstance {
blas_id: instance.blas_id,
transform: &instance.transform,
custom_data: instance.custom_data,
mask: instance.mask,
#[cfg(feature = "trace")]
if let Some(ref mut list) = cmd_buf_data.trace() {
list.push(crate::command::Command::BuildAccelerationStructures {
blas: trace_blas.clone(),
tlas: trace_tlas.clone(),
});
}
cmd_buf_data.push_with(|| -> Result<_, BuildAccelerationStructureError> {
let blas = trace_blas
.iter()
.map(|blas_entry| {
let geometries = match &blas_entry.geometries {
TraceBlasGeometries::TriangleGeometries(triangle_geometries) => {
let tri_geo = triangle_geometries
.iter()
.map(|tg| {
Ok(ArcBlasTriangleGeometry {
size: tg.size.clone(),
vertex_buffer: self.resolve_buffer_id(tg.vertex_buffer)?,
index_buffer: tg
.index_buffer
.map(|id| self.resolve_buffer_id(id))
.transpose()?,
transform_buffer: tg
.transform_buffer
.map(|id| self.resolve_buffer_id(id))
.transpose()?,
first_vertex: tg.first_vertex,
vertex_stride: tg.vertex_stride,
first_index: tg.first_index,
transform_buffer_offset: tg.transform_buffer_offset,
})
})
.collect::<Result<_, BuildAccelerationStructureError>>()?;
ArcBlasGeometries::TriangleGeometries(tri_geo)
}
};
Ok(ArcBlasBuildEntry {
blas: self.resolve_blas_id(blas_entry.blas_id)?,
geometries,
})
});
TlasPackage {
tlas_id: tlas_package.tlas_id,
instances: Box::new(instances),
lowest_unmodified: tlas_package.lowest_unmodified,
}
});
})
.collect::<Result<_, BuildAccelerationStructureError>>()?;
build_acceleration_structures(
cmd_buf_data,
hub,
&cmd_enc,
trace_blas.clone(),
trace_tlas.clone(),
blas_iter,
tlas_iter,
)
let tlas = trace_tlas
.iter()
.map(|tlas_package| {
let instances = tlas_package
.instances
.iter()
.map(|instance| {
instance
.as_ref()
.map(|instance| {
Ok(ArcTlasInstance {
blas: self.resolve_blas_id(instance.blas_id)?,
transform: instance.transform,
custom_data: instance.custom_data,
mask: instance.mask,
})
})
.transpose()
})
.collect::<Result<_, BuildAccelerationStructureError>>()?;
Ok(ArcTlasPackage {
tlas: self.resolve_tlas_id(tlas_package.tlas_id)?,
instances,
lowest_unmodified: tlas_package.lowest_unmodified,
})
})
.collect::<Result<_, BuildAccelerationStructureError>>()?;
Ok(ArcCommand::BuildAccelerationStructures { blas, tlas })
})
}
}
pub(crate) fn build_acceleration_structures<'a>(
cmd_buf_data: &'a mut CommandBufferMutable,
hub: &'a Hub,
cmd_enc: &'a Arc<CommandEncoder>,
trace_blas: Vec<TraceBlasBuildEntry>,
trace_tlas: Vec<TraceTlasPackage>,
blas_iter: impl Iterator<Item = BlasBuildEntry<'a>>,
tlas_iter: impl Iterator<Item = TlasPackage<'a>>,
pub(crate) fn build_acceleration_structures(
state: &mut EncodingState,
blas: Vec<ArcBlasBuildEntry>,
tlas: Vec<ArcTlasPackage>,
) -> Result<(), BuildAccelerationStructureError> {
#[cfg(feature = "trace")]
if let Some(ref mut list) = cmd_buf_data.trace_commands {
list.push(crate::command::Command::BuildAccelerationStructures {
blas: trace_blas,
tlas: trace_tlas,
});
}
#[cfg(not(feature = "trace"))]
{
let _ = trace_blas;
let _ = trace_tlas;
}
let device = &cmd_enc.device;
device.check_is_valid()?;
device.require_features(Features::EXPERIMENTAL_RAY_QUERY)?;
state
.device
.require_features(Features::EXPERIMENTAL_RAY_QUERY)?;
let mut build_command = AsBuild::default();
let mut buf_storage = Vec::new();
iter_blas(
blas_iter,
cmd_buf_data,
blas.into_iter(),
state.tracker,
&mut build_command,
&mut buf_storage,
hub,
)?;
let snatch_guard = device.snatchable_lock.read();
let mut input_barriers = Vec::<hal::BufferBarrier<dyn hal::DynBuffer>>::new();
let mut scratch_buffer_blas_size = 0;
let mut blas_storage = Vec::new();
iter_buffers(
state,
&mut buf_storage,
&snatch_guard,
&mut input_barriers,
cmd_buf_data,
&mut scratch_buffer_blas_size,
&mut blas_storage,
hub,
device.alignments.ray_tracing_scratch_buffer_alignment,
)?;
let mut tlas_lock_store = Vec::<(Option<TlasPackage>, Arc<Tlas>)>::new();
for package in tlas_iter {
let tlas = hub.tlas_s.get(package.tlas_id).get()?;
cmd_buf_data.trackers.tlas_s.insert_single(tlas.clone());
let mut tlas_lock_store = Vec::<(Option<ArcTlasPackage>, Arc<Tlas>)>::new();
for package in tlas.into_iter() {
let tlas = package.tlas.clone();
state.tracker.tlas_s.insert_single(tlas.clone());
tlas_lock_store.push((Some(package), tlas))
}
@ -283,7 +299,7 @@ pub(crate) fn build_acceleration_structures<'a>(
let scratch_buffer_offset = scratch_buffer_tlas_size;
scratch_buffer_tlas_size += align_to(
tlas.size_info.build_scratch_size as u32,
device.alignments.ray_tracing_scratch_buffer_alignment,
state.device.alignments.ray_tracing_scratch_buffer_alignment,
) as u64;
let first_byte_index = instance_buffer_staging_source.len();
@ -291,19 +307,18 @@ pub(crate) fn build_acceleration_structures<'a>(
let mut dependencies = Vec::new();
let mut instance_count = 0;
for instance in package.instances.flatten() {
for instance in package.instances.into_iter().flatten() {
if instance.custom_data >= (1u32 << 24u32) {
return Err(BuildAccelerationStructureError::TlasInvalidCustomIndex(
tlas.error_ident(),
));
}
let blas = hub.blas_s.get(instance.blas_id).get()?;
let blas = &instance.blas;
state.tracker.blas_s.insert_single(blas.clone());
cmd_buf_data.trackers.blas_s.insert_single(blas.clone());
instance_buffer_staging_source.extend(device.raw().tlas_instance_to_bytes(
instance_buffer_staging_source.extend(state.device.raw().tlas_instance_to_bytes(
hal::TlasInstance {
transform: *instance.transform,
transform: instance.transform,
custom_data: instance.custom_data,
mask: instance.mask,
blas_address: blas.handle,
@ -366,7 +381,7 @@ pub(crate) fn build_acceleration_structures<'a>(
return Ok(());
};
let scratch_buffer = ScratchBuffer::new(device, scratch_size)?;
let scratch_buffer = ScratchBuffer::new(state.device, scratch_size)?;
let scratch_buffer_barrier = hal::BufferBarrier::<dyn hal::DynBuffer> {
buffer: scratch_buffer.raw(),
@ -396,7 +411,7 @@ pub(crate) fn build_acceleration_structures<'a>(
mode: hal::AccelerationStructureBuildMode::Build,
flags: tlas.flags,
source_acceleration_structure: None,
destination_acceleration_structure: tlas.try_raw(&snatch_guard)?,
destination_acceleration_structure: tlas.try_raw(state.snatch_guard)?,
scratch_buffer: scratch_buffer.raw(),
scratch_buffer_offset: *scratch_buffer_offset,
})
@ -405,7 +420,7 @@ pub(crate) fn build_acceleration_structures<'a>(
let blas_present = !blas_storage.is_empty();
let tlas_present = !tlas_storage.is_empty();
let cmd_buf_raw = cmd_buf_data.encoder.open()?;
let raw_encoder = &mut state.raw_encoder;
let mut blas_s_compactable = Vec::new();
let mut descriptors = Vec::new();
@ -414,13 +429,13 @@ pub(crate) fn build_acceleration_structures<'a>(
descriptors.push(map_blas(
storage,
scratch_buffer.raw(),
&snatch_guard,
state.snatch_guard,
&mut blas_s_compactable,
)?);
}
build_blas(
cmd_buf_raw,
*raw_encoder,
blas_present,
tlas_present,
input_barriers,
@ -432,7 +447,7 @@ pub(crate) fn build_acceleration_structures<'a>(
if tlas_present {
let staging_buffer = if !instance_buffer_staging_source.is_empty() {
let mut staging_buffer = StagingBuffer::new(
device,
state.device,
wgt::BufferSize::new(instance_buffer_staging_source.len() as u64).unwrap(),
)?;
staging_buffer.write(&instance_buffer_staging_source);
@ -444,7 +459,7 @@ pub(crate) fn build_acceleration_structures<'a>(
unsafe {
if let Some(ref staging_buffer) = staging_buffer {
cmd_buf_raw.transition_buffers(&[hal::BufferBarrier::<dyn hal::DynBuffer> {
raw_encoder.transition_buffers(&[hal::BufferBarrier::<dyn hal::DynBuffer> {
buffer: staging_buffer.raw(),
usage: hal::StateTransition {
from: BufferUses::MAP_WRITE,
@ -472,7 +487,7 @@ pub(crate) fn build_acceleration_structures<'a>(
},
});
unsafe {
cmd_buf_raw.transition_buffers(&[hal::BufferBarrier::<dyn hal::DynBuffer> {
raw_encoder.transition_buffers(&[hal::BufferBarrier::<dyn hal::DynBuffer> {
buffer: tlas.instance_buffer.as_ref(),
usage: hal::StateTransition {
from: BufferUses::TOP_LEVEL_ACCELERATION_STRUCTURE_INPUT,
@ -484,9 +499,7 @@ pub(crate) fn build_acceleration_structures<'a>(
dst_offset: 0,
size,
};
cmd_buf_raw.copy_buffer_to_buffer(
// the range whose size we just checked end is at (at that point in time) instance_buffer_staging_source.len()
// and since instance_buffer_staging_source doesn't shrink we can un wrap this without a panic
raw_encoder.copy_buffer_to_buffer(
staging_buffer.as_ref().unwrap().raw(),
tlas.instance_buffer.as_ref(),
&[temp],
@ -495,11 +508,11 @@ pub(crate) fn build_acceleration_structures<'a>(
}
unsafe {
cmd_buf_raw.transition_buffers(&instance_buffer_barriers);
raw_encoder.transition_buffers(&instance_buffer_barriers);
cmd_buf_raw.build_acceleration_structures(&tlas_descriptors);
raw_encoder.build_acceleration_structures(&tlas_descriptors);
cmd_buf_raw.place_acceleration_structure_barrier(hal::AccelerationStructureBarrier {
raw_encoder.place_acceleration_structure_barrier(hal::AccelerationStructureBarrier {
usage: hal::StateTransition {
from: hal::AccelerationStructureUses::BUILD_OUTPUT,
to: hal::AccelerationStructureUses::SHADER_INPUT,
@ -508,17 +521,17 @@ pub(crate) fn build_acceleration_structures<'a>(
}
if let Some(staging_buffer) = staging_buffer {
cmd_buf_data
state
.temp_resources
.push(TempResource::StagingBuffer(staging_buffer));
}
}
cmd_buf_data
state
.temp_resources
.push(TempResource::ScratchBuffer(scratch_buffer));
cmd_buf_data.as_actions.push(AsAction::Build(build_command));
state.as_actions.push(AsAction::Build(build_command));
Ok(())
}
@ -600,23 +613,22 @@ impl CommandBufferMutable {
}
///iterates over the blas iterator, and it's geometry, pushing the buffers into a storage vector (and also some validation).
fn iter_blas<'a>(
blas_iter: impl Iterator<Item = BlasBuildEntry<'a>>,
cmd_buf_data: &mut CommandBufferMutable,
fn iter_blas(
blas_iter: impl Iterator<Item = ArcBlasBuildEntry>,
tracker: &mut Tracker,
build_command: &mut AsBuild,
buf_storage: &mut Vec<TriangleBufferStore<'a>>,
hub: &Hub,
buf_storage: &mut Vec<TriangleBufferStore>,
) -> Result<(), BuildAccelerationStructureError> {
let mut temp_buffer = Vec::new();
for entry in blas_iter {
let blas = hub.blas_s.get(entry.blas_id).get()?;
cmd_buf_data.trackers.blas_s.insert_single(blas.clone());
let blas = &entry.blas;
tracker.blas_s.insert_single(blas.clone());
build_command.blas_s_built.push(blas.clone());
match entry.geometries {
BlasGeometries::TriangleGeometries(triangle_geometries) => {
for (i, mesh) in triangle_geometries.enumerate() {
ArcBlasGeometries::TriangleGeometries(triangle_geometries) => {
for (i, mesh) in triangle_geometries.into_iter().enumerate() {
let size_desc = match &blas.sizes {
wgt::BlasGeometrySizeDescriptors::Triangles { descriptors } => descriptors,
};
@ -715,13 +727,12 @@ fn iter_blas<'a>(
blas.error_ident(),
));
}
let vertex_buffer = hub.buffers.get(mesh.vertex_buffer).get()?;
let vertex_pending = cmd_buf_data.trackers.buffers.set_single(
let vertex_buffer = mesh.vertex_buffer.clone();
let vertex_pending = tracker.buffers.set_single(
&vertex_buffer,
BufferUses::BOTTOM_LEVEL_ACCELERATION_STRUCTURE_INPUT,
);
let index_data = if let Some(index_id) = mesh.index_buffer {
let index_buffer = hub.buffers.get(index_id).get()?;
let index_data = if let Some(index_buffer) = mesh.index_buffer {
if mesh.first_index.is_none()
|| mesh.size.index_count.is_none()
|| mesh.size.index_count.is_none()
@ -730,7 +741,7 @@ fn iter_blas<'a>(
index_buffer.error_ident(),
));
}
let data = cmd_buf_data.trackers.buffers.set_single(
let data = tracker.buffers.set_single(
&index_buffer,
BufferUses::BOTTOM_LEVEL_ACCELERATION_STRUCTURE_INPUT,
);
@ -738,7 +749,7 @@ fn iter_blas<'a>(
} else {
None
};
let transform_data = if let Some(transform_id) = mesh.transform_buffer {
let transform_data = if let Some(transform_buffer) = mesh.transform_buffer {
if !blas
.flags
.contains(wgt::AccelerationStructureFlags::USE_TRANSFORM)
@ -747,13 +758,12 @@ fn iter_blas<'a>(
blas.error_ident(),
));
}
let transform_buffer = hub.buffers.get(transform_id).get()?;
if mesh.transform_buffer_offset.is_none() {
return Err(BuildAccelerationStructureError::MissingAssociatedData(
transform_buffer.error_ident(),
));
}
let data = cmd_buf_data.trackers.buffers.set_single(
let data = tracker.buffers.set_single(
&transform_buffer,
BufferUses::BOTTOM_LEVEL_ACCELERATION_STRUCTURE_INPUT,
);
@ -774,13 +784,19 @@ fn iter_blas<'a>(
vertex_transition: vertex_pending,
index_buffer_transition: index_data,
transform_buffer_transition: transform_data,
geometry: mesh,
geometry: BlasTriangleGeometryInfo {
size: mesh.size,
first_vertex: mesh.first_vertex,
vertex_stride: mesh.vertex_stride,
first_index: mesh.first_index,
transform_buffer_offset: mesh.transform_buffer_offset,
},
ending_blas: None,
});
}
if let Some(last) = temp_buffer.last_mut() {
last.ending_blas = Some(blas);
last.ending_blas = Some(blas.clone());
buf_storage.append(&mut temp_buffer);
}
}
@ -790,29 +806,30 @@ fn iter_blas<'a>(
}
/// Iterates over the buffers generated in [iter_blas], convert the barriers into hal barriers, and the triangles into [hal::AccelerationStructureEntries] (and also some validation).
fn iter_buffers<'a, 'b>(
buf_storage: &'a mut Vec<TriangleBufferStore<'b>>,
snatch_guard: &'a SnatchGuard,
input_barriers: &mut Vec<hal::BufferBarrier<'a, dyn hal::DynBuffer>>,
cmd_buf_data: &mut CommandBufferMutable,
///
/// `'buffers` is the lifetime of `&dyn hal::DynBuffer` in our working data,
/// i.e., needs to span until `build_acceleration_structures` finishes encoding.
/// `'snatch_guard` is the lifetime of the snatch lock acquisition.
fn iter_buffers<'snatch_guard: 'buffers, 'buffers>(
state: &mut EncodingState<'snatch_guard, '_>,
buf_storage: &'buffers mut Vec<TriangleBufferStore>,
input_barriers: &mut Vec<hal::BufferBarrier<'buffers, dyn hal::DynBuffer>>,
scratch_buffer_blas_size: &mut u64,
blas_storage: &mut Vec<BlasStore<'a>>,
hub: &Hub,
ray_tracing_scratch_buffer_alignment: u32,
blas_storage: &mut Vec<BlasStore<'buffers>>,
) -> Result<(), BuildAccelerationStructureError> {
let mut triangle_entries =
Vec::<hal::AccelerationStructureTriangles<dyn hal::DynBuffer>>::new();
for buf in buf_storage {
let mesh = &buf.geometry;
let vertex_buffer = {
let vertex_buffer = buf.vertex_buffer.as_ref();
let vertex_raw = vertex_buffer.try_raw(snatch_guard)?;
let vertex_raw = buf.vertex_buffer.as_ref().try_raw(state.snatch_guard)?;
let vertex_buffer = &buf.vertex_buffer;
vertex_buffer.check_usage(BufferUsages::BLAS_INPUT)?;
if let Some(barrier) = buf
.vertex_transition
.take()
.map(|pending| pending.into_hal(vertex_buffer, snatch_guard))
.map(|pending| pending.into_hal(buf.vertex_buffer.as_ref(), state.snatch_guard))
{
input_barriers.push(barrier);
}
@ -826,9 +843,9 @@ fn iter_buffers<'a, 'b>(
));
}
let vertex_buffer_offset = mesh.first_vertex as u64 * mesh.vertex_stride;
cmd_buf_data.buffer_memory_init_actions.extend(
state.buffer_memory_init_actions.extend(
vertex_buffer.initialization_status.read().create_action(
&hub.buffers.get(mesh.vertex_buffer).get()?,
vertex_buffer,
vertex_buffer_offset
..(vertex_buffer_offset
+ mesh.size.vertex_count as u64 * mesh.vertex_stride),
@ -840,12 +857,12 @@ fn iter_buffers<'a, 'b>(
let index_buffer = if let Some((ref mut index_buffer, ref mut index_pending)) =
buf.index_buffer_transition
{
let index_raw = index_buffer.try_raw(snatch_guard)?;
let index_raw = index_buffer.try_raw(state.snatch_guard)?;
index_buffer.check_usage(BufferUsages::BLAS_INPUT)?;
if let Some(barrier) = index_pending
.take()
.map(|pending| pending.into_hal(index_buffer, snatch_guard))
.map(|pending| pending.into_hal(index_buffer, state.snatch_guard))
{
input_barriers.push(barrier);
}
@ -867,7 +884,7 @@ fn iter_buffers<'a, 'b>(
));
}
cmd_buf_data.buffer_memory_init_actions.extend(
state.buffer_memory_init_actions.extend(
index_buffer.initialization_status.read().create_action(
index_buffer,
offset..(offset + index_buffer_size),
@ -886,12 +903,12 @@ fn iter_buffers<'a, 'b>(
transform_buffer.error_ident(),
));
}
let transform_raw = transform_buffer.try_raw(snatch_guard)?;
let transform_raw = transform_buffer.try_raw(state.snatch_guard)?;
transform_buffer.check_usage(BufferUsages::BLAS_INPUT)?;
if let Some(barrier) = transform_pending
.take()
.map(|pending| pending.into_hal(transform_buffer, snatch_guard))
.map(|pending| pending.into_hal(transform_buffer, state.snatch_guard))
{
input_barriers.push(barrier);
}
@ -912,7 +929,7 @@ fn iter_buffers<'a, 'b>(
48 + offset,
));
}
cmd_buf_data.buffer_memory_init_actions.extend(
state.buffer_memory_init_actions.extend(
transform_buffer.initialization_status.read().create_action(
transform_buffer,
offset..(offset + 48),
@ -952,7 +969,7 @@ fn iter_buffers<'a, 'b>(
let scratch_buffer_offset = *scratch_buffer_blas_size;
*scratch_buffer_blas_size += align_to(
blas.size_info.build_scratch_size as u32,
ray_tracing_scratch_buffer_alignment,
state.device.alignments.ray_tracing_scratch_buffer_alignment,
) as u64;
blas_storage.push(BlasStore {

View File

@ -1,5 +1,6 @@
use alloc::{borrow::Cow, sync::Arc, vec::Vec};
use core::{fmt, num::NonZeroU32, ops::Range, str};
use core::{convert::Infallible, fmt, num::NonZeroU32, ops::Range, str};
use smallvec::SmallVec;
use arrayvec::ArrayVec;
use thiserror::Error;
@ -11,8 +12,8 @@ use wgt::{
use crate::command::{
encoder::EncodingState, pass, pass_base, pass_try, validate_and_begin_occlusion_query,
validate_and_begin_pipeline_statistics_query, CommandBufferMutable, DebugGroupError,
EncoderStateError, InnerCommandEncoder, PassStateError, TimestampWritesError,
validate_and_begin_pipeline_statistics_query, ArcCommand, DebugGroupError, EncoderStateError,
InnerCommandEncoder, PassStateError, TimestampWritesError,
};
use crate::pipeline::{RenderPipeline, VertexStep};
use crate::resource::RawResourceAccess;
@ -117,7 +118,7 @@ impl<V: Copy + Default> PassChannel<Option<V>> {
}
}
#[derive(Debug)]
#[derive(Clone, Debug)]
pub enum ResolvedPassChannel<V> {
ReadOnly,
Operational(wgt::Operations<V>),
@ -180,6 +181,11 @@ pub struct RenderPassColorAttachment<TV = id::TextureViewId> {
pub type ArcRenderPassColorAttachment = RenderPassColorAttachment<Arc<TextureView>>;
// Avoid allocation in the common case that there is only one color attachment,
// but don't bloat `ArcCommand::RunRenderPass` excessively.
pub type ArcRenderPassColorAttachmentArray =
SmallVec<[Option<RenderPassColorAttachment<Arc<TextureView>>>; 1]>;
impl ArcRenderPassColorAttachment {
fn hal_ops(&self) -> hal::AttachmentOps {
load_hal_ops(self.load_op) | store_hal_ops(self.store_op)
@ -207,7 +213,7 @@ pub struct RenderPassDepthStencilAttachment {
}
/// Describes a depth/stencil attachment to a render pass.
#[derive(Debug)]
#[derive(Clone, Debug)]
pub struct ArcRenderPassDepthStencilAttachment {
/// The view to use as an attachment.
pub view: Arc<TextureView>,
@ -494,7 +500,7 @@ impl VertexState {
}
}
struct State<'scope, 'snatch_guard, 'cmd_enc, 'raw_encoder> {
struct State<'scope, 'snatch_guard, 'cmd_enc> {
pipeline_flags: PipelineFlags,
blend_constant: OptionalState,
stencil_reference: u32,
@ -504,15 +510,13 @@ struct State<'scope, 'snatch_guard, 'cmd_enc, 'raw_encoder> {
info: RenderPassInfo,
pass: pass::PassState<'scope, 'snatch_guard, 'cmd_enc, 'raw_encoder>,
pass: pass::PassState<'scope, 'snatch_guard, 'cmd_enc>,
active_occlusion_query: Option<(Arc<QuerySet>, u32)>,
active_pipeline_statistics_query: Option<(Arc<QuerySet>, u32)>,
}
impl<'scope, 'snatch_guard, 'cmd_enc, 'raw_encoder>
State<'scope, 'snatch_guard, 'cmd_enc, 'raw_encoder>
{
impl<'scope, 'snatch_guard, 'cmd_enc> State<'scope, 'snatch_guard, 'cmd_enc> {
fn is_ready(&self, family: DrawCommandFamily) -> Result<(), DrawError> {
if let Some(pipeline) = self.pipeline.as_ref() {
self.pass.binder.check_compatibility(pipeline.as_ref())?;
@ -953,14 +957,11 @@ impl RenderPassInfo {
fn start(
device: &Arc<Device>,
hal_label: Option<&str>,
color_attachments: ArrayVec<
Option<ArcRenderPassColorAttachment>,
{ hal::MAX_COLOR_ATTACHMENTS },
>,
color_attachments: &[Option<ArcRenderPassColorAttachment>],
mut depth_stencil_attachment: Option<ArcRenderPassDepthStencilAttachment>,
mut timestamp_writes: Option<ArcPassTimestampWrites>,
mut occlusion_query_set: Option<Arc<QuerySet>>,
encoder: &mut InnerCommandEncoder,
encoder: &mut dyn hal::DynCommandEncoder,
trackers: &mut Tracker,
texture_memory_actions: &mut CommandBufferTextureMemoryActions,
pending_query_resets: &mut QueryResetMap,
@ -1422,7 +1423,6 @@ impl RenderPassInfo {
};
unsafe {
encoder
.raw
.begin_render_pass(&hal_desc)
.map_err(|e| device.handle_hal_error(e))?;
};
@ -1438,9 +1438,9 @@ impl RenderPassInfo {
if let Some(at) = depth_stencil_attachment.take() {
trackers.views.insert_single(at.view.clone());
}
for at in color_attachments.into_iter().flatten() {
for at in color_attachments.iter().flatten() {
trackers.views.insert_single(at.view.clone());
if let Some(resolve_target) = at.resolve_target {
if let Some(resolve_target) = at.resolve_target.clone() {
trackers.views.insert_single(resolve_target);
}
}
@ -1740,7 +1740,7 @@ impl Global {
pub fn render_pass_end_with_unresolved_commands(
&self,
encoder_id: id::CommandEncoderId,
base: BasePass<super::RenderCommand, core::convert::Infallible>,
base: BasePass<super::RenderCommand, Infallible>,
color_attachments: &[Option<RenderPassColorAttachment>],
depth_stencil_attachment: Option<&RenderPassDepthStencilAttachment>,
timestamp_writes: Option<&PassTimestampWrites>,
@ -1815,87 +1815,97 @@ impl Global {
let cmd_enc = pass.parent.take().ok_or(EncoderStateError::Ended)?;
let mut cmd_buf_data = cmd_enc.data.lock();
if let Some(err) = pass.base.error.take() {
if matches!(
err,
RenderPassError {
inner: RenderPassErrorInner::EncoderState(EncoderStateError::Ended),
scope: _,
}
) {
// If the encoder was already finished at time of pass creation,
// then it was not put in the locked state, so we need to
// generate a validation error here due to the encoder not being
// locked. The encoder already has a copy of the error.
return Err(EncoderStateError::Ended);
} else {
// If the pass is invalid, invalidate the parent encoder and return.
// Since we do not track the state of an invalid encoder, it is not
// necessary to unlock it.
cmd_buf_data.invalidate(err);
return Ok(());
}
cmd_buf_data.unlock_encoder()?;
let base = pass.base.take();
if matches!(
base,
Err(RenderPassError {
inner: RenderPassErrorInner::EncoderState(EncoderStateError::Ended),
scope: _,
})
) {
// If the encoder was already finished at time of pass creation,
// then it was not put in the locked state, so we need to
// generate a validation error here and now due to the encoder not
// being locked. The encoder already holds an error from when the
// pass was opened, or earlier.
//
// All other errors are propagated to the encoder within `push_with`,
// and will be reported later.
return Err(EncoderStateError::Ended);
}
cmd_buf_data.unlock_and_record(|cmd_buf_data| -> Result<(), RenderPassError> {
encode_render_pass(cmd_buf_data, &cmd_enc, pass)
cmd_buf_data.push_with(|| -> Result<_, RenderPassError> {
Ok(ArcCommand::RunRenderPass {
pass: base?,
color_attachments: SmallVec::from(pass.color_attachments.as_slice()),
depth_stencil_attachment: pass.depth_stencil_attachment.take(),
timestamp_writes: pass.timestamp_writes.take(),
occlusion_query_set: pass.occlusion_query_set.take(),
})
})
}
}
fn encode_render_pass(
cmd_buf_data: &mut CommandBufferMutable,
cmd_enc: &Arc<CommandEncoder>,
pass: &mut RenderPass,
pub(super) fn encode_render_pass(
parent_state: &mut EncodingState<InnerCommandEncoder>,
mut base: BasePass<ArcRenderCommand, Infallible>,
color_attachments: ArcRenderPassColorAttachmentArray,
mut depth_stencil_attachment: Option<ArcRenderPassDepthStencilAttachment>,
mut timestamp_writes: Option<ArcPassTimestampWrites>,
occlusion_query_set: Option<Arc<QuerySet>>,
) -> Result<(), RenderPassError> {
let pass_scope = PassErrorScope::Pass;
let device = &cmd_enc.device;
device.check_is_valid().map_pass_err(pass_scope)?;
let snatch_guard = &device.snatchable_lock.read();
let base = &mut pass.base;
let device = parent_state.device;
let mut indirect_draw_validation_batcher = crate::indirect_validation::DrawBatcher::new();
// We automatically keep extending command buffers over time, and because
// we want to insert a command buffer _before_ what we're about to record,
// we need to make sure to close the previous one.
parent_state
.raw_encoder
.close_if_open()
.map_pass_err(pass_scope)?;
let raw_encoder = parent_state
.raw_encoder
.open_pass(base.label.as_deref())
.map_pass_err(pass_scope)?;
let (scope, pending_discard_init_fixups, mut pending_query_resets) = {
let encoder = &mut cmd_buf_data.encoder;
let tracker = &mut cmd_buf_data.trackers;
let buffer_memory_init_actions = &mut cmd_buf_data.buffer_memory_init_actions;
let texture_memory_actions = &mut cmd_buf_data.texture_memory_actions;
// We automatically keep extending command buffers over time, and because
// we want to insert a command buffer _before_ what we're about to record,
// we need to make sure to close the previous one.
encoder.close_if_open().map_pass_err(pass_scope)?;
encoder
.open_pass(base.label.as_deref())
.map_pass_err(pass_scope)?;
let mut pending_query_resets = QueryResetMap::new();
let mut pending_discard_init_fixups = SurfacesInDiscardState::new();
let info = RenderPassInfo::start(
device,
hal_label(base.label.as_deref(), device.instance_flags),
pass.color_attachments.take(),
pass.depth_stencil_attachment.take(),
pass.timestamp_writes.take(),
&color_attachments,
depth_stencil_attachment.take(),
timestamp_writes.take(),
// Still needed down the line.
// TODO(wumpf): by restructuring the code, we could get rid of some of this Arc clone.
pass.occlusion_query_set.clone(),
encoder,
tracker,
texture_memory_actions,
occlusion_query_set.clone(),
raw_encoder,
parent_state.tracker,
parent_state.texture_memory_actions,
&mut pending_query_resets,
&mut pending_discard_init_fixups,
snatch_guard,
parent_state.snatch_guard,
)
.map_pass_err(pass_scope)?;
let indices = &device.tracker_indices;
tracker.buffers.set_size(indices.buffers.size());
tracker.textures.set_size(indices.textures.size());
parent_state
.tracker
.buffers
.set_size(indices.buffers.size());
parent_state
.tracker
.textures
.set_size(indices.textures.size());
let mut debug_scope_depth = 0;
@ -1912,14 +1922,15 @@ fn encode_render_pass(
pass: pass::PassState {
base: EncodingState {
device,
raw_encoder: encoder.raw.as_mut(),
tracker,
buffer_memory_init_actions,
texture_memory_actions,
as_actions: &mut cmd_buf_data.as_actions,
indirect_draw_validation_resources: &mut cmd_buf_data
raw_encoder,
tracker: parent_state.tracker,
buffer_memory_init_actions: parent_state.buffer_memory_init_actions,
texture_memory_actions: parent_state.texture_memory_actions,
as_actions: parent_state.as_actions,
temp_resources: parent_state.temp_resources,
indirect_draw_validation_resources: parent_state
.indirect_draw_validation_resources,
snatch_guard,
snatch_guard: parent_state.snatch_guard,
debug_scope_depth: &mut debug_scope_depth,
},
pending_discard_init_fixups,
@ -1946,7 +1957,7 @@ fn encode_render_pass(
let scope = PassErrorScope::SetBindGroup;
pass::set_bind_group::<RenderPassErrorInner>(
&mut state.pass,
cmd_enc.as_ref(),
device,
&base.dynamic_offsets,
index,
num_dynamic_offsets,
@ -1957,7 +1968,7 @@ fn encode_render_pass(
}
ArcRenderCommand::SetPipeline(pipeline) => {
let scope = PassErrorScope::SetPipelineRender;
set_pipeline(&mut state, cmd_enc, pipeline).map_pass_err(scope)?;
set_pipeline(&mut state, device, pipeline).map_pass_err(scope)?;
}
ArcRenderCommand::SetIndexBuffer {
buffer,
@ -1966,7 +1977,7 @@ fn encode_render_pass(
size,
} => {
let scope = PassErrorScope::SetIndexBuffer;
set_index_buffer(&mut state, cmd_enc, buffer, index_format, offset, size)
set_index_buffer(&mut state, device, buffer, index_format, offset, size)
.map_pass_err(scope)?;
}
ArcRenderCommand::SetVertexBuffer {
@ -1976,7 +1987,7 @@ fn encode_render_pass(
size,
} => {
let scope = PassErrorScope::SetVertexBuffer;
set_vertex_buffer(&mut state, cmd_enc, slot, buffer, offset, size)
set_vertex_buffer(&mut state, device, slot, buffer, offset, size)
.map_pass_err(scope)?;
}
ArcRenderCommand::SetBlendConstant(ref color) => {
@ -2087,7 +2098,7 @@ fn encode_render_pass(
multi_draw_indirect(
&mut state,
&mut indirect_draw_validation_batcher,
cmd_enc,
device,
buffer,
offset,
count,
@ -2109,7 +2120,7 @@ fn encode_render_pass(
};
multi_draw_indirect_count(
&mut state,
cmd_enc,
device,
buffer,
offset,
count_buffer,
@ -2137,7 +2148,7 @@ fn encode_render_pass(
let scope = PassErrorScope::WriteTimestamp;
pass::write_timestamp::<RenderPassErrorInner>(
&mut state.pass,
cmd_enc.as_ref(),
device,
Some(&mut pending_query_resets),
query_set,
query_index,
@ -2148,8 +2159,7 @@ fn encode_render_pass(
api_log!("RenderPass::begin_occlusion_query {query_index}");
let scope = PassErrorScope::BeginOcclusionQuery;
let query_set = pass
.occlusion_query_set
let query_set = occlusion_query_set
.clone()
.ok_or(RenderPassErrorInner::MissingOcclusionQuerySet)
.map_pass_err(scope)?;
@ -2188,7 +2198,7 @@ fn encode_render_pass(
query_set,
state.pass.base.raw_encoder,
&mut state.pass.base.tracker.query_sets,
cmd_enc.as_ref(),
device,
query_index,
Some(&mut pending_query_resets),
&mut state.active_pipeline_statistics_query,
@ -2210,7 +2220,7 @@ fn encode_render_pass(
execute_bundle(
&mut state,
&mut indirect_draw_validation_batcher,
cmd_enc,
device,
bundle,
)
.map_pass_err(scope)?;
@ -2240,12 +2250,12 @@ fn encode_render_pass(
let pending_discard_init_fixups = state.pass.pending_discard_init_fixups;
encoder.close().map_pass_err(pass_scope)?;
parent_state.raw_encoder.close().map_pass_err(pass_scope)?;
(trackers, pending_discard_init_fixups, pending_query_resets)
};
let encoder = &mut cmd_buf_data.encoder;
let tracker = &mut cmd_buf_data.trackers;
let encoder = &mut parent_state.raw_encoder;
let tracker = &mut parent_state.tracker;
{
let transit = encoder
@ -2259,22 +2269,27 @@ fn encode_render_pass(
pending_discard_init_fixups.into_iter(),
transit,
&mut tracker.textures,
&cmd_enc.device,
snatch_guard,
device,
parent_state.snatch_guard,
);
pending_query_resets.reset_queries(transit);
CommandEncoder::insert_barriers_from_scope(transit, tracker, &scope, snatch_guard);
CommandEncoder::insert_barriers_from_scope(
transit,
tracker,
&scope,
parent_state.snatch_guard,
);
if let Some(ref indirect_validation) = device.indirect_validation {
indirect_validation
.draw
.inject_validation_pass(
device,
snatch_guard,
&mut cmd_buf_data.indirect_draw_validation_resources,
&mut cmd_buf_data.temp_resources,
parent_state.snatch_guard,
parent_state.indirect_draw_validation_resources,
parent_state.temp_resources,
transit,
indirect_draw_validation_batcher,
)
@ -2289,7 +2304,7 @@ fn encode_render_pass(
fn set_pipeline(
state: &mut State,
cmd_enc: &Arc<CommandEncoder>,
device: &Arc<Device>,
pipeline: Arc<RenderPipeline>,
) -> Result<(), RenderPassErrorInner> {
api_log!("RenderPass::set_pipeline {}", pipeline.error_ident());
@ -2304,7 +2319,7 @@ fn set_pipeline(
.insert_single(pipeline)
.clone();
pipeline.same_device_as(cmd_enc.as_ref())?;
pipeline.same_device(device)?;
state
.info
@ -2359,7 +2374,7 @@ fn set_pipeline(
// This function is duplicative of `bundle::set_index_buffer`.
fn set_index_buffer(
state: &mut State,
cmd_enc: &Arc<CommandEncoder>,
device: &Arc<Device>,
buffer: Arc<crate::resource::Buffer>,
index_format: IndexFormat,
offset: u64,
@ -2373,7 +2388,7 @@ fn set_index_buffer(
.buffers
.merge_single(&buffer, wgt::BufferUses::INDEX)?;
buffer.same_device_as(cmd_enc.as_ref())?;
buffer.same_device(device)?;
buffer.check_usage(BufferUsages::INDEX)?;
@ -2411,7 +2426,7 @@ fn set_index_buffer(
// This function is duplicative of `render::set_vertex_buffer`.
fn set_vertex_buffer(
state: &mut State,
cmd_enc: &Arc<CommandEncoder>,
device: &Arc<Device>,
slot: u32,
buffer: Arc<crate::resource::Buffer>,
offset: u64,
@ -2428,7 +2443,7 @@ fn set_vertex_buffer(
.buffers
.merge_single(&buffer, wgt::BufferUses::VERTEX)?;
buffer.same_device_as(cmd_enc.as_ref())?;
buffer.same_device(device)?;
let max_vertex_buffers = state.pass.base.device.limits.max_vertex_buffers;
if slot >= max_vertex_buffers {
@ -2688,7 +2703,7 @@ fn draw_mesh_tasks(
fn multi_draw_indirect(
state: &mut State,
indirect_draw_validation_batcher: &mut crate::indirect_validation::DrawBatcher,
cmd_enc: &Arc<CommandEncoder>,
device: &Arc<Device>,
indirect_buffer: Arc<crate::resource::Buffer>,
offset: u64,
count: u32,
@ -2707,7 +2722,7 @@ fn multi_draw_indirect(
.device
.require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION)?;
indirect_buffer.same_device_as(cmd_enc.as_ref())?;
indirect_buffer.same_device(device)?;
indirect_buffer.check_usage(BufferUsages::INDIRECT)?;
indirect_buffer.check_destroyed(state.pass.base.snatch_guard)?;
@ -2866,7 +2881,7 @@ fn multi_draw_indirect(
fn multi_draw_indirect_count(
state: &mut State,
cmd_enc: &Arc<CommandEncoder>,
device: &Arc<Device>,
indirect_buffer: Arc<crate::resource::Buffer>,
offset: u64,
count_buffer: Arc<crate::resource::Buffer>,
@ -2895,8 +2910,8 @@ fn multi_draw_indirect_count(
.device
.require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION)?;
indirect_buffer.same_device_as(cmd_enc.as_ref())?;
count_buffer.same_device_as(cmd_enc.as_ref())?;
indirect_buffer.same_device(device)?;
count_buffer.same_device(device)?;
state
.pass
@ -2989,14 +3004,14 @@ fn multi_draw_indirect_count(
fn execute_bundle(
state: &mut State,
indirect_draw_validation_batcher: &mut crate::indirect_validation::DrawBatcher,
cmd_enc: &Arc<CommandEncoder>,
device: &Arc<Device>,
bundle: Arc<super::RenderBundle>,
) -> Result<(), RenderPassErrorInner> {
api_log!("RenderPass::execute_bundle {}", bundle.error_ident());
let bundle = state.pass.base.tracker.bundles.insert_single(bundle);
bundle.same_device_as(cmd_enc.as_ref())?;
bundle.same_device(device)?;
state
.info

View File

@ -12,10 +12,12 @@ use wgt::{
use crate::command::Command as TraceCommand;
use crate::{
api_log,
command::{clear_texture, CommandEncoderError, EncoderStateError},
device::{Device, MissingDownlevelFlags},
command::{
clear_texture, encoder::EncodingState, ArcCommand, CommandEncoderError, EncoderStateError,
},
device::MissingDownlevelFlags,
global::Global,
id::{BufferId, CommandEncoderId},
id::{BufferId, CommandEncoderId, TextureId},
init_tracker::{
has_copy_partial_init_tracker_coverage, MemoryInitKind, TextureInitRange,
TextureInitTrackerAction,
@ -24,13 +26,12 @@ use crate::{
Buffer, MissingBufferUsageError, MissingTextureUsageError, ParentDevice, RawResourceAccess,
Texture, TextureErrorDimension,
},
snatch::SnatchGuard,
};
use super::{ClearError, CommandBufferMutable};
use super::ClearError;
use super::TexelCopyBufferInfo;
use super::TexelCopyTextureInfo;
type TexelCopyBufferInfo = wgt::TexelCopyBufferInfo<BufferId>;
type TexelCopyTextureInfo = wgt::TexelCopyTextureInfo<Arc<Texture>>;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CopySide {
@ -629,13 +630,11 @@ pub(crate) fn validate_copy_within_same_texture<T>(
}
fn handle_texture_init(
state: &mut EncodingState,
init_kind: MemoryInitKind,
cmd_buf_data: &mut CommandBufferMutable,
device: &Device,
copy_texture: &TexelCopyTextureInfo,
copy_size: &Extent3d,
texture: &Arc<Texture>,
snatch_guard: &SnatchGuard<'_>,
) -> Result<(), ClearError> {
let init_action = TextureInitTrackerAction {
texture: texture.clone(),
@ -648,13 +647,12 @@ fn handle_texture_init(
};
// Register the init action.
let immediate_inits = cmd_buf_data
let immediate_inits = state
.texture_memory_actions
.register_init_action(&{ init_action });
// In rare cases we may need to insert an init operation immediately onto the command buffer.
if !immediate_inits.is_empty() {
let cmd_buf_raw = cmd_buf_data.encoder.open()?;
for init in immediate_inits {
clear_texture(
&init.texture,
@ -662,12 +660,12 @@ fn handle_texture_init(
mip_range: init.mip_level..(init.mip_level + 1),
layer_range: init.layer..(init.layer + 1),
},
cmd_buf_raw,
&mut cmd_buf_data.trackers.textures,
&device.alignments,
device.zero_buffer.as_ref(),
snatch_guard,
device.instance_flags,
state.raw_encoder,
&mut state.tracker.textures,
&state.device.alignments,
state.device.zero_buffer.as_ref(),
state.snatch_guard,
state.device.instance_flags,
)?;
}
}
@ -680,21 +678,17 @@ fn handle_texture_init(
/// Ensure the source texture of a transfer is in the right initialization
/// state, and record the state for after the transfer operation.
fn handle_src_texture_init(
cmd_buf_data: &mut CommandBufferMutable,
device: &Device,
state: &mut EncodingState,
source: &TexelCopyTextureInfo,
copy_size: &Extent3d,
texture: &Arc<Texture>,
snatch_guard: &SnatchGuard<'_>,
) -> Result<(), TransferError> {
handle_texture_init(
state,
MemoryInitKind::NeedsInitializedMemory,
cmd_buf_data,
device,
source,
copy_size,
texture,
snatch_guard,
)?;
Ok(())
}
@ -704,12 +698,10 @@ fn handle_src_texture_init(
/// Ensure the destination texture of a transfer is in the right initialization
/// state, and record the state for after the transfer operation.
fn handle_dst_texture_init(
cmd_buf_data: &mut CommandBufferMutable,
device: &Device,
destination: &TexelCopyTextureInfo,
state: &mut EncodingState,
destination: &wgt::TexelCopyTextureInfo<Arc<Texture>>,
copy_size: &Extent3d,
texture: &Arc<Texture>,
snatch_guard: &SnatchGuard<'_>,
) -> Result<(), TransferError> {
// Attention: If we don't write full texture subresources, we need to a full
// clear first since we don't track subrects. This means that in rare cases
@ -725,15 +717,7 @@ fn handle_dst_texture_init(
MemoryInitKind::ImplicitlyInitialized
};
handle_texture_init(
dst_init_kind,
cmd_buf_data,
device,
destination,
copy_size,
texture,
snatch_guard,
)?;
handle_texture_init(state, dst_init_kind, destination, copy_size, texture)?;
Ok(())
}
@ -742,9 +726,8 @@ fn handle_dst_texture_init(
/// Ensures that the transfer will not read from uninitialized memory, and updates
/// the initialization state information to reflect the transfer.
fn handle_buffer_init(
cmd_buf_data: &mut CommandBufferMutable,
info: &TexelCopyBufferInfo,
buffer: &Arc<Buffer>,
state: &mut EncodingState,
info: &wgt::TexelCopyBufferInfo<Arc<Buffer>>,
direction: CopySide,
required_buffer_bytes_in_copy: BufferAddress,
is_contiguous: bool,
@ -752,6 +735,7 @@ fn handle_buffer_init(
const ALIGN_SIZE: BufferAddress = wgt::COPY_BUFFER_ALIGNMENT;
const ALIGN_MASK: BufferAddress = wgt::COPY_BUFFER_ALIGNMENT - 1;
let buffer = &info.buffer;
let start = info.layout.offset;
let end = info.layout.offset + required_buffer_bytes_in_copy;
if !is_contiguous || direction == CopySide::Source {
@ -767,13 +751,13 @@ fn handle_buffer_init(
// Adjust the start/end outwards to 4B alignment.
let aligned_start = start & !ALIGN_MASK;
let aligned_end = (end + ALIGN_MASK) & !ALIGN_MASK;
cmd_buf_data.buffer_memory_init_actions.extend(
buffer.initialization_status.read().create_action(
state
.buffer_memory_init_actions
.extend(buffer.initialization_status.read().create_action(
buffer,
aligned_start..aligned_end,
MemoryInitKind::NeedsInitializedMemory,
),
);
));
} else {
// If the transfer will write a contiguous region of the buffer, then we
// don't need to initialize that region.
@ -787,7 +771,7 @@ fn handle_buffer_init(
let aligned_start = (start + ALIGN_MASK) & !ALIGN_MASK;
let aligned_end = end & !ALIGN_MASK;
if aligned_start != start {
cmd_buf_data.buffer_memory_init_actions.extend(
state.buffer_memory_init_actions.extend(
buffer.initialization_status.read().create_action(
buffer,
aligned_start - ALIGN_SIZE..aligned_start,
@ -796,7 +780,7 @@ fn handle_buffer_init(
);
}
if aligned_start != aligned_end {
cmd_buf_data.buffer_memory_init_actions.extend(
state.buffer_memory_init_actions.extend(
buffer.initialization_status.read().create_action(
buffer,
aligned_start..aligned_end,
@ -810,7 +794,7 @@ fn handle_buffer_init(
// final size of the buffer. The final size of the buffer is not
// readily available, but was rounded up to COPY_BUFFER_ALIGNMENT,
// so no overrun is possible.
cmd_buf_data.buffer_memory_init_actions.extend(
state.buffer_memory_init_actions.extend(
buffer.initialization_status.read().create_action(
buffer,
aligned_end..aligned_end + ALIGN_SIZE,
@ -840,17 +824,26 @@ impl Global {
let cmd_enc = hub.command_encoders.get(command_encoder_id);
let mut cmd_buf_data = cmd_enc.data.lock();
cmd_buf_data.record_with(|cmd_buf_data| -> Result<(), CommandEncoderError> {
copy_buffer_to_buffer(
cmd_buf_data,
hub,
&cmd_enc,
source,
source_offset,
destination,
destination_offset,
#[cfg(feature = "trace")]
if let Some(ref mut list) = cmd_buf_data.trace() {
list.push(TraceCommand::CopyBufferToBuffer {
src: source,
src_offset: source_offset,
dst: destination,
dst_offset: destination_offset,
size,
)
});
}
cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {
Ok(ArcCommand::CopyBufferToBuffer {
src: self.resolve_buffer_id(source)?,
src_offset: source_offset,
dst: self.resolve_buffer_id(destination)?,
dst_offset: destination_offset,
size,
})
})
}
@ -858,7 +851,7 @@ impl Global {
&self,
command_encoder_id: CommandEncoderId,
source: &TexelCopyBufferInfo,
destination: &TexelCopyTextureInfo,
destination: &wgt::TexelCopyTextureInfo<TextureId>,
copy_size: &Extent3d,
) -> Result<(), EncoderStateError> {
profiling::scope!("CommandEncoder::copy_buffer_to_texture");
@ -868,19 +861,39 @@ impl Global {
destination.texture
);
let hub = &self.hub;
let cmd_enc = hub.command_encoders.get(command_encoder_id);
let cmd_enc = self.hub.command_encoders.get(command_encoder_id);
let mut cmd_buf_data = cmd_enc.data.lock();
cmd_buf_data.record_with(|cmd_buf_data| -> Result<(), CommandEncoderError> {
copy_buffer_to_texture(cmd_buf_data, hub, &cmd_enc, source, destination, copy_size)
#[cfg(feature = "trace")]
if let Some(ref mut list) = cmd_buf_data.trace() {
list.push(TraceCommand::CopyBufferToTexture {
src: *source,
dst: *destination,
size: *copy_size,
});
}
cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {
Ok(ArcCommand::CopyBufferToTexture {
src: wgt::TexelCopyBufferInfo::<Arc<Buffer>> {
buffer: self.resolve_buffer_id(source.buffer)?,
layout: source.layout,
},
dst: wgt::TexelCopyTextureInfo::<Arc<Texture>> {
texture: self.resolve_texture_id(destination.texture)?,
mip_level: destination.mip_level,
origin: destination.origin,
aspect: destination.aspect,
},
size: *copy_size,
})
})
}
pub fn command_encoder_copy_texture_to_buffer(
&self,
command_encoder_id: CommandEncoderId,
source: &TexelCopyTextureInfo,
source: &wgt::TexelCopyTextureInfo<TextureId>,
destination: &TexelCopyBufferInfo,
copy_size: &Extent3d,
) -> Result<(), EncoderStateError> {
@ -891,20 +904,40 @@ impl Global {
destination.buffer
);
let hub = &self.hub;
let cmd_enc = hub.command_encoders.get(command_encoder_id);
let cmd_enc = self.hub.command_encoders.get(command_encoder_id);
let mut cmd_buf_data = cmd_enc.data.lock();
cmd_buf_data.record_with(|cmd_buf_data| -> Result<(), CommandEncoderError> {
copy_texture_to_buffer(cmd_buf_data, hub, &cmd_enc, source, destination, copy_size)
#[cfg(feature = "trace")]
if let Some(list) = cmd_buf_data.trace() {
list.push(TraceCommand::CopyTextureToBuffer {
src: *source,
dst: *destination,
size: *copy_size,
});
}
cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {
Ok(ArcCommand::CopyTextureToBuffer {
src: wgt::TexelCopyTextureInfo::<Arc<Texture>> {
texture: self.resolve_texture_id(source.texture)?,
mip_level: source.mip_level,
origin: source.origin,
aspect: source.aspect,
},
dst: wgt::TexelCopyBufferInfo::<Arc<Buffer>> {
buffer: self.resolve_buffer_id(destination.buffer)?,
layout: destination.layout,
},
size: *copy_size,
})
})
}
pub fn command_encoder_copy_texture_to_texture(
&self,
command_encoder_id: CommandEncoderId,
source: &TexelCopyTextureInfo,
destination: &TexelCopyTextureInfo,
source: &wgt::TexelCopyTextureInfo<TextureId>,
destination: &wgt::TexelCopyTextureInfo<TextureId>,
copy_size: &Extent3d,
) -> Result<(), EncoderStateError> {
profiling::scope!("CommandEncoder::copy_texture_to_texture");
@ -914,76 +947,76 @@ impl Global {
destination.texture
);
let hub = &self.hub;
let cmd_enc = hub.command_encoders.get(command_encoder_id);
let cmd_enc = self.hub.command_encoders.get(command_encoder_id);
let mut cmd_buf_data = cmd_enc.data.lock();
cmd_buf_data.record_with(|cmd_buf_data| -> Result<(), CommandEncoderError> {
copy_texture_to_texture(cmd_buf_data, hub, &cmd_enc, source, destination, copy_size)
#[cfg(feature = "trace")]
if let Some(ref mut list) = cmd_buf_data.trace() {
list.push(TraceCommand::CopyTextureToTexture {
src: *source,
dst: *destination,
size: *copy_size,
});
}
cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {
Ok(ArcCommand::CopyTextureToTexture {
src: wgt::TexelCopyTextureInfo {
texture: self.resolve_texture_id(source.texture)?,
mip_level: source.mip_level,
origin: source.origin,
aspect: source.aspect,
},
dst: wgt::TexelCopyTextureInfo {
texture: self.resolve_texture_id(destination.texture)?,
mip_level: destination.mip_level,
origin: destination.origin,
aspect: destination.aspect,
},
size: *copy_size,
})
})
}
}
fn copy_buffer_to_buffer(
cmd_buf_data: &mut CommandBufferMutable,
hub: &crate::hub::Hub,
cmd_enc: &Arc<super::CommandEncoder>,
source: BufferId,
pub(super) fn copy_buffer_to_buffer(
state: &mut EncodingState,
src_buffer: &Arc<Buffer>,
source_offset: BufferAddress,
destination: BufferId,
dst_buffer: &Arc<Buffer>,
destination_offset: BufferAddress,
size: Option<BufferAddress>,
) -> Result<(), CommandEncoderError> {
let device = &cmd_enc.device;
device.check_is_valid()?;
if source == destination {
if src_buffer.is_equal(dst_buffer) {
return Err(TransferError::SameSourceDestinationBuffer.into());
}
#[cfg(feature = "trace")]
if let Some(ref mut list) = cmd_buf_data.trace_commands {
list.push(TraceCommand::CopyBufferToBuffer {
src: source,
src_offset: source_offset,
dst: destination,
dst_offset: destination_offset,
size,
});
}
src_buffer.same_device(state.device)?;
let snatch_guard = device.snatchable_lock.read();
let src_buffer = hub.buffers.get(source).get()?;
src_buffer.same_device_as(cmd_enc.as_ref())?;
let src_pending = cmd_buf_data
.trackers
let src_pending = state
.tracker
.buffers
.set_single(&src_buffer, wgt::BufferUses::COPY_SRC);
.set_single(src_buffer, wgt::BufferUses::COPY_SRC);
let src_raw = src_buffer.try_raw(&snatch_guard)?;
let src_raw = src_buffer.try_raw(state.snatch_guard)?;
src_buffer
.check_usage(BufferUsages::COPY_SRC)
.map_err(TransferError::MissingBufferUsage)?;
// expecting only a single barrier
let src_barrier = src_pending.map(|pending| pending.into_hal(&src_buffer, &snatch_guard));
let src_barrier = src_pending.map(|pending| pending.into_hal(src_buffer, state.snatch_guard));
let dst_buffer = hub.buffers.get(destination).get()?;
dst_buffer.same_device(state.device)?;
dst_buffer.same_device_as(cmd_enc.as_ref())?;
let dst_pending = cmd_buf_data
.trackers
let dst_pending = state
.tracker
.buffers
.set_single(&dst_buffer, wgt::BufferUses::COPY_DST);
.set_single(dst_buffer, wgt::BufferUses::COPY_DST);
let dst_raw = dst_buffer.try_raw(&snatch_guard)?;
let dst_raw = dst_buffer.try_raw(state.snatch_guard)?;
dst_buffer
.check_usage(BufferUsages::COPY_DST)
.map_err(TransferError::MissingBufferUsage)?;
let dst_barrier = dst_pending.map(|pending| pending.into_hal(&dst_buffer, &snatch_guard));
let dst_barrier = dst_pending.map(|pending| pending.into_hal(dst_buffer, state.snatch_guard));
let (size, source_end_offset) = match size {
Some(size) => (size, source_offset + size),
@ -999,7 +1032,8 @@ fn copy_buffer_to_buffer(
if destination_offset % wgt::COPY_BUFFER_ALIGNMENT != 0 {
return Err(TransferError::UnalignedBufferOffset(destination_offset).into());
}
if !device
if !state
.device
.downlevel
.flags
.contains(wgt::DownlevelFlags::UNRESTRICTED_INDEX_BUFFER)
@ -1046,64 +1080,51 @@ fn copy_buffer_to_buffer(
}
// Make sure source is initialized memory and mark dest as initialized.
cmd_buf_data.buffer_memory_init_actions.extend(
dst_buffer.initialization_status.read().create_action(
&dst_buffer,
state
.buffer_memory_init_actions
.extend(dst_buffer.initialization_status.read().create_action(
dst_buffer,
destination_offset..(destination_offset + size),
MemoryInitKind::ImplicitlyInitialized,
),
);
cmd_buf_data.buffer_memory_init_actions.extend(
src_buffer.initialization_status.read().create_action(
&src_buffer,
));
state
.buffer_memory_init_actions
.extend(src_buffer.initialization_status.read().create_action(
src_buffer,
source_offset..(source_offset + size),
MemoryInitKind::NeedsInitializedMemory,
),
);
));
let region = hal::BufferCopy {
src_offset: source_offset,
dst_offset: destination_offset,
size: wgt::BufferSize::new(size).unwrap(),
};
let cmd_buf_raw = cmd_buf_data.encoder.open()?;
let barriers = src_barrier
.into_iter()
.chain(dst_barrier)
.collect::<Vec<_>>();
unsafe {
cmd_buf_raw.transition_buffers(&barriers);
cmd_buf_raw.copy_buffer_to_buffer(src_raw, dst_raw, &[region]);
state.raw_encoder.transition_buffers(&barriers);
state
.raw_encoder
.copy_buffer_to_buffer(src_raw, dst_raw, &[region]);
}
Ok(())
}
fn copy_buffer_to_texture(
cmd_buf_data: &mut CommandBufferMutable,
hub: &crate::hub::Hub,
cmd_enc: &Arc<super::CommandEncoder>,
source: &TexelCopyBufferInfo,
destination: &TexelCopyTextureInfo,
pub(super) fn copy_buffer_to_texture(
state: &mut EncodingState,
source: &wgt::TexelCopyBufferInfo<Arc<Buffer>>,
destination: &wgt::TexelCopyTextureInfo<Arc<Texture>>,
copy_size: &Extent3d,
) -> Result<(), CommandEncoderError> {
let device = &cmd_enc.device;
device.check_is_valid()?;
let dst_texture = &destination.texture;
let src_buffer = &source.buffer;
#[cfg(feature = "trace")]
if let Some(ref mut list) = cmd_buf_data.trace_commands {
list.push(TraceCommand::CopyBufferToTexture {
src: *source,
dst: *destination,
size: *copy_size,
});
}
let dst_texture = hub.textures.get(destination.texture).get()?;
let src_buffer = hub.buffers.get(source.buffer).get()?;
dst_texture.same_device_as(cmd_enc.as_ref())?;
src_buffer.same_device_as(cmd_enc.as_ref())?;
dst_texture.same_device(state.device)?;
src_buffer.same_device(state.device)?;
let (hal_copy_size, array_layer_count) = validate_texture_copy_range(
destination,
@ -1112,12 +1133,10 @@ fn copy_buffer_to_texture(
copy_size,
)?;
let (dst_range, dst_base) = extract_texture_selector(destination, copy_size, &dst_texture)?;
let (dst_range, dst_base) = extract_texture_selector(destination, copy_size, dst_texture)?;
let snatch_guard = device.snatchable_lock.read();
let src_raw = src_buffer.try_raw(&snatch_guard)?;
let dst_raw = dst_texture.try_raw(&snatch_guard)?;
let src_raw = src_buffer.try_raw(state.snatch_guard)?;
let dst_raw = dst_texture.try_raw(state.snatch_guard)?;
if copy_size.width == 0 || copy_size.height == 0 || copy_size.depth_or_array_layers == 0 {
log::trace!("Ignoring copy_buffer_to_texture of size 0");
@ -1127,30 +1146,23 @@ fn copy_buffer_to_texture(
// Handle texture init *before* dealing with barrier transitions so we
// have an easier time inserting "immediate-inits" that may be required
// by prior discards in rare cases.
handle_dst_texture_init(
cmd_buf_data,
device,
destination,
copy_size,
&dst_texture,
&snatch_guard,
)?;
handle_dst_texture_init(state, destination, copy_size, dst_texture)?;
let src_pending = cmd_buf_data
.trackers
let src_pending = state
.tracker
.buffers
.set_single(&src_buffer, wgt::BufferUses::COPY_SRC);
.set_single(src_buffer, wgt::BufferUses::COPY_SRC);
src_buffer
.check_usage(BufferUsages::COPY_SRC)
.map_err(TransferError::MissingBufferUsage)?;
let src_barrier = src_pending.map(|pending| pending.into_hal(&src_buffer, &snatch_guard));
let src_barrier = src_pending.map(|pending| pending.into_hal(src_buffer, state.snatch_guard));
let dst_pending = cmd_buf_data.trackers.textures.set_single(
&dst_texture,
dst_range,
wgt::TextureUses::COPY_DST,
);
let dst_pending =
state
.tracker
.textures
.set_single(dst_texture, dst_range, wgt::TextureUses::COPY_DST);
dst_texture
.check_usage(TextureUsages::COPY_DST)
.map_err(TransferError::MissingTextureUsage)?;
@ -1179,15 +1191,15 @@ fn copy_buffer_to_texture(
)?;
if dst_texture.desc.format.is_depth_stencil_format() {
device
state
.device
.require_downlevel_flags(wgt::DownlevelFlags::DEPTH_TEXTURE_AND_BUFFER_COPIES)
.map_err(TransferError::from)?;
}
handle_buffer_init(
cmd_buf_data,
state,
source,
&src_buffer,
CopySide::Source,
required_buffer_bytes_in_copy,
is_contiguous,
@ -1207,50 +1219,35 @@ fn copy_buffer_to_texture(
})
.collect::<Vec<_>>();
let cmd_buf_raw = cmd_buf_data.encoder.open()?;
unsafe {
cmd_buf_raw.transition_textures(&dst_barrier);
cmd_buf_raw.transition_buffers(src_barrier.as_slice());
cmd_buf_raw.copy_buffer_to_texture(src_raw, dst_raw, &regions);
state.raw_encoder.transition_textures(&dst_barrier);
state.raw_encoder.transition_buffers(src_barrier.as_slice());
state
.raw_encoder
.copy_buffer_to_texture(src_raw, dst_raw, &regions);
}
Ok(())
}
fn copy_texture_to_buffer(
cmd_buf_data: &mut CommandBufferMutable,
hub: &crate::hub::Hub,
cmd_enc: &Arc<super::CommandEncoder>,
pub(super) fn copy_texture_to_buffer(
state: &mut EncodingState,
source: &TexelCopyTextureInfo,
destination: &TexelCopyBufferInfo,
destination: &wgt::TexelCopyBufferInfo<Arc<Buffer>>,
copy_size: &Extent3d,
) -> Result<(), CommandEncoderError> {
let device = &cmd_enc.device;
device.check_is_valid()?;
let src_texture = &source.texture;
let dst_buffer = &destination.buffer;
#[cfg(feature = "trace")]
if let Some(list) = cmd_buf_data.trace_commands.as_mut() {
list.push(TraceCommand::CopyTextureToBuffer {
src: *source,
dst: *destination,
size: *copy_size,
});
}
let src_texture = hub.textures.get(source.texture).get()?;
let dst_buffer = hub.buffers.get(destination.buffer).get()?;
src_texture.same_device_as(cmd_enc.as_ref())?;
dst_buffer.same_device_as(cmd_enc.as_ref())?;
src_texture.same_device(state.device)?;
dst_buffer.same_device(state.device)?;
let (hal_copy_size, array_layer_count) =
validate_texture_copy_range(source, &src_texture.desc, CopySide::Source, copy_size)?;
let (src_range, src_base) = extract_texture_selector(source, copy_size, &src_texture)?;
let (src_range, src_base) = extract_texture_selector(source, copy_size, src_texture)?;
let snatch_guard = device.snatchable_lock.read();
let src_raw = src_texture.try_raw(&snatch_guard)?;
let src_raw = src_texture.try_raw(state.snatch_guard)?;
src_texture
.check_usage(TextureUsages::COPY_SRC)
.map_err(TransferError::MissingTextureUsage)?;
@ -1284,12 +1281,13 @@ fn copy_texture_to_buffer(
)?;
if src_texture.desc.format.is_depth_stencil_format() {
device
state
.device
.require_downlevel_flags(wgt::DownlevelFlags::DEPTH_TEXTURE_AND_BUFFER_COPIES)
.map_err(TransferError::from)?;
}
let dst_raw = dst_buffer.try_raw(&snatch_guard)?;
let dst_raw = dst_buffer.try_raw(state.snatch_guard)?;
dst_buffer
.check_usage(BufferUsages::COPY_DST)
.map_err(TransferError::MissingBufferUsage)?;
@ -1302,35 +1300,27 @@ fn copy_texture_to_buffer(
// Handle texture init *before* dealing with barrier transitions so we
// have an easier time inserting "immediate-inits" that may be required
// by prior discards in rare cases.
handle_src_texture_init(
cmd_buf_data,
device,
source,
copy_size,
&src_texture,
&snatch_guard,
)?;
handle_src_texture_init(state, source, copy_size, src_texture)?;
let src_pending = cmd_buf_data.trackers.textures.set_single(
&src_texture,
src_range,
wgt::TextureUses::COPY_SRC,
);
let src_pending =
state
.tracker
.textures
.set_single(src_texture, src_range, wgt::TextureUses::COPY_SRC);
let src_barrier = src_pending
.map(|pending| pending.into_hal(src_raw))
.collect::<Vec<_>>();
let dst_pending = cmd_buf_data
.trackers
let dst_pending = state
.tracker
.buffers
.set_single(&dst_buffer, wgt::BufferUses::COPY_DST);
.set_single(dst_buffer, wgt::BufferUses::COPY_DST);
let dst_barrier = dst_pending.map(|pending| pending.into_hal(&dst_buffer, &snatch_guard));
let dst_barrier = dst_pending.map(|pending| pending.into_hal(dst_buffer, state.snatch_guard));
handle_buffer_init(
cmd_buf_data,
state,
destination,
&dst_buffer,
CopySide::Destination,
required_buffer_bytes_in_copy,
is_contiguous,
@ -1349,43 +1339,31 @@ fn copy_texture_to_buffer(
}
})
.collect::<Vec<_>>();
let cmd_buf_raw = cmd_buf_data.encoder.open()?;
unsafe {
cmd_buf_raw.transition_buffers(dst_barrier.as_slice());
cmd_buf_raw.transition_textures(&src_barrier);
cmd_buf_raw.copy_texture_to_buffer(src_raw, wgt::TextureUses::COPY_SRC, dst_raw, &regions);
state.raw_encoder.transition_buffers(dst_barrier.as_slice());
state.raw_encoder.transition_textures(&src_barrier);
state.raw_encoder.copy_texture_to_buffer(
src_raw,
wgt::TextureUses::COPY_SRC,
dst_raw,
&regions,
);
}
Ok(())
}
fn copy_texture_to_texture(
cmd_buf_data: &mut CommandBufferMutable,
hub: &crate::hub::Hub,
cmd_enc: &Arc<super::CommandEncoder>,
pub(super) fn copy_texture_to_texture(
state: &mut EncodingState,
source: &TexelCopyTextureInfo,
destination: &TexelCopyTextureInfo,
copy_size: &Extent3d,
) -> Result<(), CommandEncoderError> {
let device = &cmd_enc.device;
device.check_is_valid()?;
let src_texture = &source.texture;
let dst_texture = &destination.texture;
let snatch_guard = device.snatchable_lock.read();
#[cfg(feature = "trace")]
if let Some(ref mut list) = cmd_buf_data.trace_commands {
list.push(TraceCommand::CopyTextureToTexture {
src: *source,
dst: *destination,
size: *copy_size,
});
}
let src_texture = hub.textures.get(source.texture).get()?;
let dst_texture = hub.textures.get(destination.texture).get()?;
src_texture.same_device_as(cmd_enc.as_ref())?;
dst_texture.same_device_as(cmd_enc.as_ref())?;
src_texture.same_device(state.device)?;
dst_texture.same_device(state.device)?;
// src and dst texture format must be copy-compatible
// https://gpuweb.github.io/gpuweb/#copy-compatible
@ -1407,7 +1385,7 @@ fn copy_texture_to_texture(
copy_size,
)?;
if Arc::as_ptr(&src_texture) == Arc::as_ptr(&dst_texture) {
if Arc::as_ptr(src_texture) == Arc::as_ptr(dst_texture) {
validate_copy_within_same_texture(
source,
destination,
@ -1416,8 +1394,8 @@ fn copy_texture_to_texture(
)?;
}
let (src_range, src_tex_base) = extract_texture_selector(source, copy_size, &src_texture)?;
let (dst_range, dst_tex_base) = extract_texture_selector(destination, copy_size, &dst_texture)?;
let (src_range, src_tex_base) = extract_texture_selector(source, copy_size, src_texture)?;
let (dst_range, dst_tex_base) = extract_texture_selector(destination, copy_size, dst_texture)?;
let src_texture_aspects = hal::FormatAspects::from(src_texture.desc.format);
let dst_texture_aspects = hal::FormatAspects::from(dst_texture.desc.format);
if src_tex_base.aspect != src_texture_aspects {
@ -1438,28 +1416,14 @@ fn copy_texture_to_texture(
// Handle texture init *before* dealing with barrier transitions so we
// have an easier time inserting "immediate-inits" that may be required
// by prior discards in rare cases.
handle_src_texture_init(
cmd_buf_data,
device,
source,
copy_size,
&src_texture,
&snatch_guard,
)?;
handle_dst_texture_init(
cmd_buf_data,
device,
destination,
copy_size,
&dst_texture,
&snatch_guard,
)?;
handle_src_texture_init(state, source, copy_size, src_texture)?;
handle_dst_texture_init(state, destination, copy_size, dst_texture)?;
let src_raw = src_texture.try_raw(&snatch_guard)?;
let src_raw = src_texture.try_raw(state.snatch_guard)?;
src_texture
.check_usage(TextureUsages::COPY_SRC)
.map_err(TransferError::MissingTextureUsage)?;
let dst_raw = dst_texture.try_raw(&snatch_guard)?;
let dst_raw = dst_texture.try_raw(state.snatch_guard)?;
dst_texture
.check_usage(TextureUsages::COPY_DST)
.map_err(TransferError::MissingTextureUsage)?;
@ -1469,11 +1433,11 @@ fn copy_texture_to_texture(
return Ok(());
}
let src_pending = cmd_buf_data.trackers.textures.set_single(
&src_texture,
src_range,
wgt::TextureUses::COPY_SRC,
);
let src_pending =
state
.tracker
.textures
.set_single(src_texture, src_range, wgt::TextureUses::COPY_SRC);
//TODO: try to avoid this the collection. It's needed because both
// `src_pending` and `dst_pending` try to hold `trackers.textures` mutably.
@ -1481,11 +1445,11 @@ fn copy_texture_to_texture(
.map(|pending| pending.into_hal(src_raw))
.collect();
let dst_pending = cmd_buf_data.trackers.textures.set_single(
&dst_texture,
dst_range,
wgt::TextureUses::COPY_DST,
);
let dst_pending =
state
.tracker
.textures
.set_single(dst_texture, dst_range, wgt::TextureUses::COPY_DST);
barriers.extend(dst_pending.map(|pending| pending.into_hal(dst_raw)));
let hal_copy_size = hal::CopyExtent {
@ -1520,10 +1484,14 @@ fn copy_texture_to_texture(
} else {
regions.collect::<Vec<_>>()
};
let cmd_buf_raw = cmd_buf_data.encoder.open()?;
unsafe {
cmd_buf_raw.transition_textures(&barriers);
cmd_buf_raw.copy_texture_to_texture(src_raw, wgt::TextureUses::COPY_SRC, dst_raw, &regions);
state.raw_encoder.transition_textures(&barriers);
state.raw_encoder.copy_texture_to_texture(
src_raw,
wgt::TextureUses::COPY_SRC,
dst_raw,
&regions,
);
}
Ok(())

View File

@ -1,12 +1,14 @@
use alloc::{sync::Arc, vec::Vec};
use thiserror::Error;
use wgt::error::{ErrorType, WebGpuError};
use crate::{
command::{CommandEncoder, CommandEncoderError, EncoderStateError},
command::{encoder::EncodingState, ArcCommand, CommandEncoder, EncoderStateError},
device::DeviceError,
global::Global,
id::{BufferId, CommandEncoderId, TextureId},
resource::{InvalidResourceError, ParentDevice},
resource::{Buffer, InvalidResourceError, ParentDevice, Texture},
track::ResourceUsageCompatibilityError,
};
@ -24,54 +26,72 @@ impl Global {
// Lock command encoder for recording
let cmd_enc = hub.command_encoders.get(command_encoder_id);
let mut cmd_buf_data = cmd_enc.data.lock();
cmd_buf_data.record_with(|cmd_buf_data| -> Result<(), CommandEncoderError> {
// Get and lock device
let device = &cmd_enc.device;
device.check_is_valid()?;
let snatch_guard = &device.snatchable_lock.read();
let mut usage_scope = device.new_usage_scope();
let indices = &device.tracker_indices;
usage_scope.buffers.set_size(indices.buffers.size());
usage_scope.textures.set_size(indices.textures.size());
// Process buffer transitions
for buffer_transition in buffer_transitions {
let buffer = hub.buffers.get(buffer_transition.buffer).get()?;
buffer.same_device_as(cmd_enc.as_ref())?;
usage_scope
.buffers
.merge_single(&buffer, buffer_transition.state)?;
}
// Process texture transitions
for texture_transition in texture_transitions {
let texture = hub.textures.get(texture_transition.texture).get()?;
texture.same_device_as(cmd_enc.as_ref())?;
unsafe {
usage_scope.textures.merge_single(
&texture,
texture_transition.selector,
texture_transition.state,
)
}?;
}
// Record any needed barriers based on tracker data
let cmd_buf_raw = cmd_buf_data.encoder.open()?;
CommandEncoder::insert_barriers_from_scope(
cmd_buf_raw,
&mut cmd_buf_data.trackers,
&usage_scope,
snatch_guard,
);
Ok(())
cmd_buf_data.push_with(|| -> Result<_, TransitionResourcesError> {
Ok(ArcCommand::TransitionResources {
buffer_transitions: buffer_transitions
.map(|t| {
Ok(wgt::BufferTransition {
buffer: self.resolve_buffer_id(t.buffer)?,
state: t.state,
})
})
.collect::<Result<_, TransitionResourcesError>>()?,
texture_transitions: texture_transitions
.map(|t| {
Ok(wgt::TextureTransition {
texture: self.resolve_texture_id(t.texture)?,
selector: t.selector,
state: t.state,
})
})
.collect::<Result<_, TransitionResourcesError>>()?,
})
})
}
}
pub(crate) fn transition_resources(
state: &mut EncodingState,
buffer_transitions: Vec<wgt::BufferTransition<Arc<Buffer>>>,
texture_transitions: Vec<wgt::TextureTransition<Arc<Texture>>>,
) -> Result<(), TransitionResourcesError> {
let mut usage_scope = state.device.new_usage_scope();
let indices = &state.device.tracker_indices;
usage_scope.buffers.set_size(indices.buffers.size());
usage_scope.textures.set_size(indices.textures.size());
// Process buffer transitions
for buffer_transition in buffer_transitions {
buffer_transition.buffer.same_device(state.device)?;
usage_scope
.buffers
.merge_single(&buffer_transition.buffer, buffer_transition.state)?;
}
// Process texture transitions
for texture_transition in texture_transitions {
texture_transition.texture.same_device(state.device)?;
unsafe {
usage_scope.textures.merge_single(
&texture_transition.texture,
texture_transition.selector,
texture_transition.state,
)
}?;
}
// Record any needed barriers based on tracker data
CommandEncoder::insert_barriers_from_scope(
state.raw_encoder,
state.tracker,
&usage_scope,
state.snatch_guard,
);
Ok(())
}
/// Error encountered while attempting to perform [`Global::command_encoder_transition_resources`].
#[derive(Clone, Debug, Error)]
#[non_exhaustive]

View File

@ -21,7 +21,7 @@ use crate::{
device::{DeviceError, MissingFeatures},
id::{BlasId, BufferId, TlasId},
resource::{
Blas, BlasCompactCallback, BlasPrepareCompactResult, DestroyedResourceError,
Blas, BlasCompactCallback, BlasPrepareCompactResult, Buffer, DestroyedResourceError,
InvalidResourceError, MissingBufferUsageError, ResourceErrorIdent, Tlas,
},
};
@ -304,6 +304,7 @@ pub(crate) enum AsAction {
UseTlas(Arc<Tlas>),
}
/// Like [`BlasTriangleGeometry`], but with owned data.
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TraceBlasTriangleGeometry {
@ -347,6 +348,55 @@ pub struct TraceTlasPackage {
pub lowest_unmodified: u32,
}
/// Like [`BlasTriangleGeometry`], but with `Arc`s.
#[derive(Debug, Clone)]
pub struct ArcBlasTriangleGeometry {
pub size: wgt::BlasTriangleGeometrySizeDescriptor,
pub vertex_buffer: Arc<Buffer>,
pub index_buffer: Option<Arc<Buffer>>,
pub transform_buffer: Option<Arc<Buffer>>,
pub first_vertex: u32,
pub vertex_stride: BufferAddress,
pub first_index: Option<u32>,
pub transform_buffer_offset: Option<BufferAddress>,
}
/// [`BlasTriangleGeometry`], without the resources.
#[derive(Debug, Clone)]
pub struct BlasTriangleGeometryInfo {
pub size: wgt::BlasTriangleGeometrySizeDescriptor,
pub first_vertex: u32,
pub vertex_stride: BufferAddress,
pub first_index: Option<u32>,
pub transform_buffer_offset: Option<BufferAddress>,
}
#[derive(Debug, Clone)]
pub enum ArcBlasGeometries {
TriangleGeometries(Vec<ArcBlasTriangleGeometry>),
}
#[derive(Debug, Clone)]
pub struct ArcBlasBuildEntry {
pub blas: Arc<Blas>,
pub geometries: ArcBlasGeometries,
}
#[derive(Debug, Clone)]
pub struct ArcTlasInstance {
pub blas: Arc<Blas>,
pub transform: [f32; 12],
pub custom_data: u32,
pub mask: u8,
}
#[derive(Debug, Clone)]
pub struct ArcTlasPackage {
pub tlas: Arc<Tlas>,
pub instances: Vec<Option<ArcTlasInstance>>,
pub lowest_unmodified: u32,
}
#[derive(Clone, Debug, Error)]
pub enum BlasPrepareCompactError {
#[error(transparent)]

View File

@ -5365,7 +5365,7 @@ bitflags::bitflags! {
}
/// A buffer transition for use with `CommandEncoder::transition_resources`.
#[derive(Debug)]
#[derive(Clone, Debug)]
pub struct BufferTransition<T> {
/// The buffer to transition.
pub buffer: T,
@ -5668,7 +5668,7 @@ bitflags::bitflags! {
}
/// A texture transition for use with `CommandEncoder::transition_resources`.
#[derive(Debug)]
#[derive(Clone, Debug)]
pub struct TextureTransition<T> {
/// The texture to transition.
pub texture: T,

View File

@ -397,8 +397,8 @@ fn map_buffer_copy_view(
fn map_texture_copy_view(
view: crate::TexelCopyTextureInfo<'_>,
) -> wgc::command::TexelCopyTextureInfo {
wgc::command::TexelCopyTextureInfo {
) -> wgt::TexelCopyTextureInfo<wgc::id::TextureId> {
wgt::TexelCopyTextureInfo {
texture: view.texture.inner.as_core().id,
mip_level: view.mip_level,
origin: view.origin,