Skip to content

Commit 968069d

Browse files
committed
Allow VulkanMemoryModel on Vulkan 1.1 via extension
Match C++ spirv-val behavior: capabilities enabled by declared extensions should bypass SPIR-V version requirements. Previously, VulkanMemoryModel had a special-case early return that rejected it before checking extensions. Changes: - Remove VulkanMemoryModel early return in capability validation - Add extension check to operand version validation (matching C++ OperandVersionExtensionCheck) so that declared extensions like SPV_KHR_vulkan_memory_model can relax SPIR-V version requirements for operands like VulkanKHR memory model - Add test for Vulkan 1.1 + VulkanMemoryModel with extension - Update test expectations to reflect more precise error types
1 parent e0e30b3 commit 968069d

File tree

3 files changed

+58
-19
lines changed

3 files changed

+58
-19
lines changed

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

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,8 @@ use rules::capabilities::{
9696
validate_capabilities,
9797
};
9898
use rules::extensions::{
99-
extension_operand, extension_satisfied, validate_extension_allowlist, validate_extensions,
100-
ExtensionSet,
99+
extension_operand, extension_satisfied, has_extension, validate_extension_allowlist,
100+
validate_extensions, ExtensionSet,
101101
};
102102
use rules::limits::all_limit_rules;
103103

@@ -946,12 +946,25 @@ fn validate_instruction_requirements(
946946
}
947947
if let Some(required_version) = required_spirv_version_for_operand(operand) {
948948
if target_version < required_version {
949-
return Err(ValidationError::OperandRequiresSpirvVersion {
950-
opcode: inst.class.opcode,
951-
operand_index: index,
952-
required_version,
953-
target_version,
954-
});
949+
// Check if an enabling extension is declared that can
950+
// relax the version requirement (matching C++ spirv-val's
951+
// OperandVersionExtensionCheck).
952+
let has_enabling_extension = operand
953+
.required_extensions()
954+
.iter()
955+
.any(|ext| has_extension(extensions, ext))
956+
|| grammar_required_extensions_for_operand(operand)
957+
.iter()
958+
.any(|ext| has_extension(extensions, ext));
959+
960+
if !has_enabling_extension {
961+
return Err(ValidationError::OperandRequiresSpirvVersion {
962+
opcode: inst.class.opcode,
963+
operand_index: index,
964+
required_version,
965+
target_version,
966+
});
967+
}
955968
}
956969
}
957970
// Collect ALL capabilities from all sources and check DISJUNCTIVELY

rust/spirv-tools-core/src/validation/rules/capabilities.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,6 @@ pub fn validate_capabilities(
8181
let grammar_requirements = capability_info_from_grammar(capability);
8282
let allowed_by_env = env.is_capability_allowed(capability);
8383

84-
if !allowed_by_env && capability == Capability::VulkanMemoryModel {
85-
return Err(ValidationError::DisallowedCapability { capability, env });
86-
}
87-
8884
if env.is_opencl()
8985
&& matches!(
9086
capability,
@@ -211,6 +207,11 @@ pub fn validate_capabilities(
211207
let allowed_by_extension = capability_allowed_by_extension(env, capability, extensions);
212208
let allowed_by_capability =
213209
capability_enabled_by_capability(env, capability, &declared);
210+
211+
// If the capability is not allowed by any means (env allowlist,
212+
// extension, or implied by another capability), reject it
213+
// immediately. This matches C++ spirv-val which checks env/ext
214+
// allowlists first.
214215
if !(allowed_by_env || allowed_by_extension || allowed_by_capability) {
215216
return Err(ValidationError::DisallowedCapability { capability, env });
216217
}

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

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6331,16 +6331,40 @@ fn vulkan_1_0_rejects_vulkan_memory_model() {
63316331
let error = text
63326332
.as_str()
63336333
.validate(TargetEnv::Vulkan1_0)
6334-
.expect_err("Vulkan memory model is 1.2+ optional");
6334+
.expect_err("Vulkan memory model requires SPIR-V 1.5");
63356335
assert_eq!(
63366336
error,
6337-
ValidationError::DisallowedCapability {
6337+
ValidationError::CapabilityRequiresSpirvVersion {
63386338
capability: rspirv::spirv::Capability::VulkanMemoryModel,
6339-
env: TargetEnv::Vulkan1_0
6339+
required_version: SpirvVersion::new(1, 5),
6340+
target_version: TargetEnv::Vulkan1_0.spirv_version(),
63406341
}
63416342
);
63426343
}
63436344
#[test]
6345+
fn vulkan_1_1_allows_vulkan_memory_model_with_extension() {
6346+
// VulkanMemoryModel is available in Vulkan 1.1 via SPV_KHR_vulkan_memory_model extension.
6347+
// The capability requires SPIR-V 1.5, but the extension enables it on earlier versions.
6348+
let text = [
6349+
"OpCapability Shader",
6350+
"OpCapability VulkanMemoryModel",
6351+
"OpExtension \"SPV_KHR_vulkan_memory_model\"",
6352+
"OpMemoryModel Logical VulkanKHR",
6353+
"%void = OpTypeVoid",
6354+
"%fn = OpTypeFunction %void",
6355+
"%main = OpFunction %void None %fn",
6356+
"%entry = OpLabel",
6357+
"OpReturn",
6358+
"OpFunctionEnd",
6359+
]
6360+
.join("\n");
6361+
let module = text
6362+
.as_str()
6363+
.validate(TargetEnv::Vulkan1_1)
6364+
.expect("VulkanMemoryModel is optional in Vulkan 1.1 (via SPV_KHR_vulkan_memory_model)");
6365+
assert_eq!(module.env(), TargetEnv::Vulkan1_1);
6366+
}
6367+
#[test]
63446368
fn vulkan_1_2_allows_physical_storage_buffer_addresses() {
63456369
let text = [
63466370
"OpCapability Shader",
@@ -17525,14 +17549,15 @@ fn memory_model_vulkan_requires_spirv_1_5() {
1752517549
let error = words
1752617550
.as_slice()
1752717551
.validate(TargetEnv::Vulkan1_1Spirv1_4)
17528-
.expect_err("Vulkan memory model requires SPIR-V 1.5");
17552+
.expect_err("VulkanKHR memory model operand requires VulkanMemoryModel capability");
17553+
// The extension satisfies the SPIR-V version requirement, so the real
17554+
// error surfaces: the VulkanMemoryModel capability is not declared.
1752917555
assert_eq!(
1753017556
error,
17531-
ValidationError::OperandRequiresSpirvVersion {
17557+
ValidationError::MissingOperandCapability {
1753217558
opcode: rspirv::spirv::Op::MemoryModel,
1753317559
operand_index: 1,
17534-
required_version: SpirvVersion::new(1, 5),
17535-
target_version: TargetEnv::Vulkan1_1Spirv1_4.spirv_version(),
17560+
required_capability: rspirv::spirv::Capability::VulkanMemoryModel,
1753617561
}
1753717562
);
1753817563
}

0 commit comments

Comments
 (0)