Skip to content

Commit 1c43ac2

Browse files
Additional validation of buffer-texture copies (#7948)
* Additional validation of buffer-texture copies Fixes #7936, but leaves a TODO for #7947 * Skip tests failing on dx12 * Update comments and change unwrap_or to expect
1 parent ffd5969 commit 1c43ac2

File tree

5 files changed

+119
-21
lines changed

5 files changed

+119
-21
lines changed

cts_runner/test.lst

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,28 @@ webgpu:api,validation,encoding,programmable,pipeline_bind_group_compat:bind_grou
3434
webgpu:api,validation,encoding,programmable,pipeline_bind_group_compat:buffer_binding,render_pipeline:*
3535
webgpu:api,validation,encoding,programmable,pipeline_bind_group_compat:sampler_binding,render_pipeline:*
3636
webgpu:api,validation,encoding,queries,general:occlusion_query,query_index:*
37+
webgpu:api,validation,image_copy,buffer_texture_copies:depth_stencil_format,copy_usage_and_aspect:*
38+
webgpu:api,validation,image_copy,buffer_texture_copies:depth_stencil_format,copy_buffer_size:*
39+
fails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:depth_stencil_format,copy_buffer_offset:format="depth32float";aspect="depth-only";copyType="CopyT2B"
40+
fails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:depth_stencil_format,copy_buffer_offset:format="depth24plus-stencil8";aspect="stencil-only";copyType="CopyB2T"
41+
fails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:depth_stencil_format,copy_buffer_offset:format="depth24plus-stencil8";aspect="stencil-only";copyType="CopyT2B"
42+
//mix of PASS and FAIL: other subtests of copy_buffer_offset. Related bugs:
43+
// https://github.com/gfx-rs/wgpu/issues/7946, https://github.com/gfx-rs/wgpu/issues/7947
44+
webgpu:api,validation,image_copy,buffer_texture_copies:sample_count:*
45+
webgpu:api,validation,image_copy,buffer_texture_copies:texture_buffer_usages:*
46+
webgpu:api,validation,image_copy,buffer_texture_copies:device_mismatch:*
47+
fails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:offset_and_bytesPerRow:format="rgba8unorm";copyType="CopyB2T";dimension="2d"
48+
fails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:offset_and_bytesPerRow:format="rgba8unorm";copyType="CopyT2B";dimension="2d"
49+
fails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:offset_and_bytesPerRow:format="rgba8unorm";copyType="CopyB2T";dimension="3d"
50+
fails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:offset_and_bytesPerRow:format="rgba8unorm";copyType="CopyT2B";dimension="3d"
51+
fails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:offset_and_bytesPerRow:format="bgra8unorm";copyType="CopyB2T";dimension="2d"
52+
fails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:offset_and_bytesPerRow:format="bgra8unorm";copyType="CopyT2B";dimension="2d"
53+
fails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:offset_and_bytesPerRow:format="astc-4x4-unorm";copyType="CopyT2B";dimension="2d"
54+
fails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:offset_and_bytesPerRow:format="astc-4x4-unorm";copyType="CopyB2T";dimension="2d"
55+
fails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:offset_and_bytesPerRow:format="astc-4x4-unorm";copyType="CopyT2B";dimension="3d"
56+
fails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:offset_and_bytesPerRow:format="astc-4x4-unorm";copyType="CopyB2T";dimension="3d"
57+
//mix of PASS and FAIL: other subtests of offset_and_bytesPerRow. Related bugs:
58+
// https://github.com/gfx-rs/wgpu/issues/7946, https://github.com/gfx-rs/wgpu/issues/7947
3759
webgpu:api,validation,image_copy,layout_related:copy_end_overflows_u64:*
3860
webgpu:api,validation,image_copy,texture_related:format:dimension="1d";*
3961
webgpu:api,validation,queue,submit:command_buffer,device_mismatch:*

wgpu-core/src/command/transfer.rs

Lines changed: 81 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,70 @@ pub(crate) fn validate_linear_texture_data(
363363
Ok((bytes_in_copy, image_stride_bytes))
364364
}
365365

366+
/// Validation for texture/buffer copies.
367+
///
368+
/// This implements the following checks from WebGPU's [validating texture buffer copy][vtbc]
369+
/// algorithm:
370+
/// * The texture must not be multisampled.
371+
/// * The copy must be from/to a single aspect of the texture.
372+
/// * If `aligned` is true, the buffer offset must be aligned appropriately.
373+
///
374+
/// The following steps in the algorithm are implemented elsewhere:
375+
/// * Invocation of other validation algorithms.
376+
/// * The texture usage (COPY_DST / COPY_SRC) check.
377+
/// * The check for non-copyable depth/stencil formats. The caller must perform
378+
/// this check using `is_valid_copy_src_format` / `is_valid_copy_dst_format`
379+
/// before calling this function. This function will panic if
380+
/// [`wgt::TextureFormat::block_copy_size`] returns `None` due to a
381+
/// non-copyable format.
382+
///
383+
/// [vtbc]: https://gpuweb.github.io/gpuweb/#abstract-opdef-validating-texture-buffer-copy
384+
pub(crate) fn validate_texture_buffer_copy<T>(
385+
texture_copy_view: &wgt::TexelCopyTextureInfo<T>,
386+
aspect: hal::FormatAspects,
387+
desc: &wgt::TextureDescriptor<(), Vec<wgt::TextureFormat>>,
388+
offset: BufferAddress,
389+
aligned: bool,
390+
) -> Result<(), TransferError> {
391+
if desc.sample_count != 1 {
392+
return Err(TransferError::InvalidSampleCount {
393+
sample_count: desc.sample_count,
394+
});
395+
}
396+
397+
if !aspect.is_one() {
398+
return Err(TransferError::CopyAspectNotOne);
399+
}
400+
401+
let mut offset_alignment = if desc.format.is_depth_stencil_format() {
402+
4
403+
} else {
404+
// The case where `block_copy_size` returns `None` is currently
405+
// unreachable both for the reason in the expect message, and also
406+
// because the currently-defined non-copyable formats are depth/stencil
407+
// formats so would take the `if` branch.
408+
desc.format
409+
.block_copy_size(Some(texture_copy_view.aspect))
410+
.expect("non-copyable formats should have been rejected previously")
411+
};
412+
413+
// TODO(https://github.com/gfx-rs/wgpu/issues/7947): This does not match the spec.
414+
// The spec imposes no alignment requirement if `!aligned`, and otherwise
415+
// imposes only the `offset_alignment` as calculated above. wgpu currently
416+
// can panic on alignments <4B, so we reject them here to avoid a panic.
417+
if aligned {
418+
offset_alignment = offset_alignment.max(4);
419+
} else {
420+
offset_alignment = 4;
421+
}
422+
423+
if offset % u64::from(offset_alignment) != 0 {
424+
return Err(TransferError::UnalignedBufferOffset(offset));
425+
}
426+
427+
Ok(())
428+
}
429+
366430
/// Validate the extent and alignment of a texture copy.
367431
///
368432
/// Copied with minor modifications from WebGPU standard. This mostly follows
@@ -884,10 +948,6 @@ impl Global {
884948
.map(|pending| pending.into_hal(dst_raw))
885949
.collect::<Vec<_>>();
886950

887-
if !dst_base.aspect.is_one() {
888-
return Err(TransferError::CopyAspectNotOne.into());
889-
}
890-
891951
if !conv::is_valid_copy_dst_texture_format(dst_texture.desc.format, destination.aspect)
892952
{
893953
return Err(TransferError::CopyToForbiddenTextureFormat {
@@ -897,6 +957,14 @@ impl Global {
897957
.into());
898958
}
899959

960+
validate_texture_buffer_copy(
961+
destination,
962+
dst_base.aspect,
963+
&dst_texture.desc,
964+
source.layout.offset,
965+
true, // alignment required for buffer offset
966+
)?;
967+
900968
let (required_buffer_bytes_in_copy, bytes_per_array_layer) =
901969
validate_linear_texture_data(
902970
&source.layout,
@@ -999,12 +1067,7 @@ impl Global {
9991067
src_texture
10001068
.check_usage(TextureUsages::COPY_SRC)
10011069
.map_err(TransferError::MissingTextureUsage)?;
1002-
if src_texture.desc.sample_count != 1 {
1003-
return Err(TransferError::InvalidSampleCount {
1004-
sample_count: src_texture.desc.sample_count,
1005-
}
1006-
.into());
1007-
}
1070+
10081071
if source.mip_level >= src_texture.desc.mip_level_count {
10091072
return Err(TransferError::InvalidMipLevel {
10101073
requested: source.mip_level,
@@ -1013,10 +1076,6 @@ impl Global {
10131076
.into());
10141077
}
10151078

1016-
if !src_base.aspect.is_one() {
1017-
return Err(TransferError::CopyAspectNotOne.into());
1018-
}
1019-
10201079
if !conv::is_valid_copy_src_texture_format(src_texture.desc.format, source.aspect) {
10211080
return Err(TransferError::CopyFromForbiddenTextureFormat {
10221081
format: src_texture.desc.format,
@@ -1025,6 +1084,14 @@ impl Global {
10251084
.into());
10261085
}
10271086

1087+
validate_texture_buffer_copy(
1088+
source,
1089+
src_base.aspect,
1090+
&src_texture.desc,
1091+
destination.layout.offset,
1092+
true, // alignment required for buffer offset
1093+
)?;
1094+
10281095
let (required_buffer_bytes_in_copy, bytes_per_array_layer) =
10291096
validate_linear_texture_data(
10301097
&destination.layout,

wgpu-core/src/conv.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ use wgt::TextureFormatFeatures;
22

33
use crate::resource::{self, TextureDescriptor};
44

5+
// Some core-only texture format helpers. The helper methods on `TextureFormat`
6+
// defined in wgpu-types may need to be modified along with the ones here.
7+
58
pub fn is_valid_copy_src_texture_format(
69
format: wgt::TextureFormat,
710
aspect: wgt::TextureAspect,

wgpu-core/src/device/queue.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ use crate::device::trace::Action;
1919
use crate::{
2020
api_log,
2121
command::{
22-
extract_texture_selector, validate_linear_texture_data, validate_texture_copy_range,
23-
ClearError, CommandAllocator, CommandBuffer, CommandEncoder, CommandEncoderError, CopySide,
24-
TexelCopyTextureInfo, TransferError,
22+
extract_texture_selector, validate_linear_texture_data, validate_texture_buffer_copy,
23+
validate_texture_copy_range, ClearError, CommandAllocator, CommandBuffer, CommandEncoder,
24+
CommandEncoderError, CopySide, TexelCopyTextureInfo, TransferError,
2525
},
2626
conv,
2727
device::{DeviceError, WaitIdleError},
@@ -737,10 +737,6 @@ impl Queue {
737737

738738
let (selector, dst_base) = extract_texture_selector(&destination, size, &dst)?;
739739

740-
if !dst_base.aspect.is_one() {
741-
return Err(TransferError::CopyAspectNotOne.into());
742-
}
743-
744740
if !conv::is_valid_copy_dst_texture_format(dst.desc.format, destination.aspect) {
745741
return Err(TransferError::CopyToForbiddenTextureFormat {
746742
format: dst.desc.format,
@@ -749,6 +745,14 @@ impl Queue {
749745
.into());
750746
}
751747

748+
validate_texture_buffer_copy(
749+
&destination,
750+
dst_base.aspect,
751+
&dst.desc,
752+
data_layout.offset,
753+
false, // alignment not required for buffer offset
754+
)?;
755+
752756
// Note: `_source_bytes_per_array_layer` is ignored since we
753757
// have a staging copy, and it can have a different value.
754758
let (required_bytes_in_copy, _source_bytes_per_array_layer) = validate_linear_texture_data(

wgpu-types/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2608,6 +2608,8 @@ impl TextureAspect {
26082608
}
26092609
}
26102610

2611+
// There are some additional texture format helpers in `wgpu-core/src/conv.rs`,
2612+
// that may need to be modified along with the ones here.
26112613
impl TextureFormat {
26122614
/// Returns the aspect-specific format of the original format
26132615
///

0 commit comments

Comments
 (0)