mirror of
https://github.com/gfx-rs/wgpu.git
synced 2025-12-08 21:26:17 +00:00
Encode commands on finish (#8220)
Co-authored-by: Andreas Reich <r_andreas2@web.de>
This commit is contained in:
parent
990aef977e
commit
1967900565
@ -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).
|
||||
|
||||
@ -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(),
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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(())
|
||||
|
||||
@ -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()?;
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
|
||||
@ -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>>>,
|
||||
},
|
||||
}
|
||||
|
||||
@ -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(())
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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(())
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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, ®ions);
|
||||
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, ®ions);
|
||||
}
|
||||
|
||||
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, ®ions);
|
||||
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,
|
||||
®ions,
|
||||
);
|
||||
}
|
||||
|
||||
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, ®ions);
|
||||
state.raw_encoder.transition_textures(&barriers);
|
||||
state.raw_encoder.copy_texture_to_texture(
|
||||
src_raw,
|
||||
wgt::TextureUses::COPY_SRC,
|
||||
dst_raw,
|
||||
®ions,
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@ -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]
|
||||
|
||||
@ -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)]
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user