@@ -48,6 +48,15 @@ impl ValidationRule for BlockLayoutRule {
4848 continue ;
4949 }
5050
51+ // Std140 extended alignment (16-byte rounding for arrays/structs) only
52+ // applies to Uniform + Block decoration. All other combinations
53+ // (StorageBuffer + Block, BufferBlock + Uniform, PushConstant + Block, etc.)
54+ // use std430 rules which have no such rounding. This matches the C++
55+ // SPIRV-Tools behavior in validate_decorations.cpp (blockRules vs bufferRules).
56+ let is_uniform_block = block_info. decoration == BlockDecoration :: Block
57+ && block_info. storage_classes . contains ( & StorageClass :: Uniform ) ;
58+ let extended_alignment = is_uniform_block && !relax_layout;
59+
5160 let Some ( struct_inst) = ctx. definitions . get ( & struct_id) else {
5261 continue ;
5362 } ;
@@ -79,13 +88,12 @@ impl ValidationRule for BlockLayoutRule {
7988 let Some ( member_inst) = ctx. definitions . get ( & member_result_id) else {
8089 continue ;
8190 } ;
82- // Pass relax_layout to skip std140's 16-byte extended alignment rule
83- // With relaxed or scalar layout, arrays/structs align to their natural alignment
8491 let Some ( alignment) = type_alignment (
8592 member_type_id,
8693 ctx. definitions ,
8794 & mut HashSet :: new ( ) ,
8895 relax_layout,
96+ extended_alignment,
8997 ) else {
9098 continue ;
9199 } ;
@@ -507,21 +515,29 @@ fn type_alignment(
507515 definitions : & HashMap < ResultId , rspirv:: dr:: Instruction > ,
508516 visiting : & mut HashSet < TypeId > ,
509517 scalar_layout : bool ,
518+ extended_alignment : bool ,
510519) -> Option < u32 > {
511- type_alignment_extended ( ty, definitions, visiting, scalar_layout, false )
520+ type_alignment_extended ( ty, definitions, visiting, scalar_layout, false , extended_alignment )
512521}
513522
514523/// Calculates type alignment with optional extended alignment for std140 rules.
515524///
516- /// In std140:
525+ /// In std140 (Uniform + Block) :
517526/// - Arrays and structs have their base alignment rounded up to 16 bytes
518527/// - This is called "extended alignment"
528+ ///
529+ /// In std430 (StorageBuffer + Block, BufferBlock + Uniform, PushConstant + Block, etc.):
530+ /// - No 16-byte rounding is applied
531+ ///
532+ /// The `extended_alignment` parameter controls whether the 16-byte rounding applies.
533+ /// It should be `true` only for std140 (Uniform + Block) when not using relaxed/scalar layout.
519534fn type_alignment_extended (
520535 ty : TypeId ,
521536 definitions : & HashMap < ResultId , rspirv:: dr:: Instruction > ,
522537 visiting : & mut HashSet < TypeId > ,
523538 scalar_layout : bool ,
524539 use_extended : bool ,
540+ extended_alignment : bool ,
525541) -> Option < u32 > {
526542 if !visiting. insert ( ty) {
527543 return None ;
@@ -536,7 +552,7 @@ fn type_alignment_extended(
536552 let ( elem, count) = vector_info ( inst) ;
537553 let ( elem, count) = ( elem?, count?) ;
538554 let elem_align =
539- type_alignment_extended ( elem, definitions, visiting, scalar_layout, false ) ?;
555+ type_alignment_extended ( elem, definitions, visiting, scalar_layout, false , extended_alignment ) ?;
540556 if scalar_layout {
541557 Some ( elem_align)
542558 } else {
@@ -549,17 +565,16 @@ fn type_alignment_extended(
549565 // Matrix alignment follows its column vector alignment.
550566 let ( column, _) = matrix_info ( inst) ;
551567 let column = column?;
552- type_alignment_extended ( column, definitions, visiting, scalar_layout, use_extended)
568+ type_alignment_extended ( column, definitions, visiting, scalar_layout, use_extended, extended_alignment )
553569 }
554570 Op :: TypeArray | Op :: TypeRuntimeArray => {
555571 let elem = inst. operands . first ( ) . and_then ( |op| match op {
556572 rspirv:: dr:: Operand :: IdRef ( id) => TypeId :: try_from ( * id) . ok ( ) ,
557573 _ => None ,
558574 } ) ?;
559- // In std140, array element alignment is rounded up to 16 bytes (extended alignment)
560575 let base_align =
561- type_alignment_extended ( elem, definitions, visiting, scalar_layout, true ) ?;
562- if use_extended && !scalar_layout {
576+ type_alignment_extended ( elem, definitions, visiting, scalar_layout, true , extended_alignment ) ?;
577+ if use_extended && extended_alignment && !scalar_layout {
563578 Some ( round_up ( base_align, 16 ) )
564579 } else {
565580 Some ( base_align)
@@ -573,11 +588,10 @@ fn type_alignment_extended(
573588 _ => return None ,
574589 } ;
575590 let align =
576- type_alignment_extended ( ty, definitions, visiting, scalar_layout, true ) ?;
591+ type_alignment_extended ( ty, definitions, visiting, scalar_layout, true , extended_alignment ) ?;
577592 max_align = max_align. max ( align) ;
578593 }
579- // In std140, struct alignment is rounded up to 16 bytes (extended alignment)
580- if use_extended && !scalar_layout {
594+ if use_extended && extended_alignment && !scalar_layout {
581595 Some ( round_up ( max_align, 16 ) )
582596 } else {
583597 Some ( max_align)
@@ -597,7 +611,7 @@ fn vector_scalar_alignment(
597611 rspirv:: dr:: Operand :: IdRef ( id) => TypeId :: try_from ( * id) . ok ( ) ,
598612 _ => None ,
599613 } ) ?;
600- type_alignment ( elem, definitions, & mut HashSet :: new ( ) , true )
614+ type_alignment ( elem, definitions, & mut HashSet :: new ( ) , true , false )
601615}
602616
603617fn vector_info ( inst : & rspirv:: dr:: Instruction ) -> ( Option < TypeId > , Option < u32 > ) {
0 commit comments