@@ -398,11 +398,29 @@ bool IsAlignedTo(uint32_t offset, uint32_t alignment) {
398
398
return 0 == (offset % alignment);
399
399
}
400
400
401
+ std::string getStorageClassString (spv::StorageClass sc) {
402
+ switch (sc) {
403
+ case spv::StorageClass::Uniform:
404
+ return " Uniform" ;
405
+ case spv::StorageClass::UniformConstant:
406
+ return " UniformConstant" ;
407
+ case spv::StorageClass::PushConstant:
408
+ return " PushConstant" ;
409
+ case spv::StorageClass::Workgroup:
410
+ return " Workgroup" ;
411
+ case spv::StorageClass::PhysicalStorageBuffer:
412
+ return " PhysicalStorageBuffer" ;
413
+ default :
414
+ // Only other valid storage class in these checks
415
+ return " StorageBuffer" ;
416
+ }
417
+ }
418
+
401
419
// Returns SPV_SUCCESS if the given struct satisfies standard layout rules for
402
420
// Block or BufferBlocks in Vulkan. Otherwise emits a diagnostic and returns
403
421
// something other than SPV_SUCCESS. Matrices inherit the specified column
404
422
// or row major-ness.
405
- spv_result_t checkLayout (uint32_t struct_id, const char * storage_class_str ,
423
+ spv_result_t checkLayout (uint32_t struct_id, spv::StorageClass storage_class ,
406
424
const char * decoration_str, bool blockRules,
407
425
bool scalar_block_layout, uint32_t incoming_offset,
408
426
MemberConstraints& constraints,
@@ -418,22 +436,48 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str,
418
436
// is more permissive than relaxed layout.
419
437
const bool relaxed_block_layout = vstate.IsRelaxedBlockLayout ();
420
438
421
- auto fail = [&vstate, struct_id, storage_class_str , decoration_str,
422
- blockRules, relaxed_block_layout,
439
+ auto fail = [&vstate, struct_id, storage_class , decoration_str, blockRules ,
440
+ relaxed_block_layout,
423
441
scalar_block_layout](uint32_t member_idx) -> DiagnosticStream {
424
- DiagnosticStream ds =
425
- std::move ( vstate.diag (SPV_ERROR_INVALID_ID, vstate.FindDef (struct_id))
426
- << " Structure id " << struct_id << " decorated as "
427
- << decoration_str << " for variable in " << storage_class_str
428
- << " storage class must follow "
429
- << (scalar_block_layout
430
- ? " scalar "
431
- : (relaxed_block_layout ? " relaxed " : " standard " ))
432
- << (blockRules ? " uniform buffer" : " storage buffer" )
433
- << " layout rules: member " << member_idx << " " );
442
+ DiagnosticStream ds = std::move (
443
+ vstate.diag (SPV_ERROR_INVALID_ID, vstate.FindDef (struct_id))
444
+ << " Structure id " << struct_id << " decorated as " << decoration_str
445
+ << " for variable in " << getStorageClassString (storage_class)
446
+ << " storage class must follow "
447
+ << (scalar_block_layout
448
+ ? " scalar "
449
+ : (relaxed_block_layout ? " relaxed " : " standard " ))
450
+ << (blockRules ? " uniform buffer" : " storage buffer" )
451
+ << " layout rules: member " << member_idx << " " );
434
452
return ds;
435
453
};
436
454
455
+ // People often use spirv-val from Vulkan Validation Layers, it ends up
456
+ // mapping the various block layout rules from the enabled feature. This
457
+ // offers a hint to help the user understand possbily why things are not
458
+ // working when the shader itself "seems" valid, but just was a lack of adding
459
+ // a supported feature
460
+ auto extra = [&vstate, scalar_block_layout, storage_class,
461
+ relaxed_block_layout, blockRules]() {
462
+ if (!scalar_block_layout) {
463
+ if (storage_class == spv::StorageClass::Workgroup) {
464
+ return vstate.MissingFeature (
465
+ " workgroupMemoryExplicitLayoutScalarBlockLayout feature" ,
466
+ " --workgroup-scalar-block-layout" , true );
467
+ } else if (!relaxed_block_layout) {
468
+ return vstate.MissingFeature (" VK_KHR_relaxed_block_layout extension" ,
469
+ " --relax-block-layout" , true );
470
+ } else if (blockRules) {
471
+ return vstate.MissingFeature (" uniformBufferStandardLayout feature" ,
472
+ " --uniform-buffer-standard-layout" , true );
473
+ } else {
474
+ return vstate.MissingFeature (" scalarBlockLayoutfeature feature" ,
475
+ " --scalar-block-layout" , true );
476
+ }
477
+ }
478
+ return std::string (" " );
479
+ };
480
+
437
481
// If we are checking the layout of untyped pointers or physical storage
438
482
// buffer pointers, we may not actually have a struct here. Instead, pretend
439
483
// we have a struct with a single member at offset 0.
@@ -507,7 +551,7 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str,
507
551
const auto size = getSize (id, constraint, constraints, vstate);
508
552
// Check offset.
509
553
if (offset == 0xffffffff )
510
- return fail (memberIdx) << " is missing an Offset decoration" ;
554
+ return fail (memberIdx) << " is missing an Offset decoration" << extra () ;
511
555
512
556
if (opcode == spv::Op::OpTypeRuntimeArray &&
513
557
ordered_member_idx != member_offsets.size () - 1 ) {
@@ -524,42 +568,44 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str,
524
568
const auto componentId = inst->words ()[2 ];
525
569
const auto scalar_alignment = getScalarAlignment (componentId, vstate);
526
570
if (!IsAlignedTo (offset, scalar_alignment)) {
527
- return fail (memberIdx)
528
- << " at offset " << offset
529
- << " is not aligned to scalar element size " << scalar_alignment;
571
+ return fail (memberIdx) << " at offset " << offset
572
+ << " is not aligned to scalar element size "
573
+ << scalar_alignment << extra () ;
530
574
}
531
575
} else {
532
576
// Without relaxed block layout, the offset must be divisible by the
533
577
// alignment requirement.
534
578
if (!IsAlignedTo (offset, alignment)) {
535
- return fail (memberIdx)
536
- << " at offset " << offset << " is not aligned to " << alignment;
579
+ return fail (memberIdx) << " at offset " << offset
580
+ << " is not aligned to " << alignment << extra () ;
537
581
}
538
582
}
539
583
if (offset < nextValidOffset)
540
584
return fail (memberIdx) << " at offset " << offset
541
585
<< " overlaps previous member ending at offset "
542
- << nextValidOffset - 1 ;
586
+ << nextValidOffset - 1 << extra () ;
543
587
if (!scalar_block_layout && relaxed_block_layout) {
544
588
// Check improper straddle of vectors.
545
589
if (spv::Op::OpTypeVector == opcode &&
546
590
hasImproperStraddle (id, offset, constraint, constraints, vstate))
547
591
return fail (memberIdx)
548
- << " is an improperly straddling vector at offset " << offset;
592
+ << " is an improperly straddling vector at offset " << offset
593
+ << extra ();
549
594
}
550
595
// Check struct members recursively.
551
596
spv_result_t recursive_status = SPV_SUCCESS;
552
597
if (spv::Op::OpTypeStruct == opcode &&
553
598
SPV_SUCCESS != (recursive_status = checkLayout (
554
- id, storage_class_str , decoration_str, blockRules,
599
+ id, storage_class , decoration_str, blockRules,
555
600
scalar_block_layout, offset, constraints, vstate)))
556
601
return recursive_status;
557
602
// Check matrix stride.
558
603
if (spv::Op::OpTypeMatrix == opcode) {
559
604
const auto stride = constraint.matrix_stride ;
560
605
if (!IsAlignedTo (stride, alignment)) {
561
- return fail (memberIdx) << " is a matrix with stride " << stride
562
- << " not satisfying alignment to " << alignment;
606
+ return fail (memberIdx)
607
+ << " is a matrix with stride " << stride
608
+ << " not satisfying alignment to " << alignment << extra ();
563
609
}
564
610
}
565
611
@@ -576,12 +622,13 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str,
576
622
if (spv::Decoration::ArrayStride == decoration.dec_type ()) {
577
623
array_stride = decoration.params ()[0 ];
578
624
if (array_stride == 0 ) {
579
- return fail (memberIdx) << " contains an array with stride 0" ;
625
+ return fail (memberIdx)
626
+ << " contains an array with stride 0" << extra ();
580
627
}
581
628
if (!IsAlignedTo (array_stride, array_alignment))
582
629
return fail (memberIdx)
583
630
<< " contains an array with stride " << decoration.params ()[0 ]
584
- << " not satisfying alignment to " << alignment;
631
+ << " not satisfying alignment to " << alignment << extra () ;
585
632
}
586
633
}
587
634
@@ -608,7 +655,7 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str,
608
655
609
656
if (SPV_SUCCESS !=
610
657
(recursive_status = checkLayout (
611
- typeId, storage_class_str , decoration_str, blockRules,
658
+ typeId, storage_class , decoration_str, blockRules,
612
659
scalar_block_layout, next_offset, constraints, vstate)))
613
660
return recursive_status;
614
661
@@ -620,7 +667,7 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str,
620
667
if (!IsAlignedTo (stride, alignment)) {
621
668
return fail (memberIdx)
622
669
<< " is a matrix with stride " << stride
623
- << " not satisfying alignment to " << alignment;
670
+ << " not satisfying alignment to " << alignment << extra () ;
624
671
}
625
672
}
626
673
@@ -636,7 +683,7 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str,
636
683
if (element_size > array_stride) {
637
684
return fail (memberIdx)
638
685
<< " contains an array with stride " << array_stride
639
- << " , but with an element size of " << element_size;
686
+ << " , but with an element size of " << element_size << extra () ;
640
687
}
641
688
}
642
689
nextValidOffset = offset + size;
@@ -1207,8 +1254,8 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
1207
1254
if (!entry_points.empty () &&
1208
1255
!hasDecoration (var_id, spv::Decoration::Binding, vstate)) {
1209
1256
return vstate.diag (SPV_ERROR_INVALID_ID, vstate.FindDef (var_id))
1210
- << (uniform ? " Uniform " : " Storage Buffer " ) << " id '"
1211
- << var_id << " ' is missing Binding decoration.\n "
1257
+ << getStorageClassString (storageClass ) << " id '" << var_id
1258
+ << " ' is missing Binding decoration.\n "
1212
1259
<< " From ARB_gl_spirv extension:\n "
1213
1260
<< " Uniform and shader storage block variables must "
1214
1261
<< " also be decorated with a *Binding*." ;
@@ -1243,12 +1290,6 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
1243
1290
ComputeMemberConstraintsForStruct (&constraints, id,
1244
1291
LayoutConstraints (), vstate);
1245
1292
}
1246
- // Prepare for messages
1247
- const char * sc_str =
1248
- uniform
1249
- ? " Uniform"
1250
- : (push_constant ? " PushConstant"
1251
- : (workgroup ? " Workgroup" : " StorageBuffer" ));
1252
1293
1253
1294
if (spvIsVulkanEnv (vstate.context ()->target_env )) {
1254
1295
const bool block = hasDecoration (id, spv::Decoration::Block, vstate);
@@ -1294,7 +1335,8 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
1294
1335
!hasDecoration (var_id, spv::Decoration::DescriptorSet,
1295
1336
vstate)) {
1296
1337
return vstate.diag (SPV_ERROR_INVALID_ID, vstate.FindDef (var_id))
1297
- << vstate.VkErrorID (6677 ) << sc_str << " id '" << var_id
1338
+ << vstate.VkErrorID (6677 )
1339
+ << getStorageClassString (storageClass) << " id '" << var_id
1298
1340
<< " ' is missing DescriptorSet decoration.\n "
1299
1341
<< " From Vulkan spec:\n "
1300
1342
<< " These variables must have DescriptorSet and Binding "
@@ -1303,7 +1345,8 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
1303
1345
if (!entry_points.empty () &&
1304
1346
!hasDecoration (var_id, spv::Decoration::Binding, vstate)) {
1305
1347
return vstate.diag (SPV_ERROR_INVALID_ID, vstate.FindDef (var_id))
1306
- << vstate.VkErrorID (6677 ) << sc_str << " id '" << var_id
1348
+ << vstate.VkErrorID (6677 )
1349
+ << getStorageClassString (storageClass) << " id '" << var_id
1307
1350
<< " ' is missing Binding decoration.\n "
1308
1351
<< " From Vulkan spec:\n "
1309
1352
<< " These variables must have DescriptorSet and Binding "
@@ -1386,14 +1429,14 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
1386
1429
if (spvIsVulkanEnv (vstate.context ()->target_env )) {
1387
1430
if (blockRules &&
1388
1431
(SPV_SUCCESS !=
1389
- (recursive_status = checkLayout (id, sc_str, deco_str, true ,
1390
- scalar_block_layout, 0 ,
1391
- constraints, vstate)))) {
1432
+ (recursive_status = checkLayout (
1433
+ id, storageClass, deco_str, true , scalar_block_layout ,
1434
+ 0 , constraints, vstate)))) {
1392
1435
return recursive_status;
1393
1436
} else if (bufferRules &&
1394
1437
(SPV_SUCCESS != (recursive_status = checkLayout (
1395
- id, sc_str , deco_str, false ,
1396
- scalar_block_layout, 0 ,
1438
+ id, storageClass , deco_str,
1439
+ false , scalar_block_layout, 0 ,
1397
1440
constraints, vstate)))) {
1398
1441
return recursive_status;
1399
1442
}
@@ -1413,9 +1456,9 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
1413
1456
ComputeMemberConstraintsForStruct (&constraints, pointee_type_id,
1414
1457
LayoutConstraints (), vstate);
1415
1458
}
1416
- if (auto res = checkLayout (pointee_type_id, " PhysicalStorageBuffer " ,
1417
- " Block " , !buffer, scalar_block_layout, 0 ,
1418
- constraints, vstate)) {
1459
+ if (auto res = checkLayout (
1460
+ pointee_type_id, spv::StorageClass::PhysicalStorageBuffer ,
1461
+ " Block " , !buffer, scalar_block_layout, 0 , constraints, vstate)) {
1419
1462
return res;
1420
1463
}
1421
1464
} else if (vstate.HasCapability (spv::Capability::UntypedPointersKHR) &&
@@ -1464,14 +1507,6 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
1464
1507
const auto sc =
1465
1508
vstate.FindDef (ptr_ty_id)->GetOperandAs <spv::StorageClass>(1 );
1466
1509
1467
- const char * sc_str =
1468
- sc == spv::StorageClass::Uniform
1469
- ? " Uniform"
1470
- : (sc == spv::StorageClass::PushConstant
1471
- ? " PushConstant"
1472
- : (sc == spv::StorageClass::Workgroup ? " Workgroup"
1473
- : " StorageBuffer" ));
1474
-
1475
1510
auto data_type = vstate.FindDef (data_type_id);
1476
1511
scalar_block_layout =
1477
1512
sc == spv::StorageClass::Workgroup
@@ -1511,7 +1546,7 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
1511
1546
? (sc == spv::StorageClass::Uniform ? " BufferBlock" : " Block" )
1512
1547
: " Block" ;
1513
1548
if (auto result =
1514
- checkLayout (data_type_id, sc_str , deco_str, !bufferRules,
1549
+ checkLayout (data_type_id, sc , deco_str, !bufferRules,
1515
1550
scalar_block_layout, 0 , constraints, vstate)) {
1516
1551
return result;
1517
1552
}
0 commit comments