Skip to content

Commit ebd6b81

Browse files
committed
Add tests for Vulkan Block/BufferBlock decoration checks
Tests for VulkanBufferBlockDecorationsRule covering: - PushConstant must have Block decoration - StorageBuffer must have Block (not BufferBlock) - Uniform must have Block or BufferBlock - Universal env uses MissingBlockDecoration (not Vulkan-specific error)
1 parent 7abbb35 commit ebd6b81

1 file changed

Lines changed: 228 additions & 0 deletions

File tree

  • rust/spirv-tools-core/src/validation/tests

rust/spirv-tools-core/src/validation/tests/mod.rs

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31914,3 +31914,231 @@ fn pointer_member_overlap_is_rejected() {
3191431914
.expect_err("int at offset 4 overlaps 8-byte pointer at offset 0");
3191531915
assert!(matches!(err, ValidationError::InvalidBlockLayout { .. }));
3191631916
}
31917+
31918+
// ============================================================================
31919+
// Vulkan Block/BufferBlock decoration tests (#11)
31920+
// ============================================================================
31921+
31922+
/// PushConstant variable must have Block decoration in Vulkan (VUID-06675).
31923+
#[test]
31924+
fn vulkan_push_constant_must_have_block_decoration() {
31925+
// PushConstant WITHOUT Block decoration
31926+
let text = [
31927+
"OpCapability Shader",
31928+
"OpMemoryModel Logical GLSL450",
31929+
"OpMemberDecorate %struct 0 Offset 0",
31930+
"%void = OpTypeVoid",
31931+
"%fn = OpTypeFunction %void",
31932+
"%int = OpTypeInt 32 0",
31933+
"%struct = OpTypeStruct %int",
31934+
"%ptr = OpTypePointer PushConstant %struct",
31935+
"%var = OpVariable %ptr PushConstant",
31936+
"OpEntryPoint Vertex %main \"main\" %var",
31937+
"%main = OpFunction %void None %fn",
31938+
"%entry = OpLabel",
31939+
"OpReturn",
31940+
"OpFunctionEnd",
31941+
]
31942+
.join("\n");
31943+
let err = text
31944+
.as_str()
31945+
.validate(TargetEnv::Vulkan1_0)
31946+
.expect_err("PushConstant without Block should fail in Vulkan");
31947+
assert!(matches!(
31948+
err,
31949+
ValidationError::VulkanBufferMissingBlockDecoration { .. }
31950+
));
31951+
}
31952+
31953+
/// PushConstant with Block decoration should pass.
31954+
#[test]
31955+
fn vulkan_push_constant_with_block_is_valid() {
31956+
let text = [
31957+
"OpCapability Shader",
31958+
"OpMemoryModel Logical GLSL450",
31959+
"OpDecorate %struct Block",
31960+
"OpMemberDecorate %struct 0 Offset 0",
31961+
"%void = OpTypeVoid",
31962+
"%fn = OpTypeFunction %void",
31963+
"%int = OpTypeInt 32 0",
31964+
"%struct = OpTypeStruct %int",
31965+
"%ptr = OpTypePointer PushConstant %struct",
31966+
"%var = OpVariable %ptr PushConstant",
31967+
"OpEntryPoint Vertex %main \"main\" %var",
31968+
"%main = OpFunction %void None %fn",
31969+
"%entry = OpLabel",
31970+
"OpReturn",
31971+
"OpFunctionEnd",
31972+
]
31973+
.join("\n");
31974+
text.as_str()
31975+
.validate(TargetEnv::Vulkan1_0)
31976+
.expect("PushConstant with Block decoration should pass");
31977+
}
31978+
31979+
/// StorageBuffer with BufferBlock is disallowed in Vulkan (VUID-06675).
31980+
#[test]
31981+
fn vulkan_storage_buffer_with_buffer_block_is_rejected() {
31982+
let text = [
31983+
"OpCapability Shader",
31984+
"OpMemoryModel Logical GLSL450",
31985+
"OpDecorate %struct BufferBlock",
31986+
"OpDecorate %var DescriptorSet 0",
31987+
"OpDecorate %var Binding 0",
31988+
"OpMemberDecorate %struct 0 Offset 0",
31989+
"%int = OpTypeInt 32 0",
31990+
"%struct = OpTypeStruct %int",
31991+
"%ptr = OpTypePointer StorageBuffer %struct",
31992+
"%var = OpVariable %ptr StorageBuffer",
31993+
]
31994+
.join("\n");
31995+
let err = text
31996+
.as_str()
31997+
.validate(TargetEnv::Vulkan1_1)
31998+
.expect_err("StorageBuffer with BufferBlock should fail in Vulkan");
31999+
assert!(matches!(
32000+
err,
32001+
ValidationError::VulkanStorageBufferHasBufferBlock { .. }
32002+
));
32003+
}
32004+
32005+
/// StorageBuffer without Block is rejected in Vulkan (VUID-06675).
32006+
#[test]
32007+
fn vulkan_storage_buffer_must_have_block_decoration() {
32008+
let text = [
32009+
"OpCapability Shader",
32010+
"OpMemoryModel Logical GLSL450",
32011+
"OpDecorate %var DescriptorSet 0",
32012+
"OpDecorate %var Binding 0",
32013+
"OpMemberDecorate %struct 0 Offset 0",
32014+
"%int = OpTypeInt 32 0",
32015+
"%struct = OpTypeStruct %int",
32016+
"%ptr = OpTypePointer StorageBuffer %struct",
32017+
"%var = OpVariable %ptr StorageBuffer",
32018+
]
32019+
.join("\n");
32020+
let err = text
32021+
.as_str()
32022+
.validate(TargetEnv::Vulkan1_1)
32023+
.expect_err("StorageBuffer without Block should fail in Vulkan");
32024+
assert!(matches!(
32025+
err,
32026+
ValidationError::VulkanBufferMissingBlockDecoration { .. }
32027+
));
32028+
}
32029+
32030+
/// StorageBuffer with Block decoration should pass.
32031+
#[test]
32032+
fn vulkan_storage_buffer_with_block_is_valid() {
32033+
let text = [
32034+
"OpCapability Shader",
32035+
"OpMemoryModel Logical GLSL450",
32036+
"OpDecorate %struct Block",
32037+
"OpDecorate %var DescriptorSet 0",
32038+
"OpDecorate %var Binding 0",
32039+
"OpMemberDecorate %struct 0 Offset 0",
32040+
"%int = OpTypeInt 32 0",
32041+
"%struct = OpTypeStruct %int",
32042+
"%ptr = OpTypePointer StorageBuffer %struct",
32043+
"%var = OpVariable %ptr StorageBuffer",
32044+
]
32045+
.join("\n");
32046+
text.as_str()
32047+
.validate(TargetEnv::Vulkan1_1)
32048+
.expect("StorageBuffer with Block decoration should pass");
32049+
}
32050+
32051+
/// Uniform without Block or BufferBlock is rejected in Vulkan (VUID-06676).
32052+
#[test]
32053+
fn vulkan_uniform_must_have_block_or_buffer_block() {
32054+
let text = [
32055+
"OpCapability Shader",
32056+
"OpMemoryModel Logical GLSL450",
32057+
"OpDecorate %var DescriptorSet 0",
32058+
"OpDecorate %var Binding 0",
32059+
"OpMemberDecorate %struct 0 Offset 0",
32060+
"%int = OpTypeInt 32 0",
32061+
"%struct = OpTypeStruct %int",
32062+
"%ptr = OpTypePointer Uniform %struct",
32063+
"%var = OpVariable %ptr Uniform",
32064+
]
32065+
.join("\n");
32066+
let err = text
32067+
.as_str()
32068+
.validate(TargetEnv::Vulkan1_0)
32069+
.expect_err("Uniform without Block or BufferBlock should fail in Vulkan");
32070+
assert!(matches!(
32071+
err,
32072+
ValidationError::VulkanUniformMissingBlockDecoration { .. }
32073+
));
32074+
}
32075+
32076+
/// Uniform with Block decoration should pass.
32077+
#[test]
32078+
fn vulkan_uniform_with_block_is_valid() {
32079+
let text = [
32080+
"OpCapability Shader",
32081+
"OpMemoryModel Logical GLSL450",
32082+
"OpDecorate %struct Block",
32083+
"OpDecorate %var DescriptorSet 0",
32084+
"OpDecorate %var Binding 0",
32085+
"OpMemberDecorate %struct 0 Offset 0",
32086+
"%int = OpTypeInt 32 0",
32087+
"%struct = OpTypeStruct %int",
32088+
"%ptr = OpTypePointer Uniform %struct",
32089+
"%var = OpVariable %ptr Uniform",
32090+
]
32091+
.join("\n");
32092+
text.as_str()
32093+
.validate(TargetEnv::Vulkan1_0)
32094+
.expect("Uniform with Block decoration should pass");
32095+
}
32096+
32097+
/// Uniform with BufferBlock decoration should also pass.
32098+
#[test]
32099+
fn vulkan_uniform_with_buffer_block_is_valid() {
32100+
let text = [
32101+
"OpCapability Shader",
32102+
"OpMemoryModel Logical GLSL450",
32103+
"OpDecorate %struct BufferBlock",
32104+
"OpDecorate %var DescriptorSet 0",
32105+
"OpDecorate %var Binding 0",
32106+
"OpMemberDecorate %struct 0 Offset 0",
32107+
"%int = OpTypeInt 32 0",
32108+
"%struct = OpTypeStruct %int",
32109+
"%ptr = OpTypePointer Uniform %struct",
32110+
"%var = OpVariable %ptr Uniform",
32111+
]
32112+
.join("\n");
32113+
text.as_str()
32114+
.validate(TargetEnv::Vulkan1_0)
32115+
.expect("Uniform with BufferBlock decoration should pass");
32116+
}
32117+
32118+
/// Vulkan-specific checks use VUID error types. Under Universal, the base
32119+
/// SPIR-V spec also requires Block on StorageBuffer, but with a different
32120+
/// error type (MissingBlockDecoration vs VulkanBufferMissingBlockDecoration).
32121+
#[test]
32122+
fn universal_env_uses_base_spec_error_for_missing_block() {
32123+
let text = [
32124+
"OpCapability Shader",
32125+
"OpMemoryModel Logical GLSL450",
32126+
"OpDecorate %var DescriptorSet 0",
32127+
"OpDecorate %var Binding 0",
32128+
"OpMemberDecorate %struct 0 Offset 0",
32129+
"%int = OpTypeInt 32 0",
32130+
"%struct = OpTypeStruct %int",
32131+
"%ptr = OpTypePointer StorageBuffer %struct",
32132+
"%var = OpVariable %ptr StorageBuffer",
32133+
]
32134+
.join("\n");
32135+
// Under Universal, the base spec catches the missing Block decoration
32136+
let err = text
32137+
.as_str()
32138+
.validate(TargetEnv::Universal1_6)
32139+
.expect_err("Missing Block should fail in any env");
32140+
assert!(matches!(
32141+
err,
32142+
ValidationError::MissingBlockDecoration { .. }
32143+
));
32144+
}

0 commit comments

Comments
 (0)