@@ -363,6 +363,70 @@ pub(crate) fn validate_linear_texture_data(
363
363
Ok ( ( bytes_in_copy, image_stride_bytes) )
364
364
}
365
365
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
+
366
430
/// Validate the extent and alignment of a texture copy.
367
431
///
368
432
/// Copied with minor modifications from WebGPU standard. This mostly follows
@@ -884,10 +948,6 @@ impl Global {
884
948
. map ( |pending| pending. into_hal ( dst_raw) )
885
949
. collect :: < Vec < _ > > ( ) ;
886
950
887
- if !dst_base. aspect . is_one ( ) {
888
- return Err ( TransferError :: CopyAspectNotOne . into ( ) ) ;
889
- }
890
-
891
951
if !conv:: is_valid_copy_dst_texture_format ( dst_texture. desc . format , destination. aspect )
892
952
{
893
953
return Err ( TransferError :: CopyToForbiddenTextureFormat {
@@ -897,6 +957,14 @@ impl Global {
897
957
. into ( ) ) ;
898
958
}
899
959
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
+
900
968
let ( required_buffer_bytes_in_copy, bytes_per_array_layer) =
901
969
validate_linear_texture_data (
902
970
& source. layout ,
@@ -999,12 +1067,7 @@ impl Global {
999
1067
src_texture
1000
1068
. check_usage ( TextureUsages :: COPY_SRC )
1001
1069
. 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
+
1008
1071
if source. mip_level >= src_texture. desc . mip_level_count {
1009
1072
return Err ( TransferError :: InvalidMipLevel {
1010
1073
requested : source. mip_level ,
@@ -1013,10 +1076,6 @@ impl Global {
1013
1076
. into ( ) ) ;
1014
1077
}
1015
1078
1016
- if !src_base. aspect . is_one ( ) {
1017
- return Err ( TransferError :: CopyAspectNotOne . into ( ) ) ;
1018
- }
1019
-
1020
1079
if !conv:: is_valid_copy_src_texture_format ( src_texture. desc . format , source. aspect ) {
1021
1080
return Err ( TransferError :: CopyFromForbiddenTextureFormat {
1022
1081
format : src_texture. desc . format ,
@@ -1025,6 +1084,14 @@ impl Global {
1025
1084
. into ( ) ) ;
1026
1085
}
1027
1086
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
+
1028
1095
let ( required_buffer_bytes_in_copy, bytes_per_array_layer) =
1029
1096
validate_linear_texture_data (
1030
1097
& destination. layout ,
0 commit comments