@@ -12,6 +12,41 @@ use wgpu_test::{
1212 TestParameters , TestingContext ,
1313} ;
1414
15+ /// A way to write data into a texture.
16+ #[ derive( Clone , Copy ) ]
17+ #[ allow( clippy:: enum_variant_names) ]
18+ enum WriteMethod {
19+ WriteTexture ,
20+ CopyBufferToTexture ,
21+ CopyTextureToTexture ,
22+ }
23+
24+ impl WriteMethod {
25+ fn name ( self ) -> & ' static str {
26+ match self {
27+ WriteMethod :: WriteTexture => "write_texture" ,
28+ WriteMethod :: CopyBufferToTexture => "copy_buffer_to_texture" ,
29+ WriteMethod :: CopyTextureToTexture => "copy_texture_to_texture" ,
30+ }
31+ }
32+ }
33+
34+ /// A way to read data out of a texture.
35+ #[ derive( Clone , Copy ) ]
36+ enum ReadMethod {
37+ CopyTextureToBuffer ,
38+ CopyTextureToTexture ,
39+ }
40+
41+ impl ReadMethod {
42+ fn name ( self ) -> & ' static str {
43+ match self {
44+ ReadMethod :: CopyTextureToBuffer => "copy_texture_to_buffer" ,
45+ ReadMethod :: CopyTextureToTexture => "copy_texture_to_texture" ,
46+ }
47+ }
48+ }
49+
1550pub fn all_tests ( vec : & mut Vec < GpuTestInitializer > ) {
1651 vec. extend ( [
1752 COPY_BUFFER_TO_TEXTURE_PLANE0_LEAVES_PLANE1_UNINIT_NV12 ,
@@ -27,6 +62,10 @@ pub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {
2762 WRITE_TEXTURE_STENCIL_LEAVES_DEPTH_UNINIT_DEPTH24PLUS_STENCIL8 ,
2863 WRITE_TEXTURE_STENCIL_LEAVES_DEPTH_UNINIT_DEPTH32FLOAT_STENCIL8 ,
2964 DYNAMIC_OFFSET_BUFFER_BINDING_INIT ,
65+ COPY_TEXTURE_TO_BUFFER_3D_SOURCE_ORIGIN_Z_UNINIT ,
66+ COPY_TEXTURE_TO_TEXTURE_3D_SOURCE_ORIGIN_Z_UNINIT ,
67+ COPY_BUFFER_TO_TEXTURE_3D_DEST_ORIGIN_Z_PARTIAL ,
68+ COPY_TEXTURE_TO_TEXTURE_3D_DEST_ORIGIN_Z_PARTIAL ,
3069 ] ) ;
3170}
3271
@@ -574,21 +613,6 @@ struct AspectInfo {
574613 bpp : u32 ,
575614}
576615
577- #[ derive( Clone , Copy ) ]
578- enum WriteMethod {
579- WriteTexture ,
580- CopyBufferToTexture ,
581- }
582-
583- impl WriteMethod {
584- fn name ( self ) -> & ' static str {
585- match self {
586- WriteMethod :: WriteTexture => "write_texture" ,
587- WriteMethod :: CopyBufferToTexture => "copy_buffer_to_texture" ,
588- }
589- }
590- }
591-
592616async fn check_depth_stencil_write_leaves_other_uninit (
593617 ctx : & TestingContext ,
594618 format : TextureFormat ,
@@ -746,6 +770,9 @@ async fn check_write_aspect_leaves_other_uninit(
746770 ) ;
747771 ctx. queue . submit ( Some ( encoder. finish ( ) ) ) ;
748772 }
773+ WriteMethod :: CopyTextureToTexture => {
774+ unreachable ! ( "aspect-init tests do not use copy_texture_to_texture" )
775+ }
749776 }
750777
751778 let read_bytes_per_row = read. size . width * read. bpp ;
@@ -947,3 +974,264 @@ static DYNAMIC_OFFSET_BUFFER_BINDING_INIT: GpuTestConfiguration = GpuTestConfigu
947974 data[ nonzero. unwrap( ) ] ,
948975 ) ;
949976 } ) ;
977+
978+ // Tests of initialization of 3D textures.
979+ //
980+ // Init tracking only operates on array layers, not on depth/volume slices
981+ // of 3D textures. Therefore,
982+
983+ const D3_WIDTH : u32 = 256 ;
984+ const D3_HEIGHT : u32 = 2 ;
985+ const D3_DEPTH : u32 = 4 ;
986+
987+ // A read from a fresh 3D texture as a copy *source* at `origin.z >= 1` must
988+ // trigger initialization of the full texture.
989+ #[ gpu_test]
990+ static COPY_TEXTURE_TO_BUFFER_3D_SOURCE_ORIGIN_Z_UNINIT : GpuTestConfiguration =
991+ GpuTestConfiguration :: new ( )
992+ . parameters ( TestParameters :: default ( ) . limits ( Limits :: downlevel_defaults ( ) ) )
993+ . run_async ( |ctx| async move {
994+ check_3d_copy_source_init ( & ctx, ReadMethod :: CopyTextureToBuffer ) . await ;
995+ } ) ;
996+
997+ #[ gpu_test]
998+ static COPY_TEXTURE_TO_TEXTURE_3D_SOURCE_ORIGIN_Z_UNINIT : GpuTestConfiguration =
999+ GpuTestConfiguration :: new ( )
1000+ . parameters ( TestParameters :: default ( ) . limits ( Limits :: downlevel_defaults ( ) ) )
1001+ . run_async ( |ctx| async move {
1002+ check_3d_copy_source_init ( & ctx, ReadMethod :: CopyTextureToTexture ) . await ;
1003+ } ) ;
1004+
1005+ // The first depth slice must be initialized to zero before a partial copy into a fresh 3D
1006+ // texture with destination `origin.z >= 1`.
1007+ #[ gpu_test]
1008+ static COPY_BUFFER_TO_TEXTURE_3D_DEST_ORIGIN_Z_PARTIAL : GpuTestConfiguration =
1009+ GpuTestConfiguration :: new ( )
1010+ . parameters ( TestParameters :: default ( ) . limits ( Limits :: downlevel_defaults ( ) ) )
1011+ . run_async ( |ctx| async move {
1012+ check_3d_copy_dest_init ( & ctx, WriteMethod :: CopyBufferToTexture ) . await ;
1013+ } ) ;
1014+
1015+ #[ gpu_test]
1016+ static COPY_TEXTURE_TO_TEXTURE_3D_DEST_ORIGIN_Z_PARTIAL : GpuTestConfiguration =
1017+ GpuTestConfiguration :: new ( )
1018+ . parameters ( TestParameters :: default ( ) . limits ( Limits :: downlevel_defaults ( ) ) )
1019+ . run_async ( |ctx| async move {
1020+ check_3d_copy_dest_init ( & ctx, WriteMethod :: CopyTextureToTexture ) . await ;
1021+ } ) ;
1022+
1023+ fn create_3d_texture ( ctx : & TestingContext , label : & str , depth : u32 ) -> Texture {
1024+ ctx. device . create_texture ( & TextureDescriptor {
1025+ label : Some ( label) ,
1026+ size : Extent3d {
1027+ width : D3_WIDTH ,
1028+ height : D3_HEIGHT ,
1029+ depth_or_array_layers : depth,
1030+ } ,
1031+ mip_level_count : 1 ,
1032+ sample_count : 1 ,
1033+ dimension : TextureDimension :: D3 ,
1034+ format : TextureFormat :: R8Uint ,
1035+ usage : TextureUsages :: COPY_SRC | TextureUsages :: COPY_DST ,
1036+ view_formats : & [ ] ,
1037+ } )
1038+ }
1039+
1040+ fn d3_buffer_layout ( ) -> TexelCopyBufferLayout {
1041+ TexelCopyBufferLayout {
1042+ offset : 0 ,
1043+ bytes_per_row : Some ( D3_WIDTH ) ,
1044+ rows_per_image : Some ( D3_HEIGHT ) ,
1045+ }
1046+ }
1047+
1048+ async fn map_and_read ( ctx : & TestingContext , buffer : & Buffer ) -> Vec < u8 > {
1049+ let slice = buffer. slice ( ..) ;
1050+ slice. map_async ( MapMode :: Read , |_| ( ) ) ;
1051+ ctx. async_poll ( PollType :: wait_indefinitely ( ) ) . await . unwrap ( ) ;
1052+ slice. get_mapped_range ( ) . unwrap ( ) . to_vec ( )
1053+ }
1054+
1055+ async fn check_3d_copy_source_init ( ctx : & TestingContext , method : ReadMethod ) {
1056+ const COPY_Z : u32 = 1 ;
1057+ let copy_depth = D3_DEPTH - COPY_Z ;
1058+ let copy_size = Extent3d {
1059+ width : D3_WIDTH ,
1060+ height : D3_HEIGHT ,
1061+ depth_or_array_layers : copy_depth,
1062+ } ;
1063+
1064+ let src = create_3d_texture ( ctx, "3d source init test" , D3_DEPTH ) ;
1065+ let src_info = TexelCopyTextureInfo {
1066+ texture : & src,
1067+ mip_level : 0 ,
1068+ origin : Origin3d {
1069+ x : 0 ,
1070+ y : 0 ,
1071+ z : COPY_Z ,
1072+ } ,
1073+ aspect : TextureAspect :: All ,
1074+ } ;
1075+
1076+ let mut encoder = ctx
1077+ . device
1078+ . create_command_encoder ( & CommandEncoderDescriptor { label : None } ) ;
1079+ match method {
1080+ ReadMethod :: CopyTextureToBuffer => {
1081+ // The copy source is partial (it starts at origin.z = COPY_Z), so the
1082+ // readback can't go through `ReadbackBuffers`, which always copies the
1083+ // full texture from the origin.
1084+ let readback = ctx. device . create_buffer ( & BufferDescriptor {
1085+ label : Some ( "3d source readback" ) ,
1086+ size : ( D3_WIDTH * D3_HEIGHT * copy_depth) as u64 ,
1087+ usage : BufferUsages :: MAP_READ | BufferUsages :: COPY_DST ,
1088+ mapped_at_creation : false ,
1089+ } ) ;
1090+ encoder. copy_texture_to_buffer (
1091+ src_info,
1092+ TexelCopyBufferInfo {
1093+ buffer : & readback,
1094+ layout : d3_buffer_layout ( ) ,
1095+ } ,
1096+ copy_size,
1097+ ) ;
1098+ ctx. queue . submit ( Some ( encoder. finish ( ) ) ) ;
1099+
1100+ let data = map_and_read ( ctx, & readback) . await ;
1101+ let nonzero = data. iter ( ) . position ( |& b| b != 0 ) ;
1102+ assert ! (
1103+ nonzero. is_none( ) ,
1104+ "3D texture used as {} source at origin.z={} read back non-zero from \
1105+ never-written memory; first non-zero byte at offset {} = 0x{:02x}",
1106+ method. name( ) ,
1107+ COPY_Z ,
1108+ nonzero. unwrap( ) ,
1109+ data[ nonzero. unwrap( ) ] ,
1110+ ) ;
1111+ }
1112+ ReadMethod :: CopyTextureToTexture => {
1113+ let dst = create_3d_texture ( ctx, "3d source init test dst" , copy_depth) ;
1114+ encoder. copy_texture_to_texture (
1115+ src_info,
1116+ TexelCopyTextureInfo {
1117+ texture : & dst,
1118+ mip_level : 0 ,
1119+ origin : Origin3d :: ZERO ,
1120+ aspect : TextureAspect :: All ,
1121+ } ,
1122+ copy_size,
1123+ ) ;
1124+ // The full `dst` is read back, so route it through `ReadbackBuffers`.
1125+ let readback_buffers = ReadbackBuffers :: new ( & ctx. device , & dst) ;
1126+ readback_buffers. copy_from ( & ctx. device , & mut encoder, & dst) ;
1127+ ctx. queue . submit ( Some ( encoder. finish ( ) ) ) ;
1128+
1129+ assert ! (
1130+ readback_buffers. are_zero( ctx) . await ,
1131+ "3D texture used as {} source at origin.z={} read back non-zero from \
1132+ never-written memory",
1133+ method. name( ) ,
1134+ COPY_Z ,
1135+ ) ;
1136+ }
1137+ }
1138+ }
1139+
1140+ async fn check_3d_copy_dest_init ( ctx : & TestingContext , method : WriteMethod ) {
1141+ const DST_Z : u32 = 1 ;
1142+ const SENTINEL : u8 = 0xAA ;
1143+ let slice_bytes = ( D3_WIDTH * D3_HEIGHT ) as usize ;
1144+ let one_slice = Extent3d {
1145+ width : D3_WIDTH ,
1146+ height : D3_HEIGHT ,
1147+ depth_or_array_layers : 1 ,
1148+ } ;
1149+
1150+ let dst = create_3d_texture ( ctx, "3d dest init test" , D3_DEPTH ) ;
1151+ let dst_info = TexelCopyTextureInfo {
1152+ texture : & dst,
1153+ mip_level : 0 ,
1154+ origin : Origin3d {
1155+ x : 0 ,
1156+ y : 0 ,
1157+ z : DST_Z ,
1158+ } ,
1159+ aspect : TextureAspect :: All ,
1160+ } ;
1161+
1162+ let mut encoder = ctx
1163+ . device
1164+ . create_command_encoder ( & CommandEncoderDescriptor { label : None } ) ;
1165+ match method {
1166+ WriteMethod :: CopyBufferToTexture => {
1167+ let src_buffer = ctx. device . create_buffer ( & BufferDescriptor {
1168+ label : Some ( "3d dest init source" ) ,
1169+ size : slice_bytes as u64 ,
1170+ usage : BufferUsages :: COPY_SRC ,
1171+ mapped_at_creation : true ,
1172+ } ) ;
1173+ {
1174+ let mut view = src_buffer. slice ( ..) . get_mapped_range_mut ( ) . unwrap ( ) ;
1175+ view. copy_from_slice ( & vec ! [ SENTINEL ; slice_bytes] ) ;
1176+ }
1177+ src_buffer. unmap ( ) ;
1178+ encoder. copy_buffer_to_texture (
1179+ TexelCopyBufferInfo {
1180+ buffer : & src_buffer,
1181+ layout : d3_buffer_layout ( ) ,
1182+ } ,
1183+ dst_info,
1184+ one_slice,
1185+ ) ;
1186+ }
1187+ WriteMethod :: CopyTextureToTexture => {
1188+ // Initialize the source, then copy a single slice into the destination at z=1.
1189+ let src_tex = create_3d_texture ( ctx, "3d dest init source texture" , 1 ) ;
1190+ ctx. queue . write_texture (
1191+ TexelCopyTextureInfo {
1192+ texture : & src_tex,
1193+ mip_level : 0 ,
1194+ origin : Origin3d :: ZERO ,
1195+ aspect : TextureAspect :: All ,
1196+ } ,
1197+ & vec ! [ SENTINEL ; slice_bytes] ,
1198+ d3_buffer_layout ( ) ,
1199+ one_slice,
1200+ ) ;
1201+ ctx. queue . submit ( None ) ;
1202+ encoder. copy_texture_to_texture (
1203+ TexelCopyTextureInfo {
1204+ texture : & src_tex,
1205+ mip_level : 0 ,
1206+ origin : Origin3d :: ZERO ,
1207+ aspect : TextureAspect :: All ,
1208+ } ,
1209+ dst_info,
1210+ one_slice,
1211+ ) ;
1212+ }
1213+ WriteMethod :: WriteTexture => {
1214+ unreachable ! ( "3D dest-init tests exercise only the encoder copy commands" )
1215+ }
1216+ }
1217+ // Submit the partial copy on its own to ensure the init action is applied on its own,
1218+ // and not in combination with an init action for the readback.
1219+ ctx. queue . submit ( Some ( encoder. finish ( ) ) ) ;
1220+
1221+ // The whole texture is read back from the origin, so route it through
1222+ // `ReadbackBuffers`. The written slice (z = DST_Z) must keep its sentinel data
1223+ // and every untouched slice must be zero-initialized.
1224+ let readback_buffers = ReadbackBuffers :: new ( & ctx. device , & dst) ;
1225+ let mut encoder = ctx
1226+ . device
1227+ . create_command_encoder ( & CommandEncoderDescriptor { label : None } ) ;
1228+ readback_buffers. copy_from ( & ctx. device , & mut encoder, & dst) ;
1229+ ctx. queue . submit ( Some ( encoder. finish ( ) ) ) ;
1230+
1231+ let mut expected = vec ! [ 0u8 ; slice_bytes * D3_DEPTH as usize ] ;
1232+ let written_start = DST_Z as usize * slice_bytes;
1233+ expected[ written_start..written_start + slice_bytes] . fill ( SENTINEL ) ;
1234+ readback_buffers
1235+ . assert_buffer_contents ( ctx, & expected)
1236+ . await ;
1237+ }
0 commit comments