From 065d6546c403b453ad78615b952a72fd9b393ebd Mon Sep 17 00:00:00 2001 From: Robert Bamler Date: Sat, 19 Apr 2025 00:43:12 +0200 Subject: [PATCH] Check for spv language version When checking for capabilities in SPIR-V, `capabilities_available == None` indicates that all capabilities are available. However, some capabilities are not even defined for all language versions, so we still need to check if the requested capabilities even exist in the language version we're using. --- naga/src/back/spv/block.rs | 15 +-- naga/src/back/spv/layout.rs | 17 ++- naga/src/back/spv/writer.rs | 8 +- naga/tests/in/wgsl/functions-optimized.toml | 1 + .../out/spv/wgsl-functions-optimized.spvasm | 2 +- naga/tests/out/spv/wgsl-functions.spvasm | 110 ++++++++++++++---- 6 files changed, 117 insertions(+), 36 deletions(-) diff --git a/naga/src/back/spv/block.rs b/naga/src/back/spv/block.rs index 0799017ba2..96e8471c86 100644 --- a/naga/src/back/spv/block.rs +++ b/naga/src/back/spv/block.rs @@ -1143,13 +1143,14 @@ impl BlockContext<'_> { ), }, fun @ (Mf::Dot4I8Packed | Mf::Dot4U8Packed) => { - if self - .writer - .require_all(&[ - spirv::Capability::DotProduct, - spirv::Capability::DotProductInput4x8BitPacked, - ]) - .is_ok() + if self.writer.lang_version() >= (1, 6) + && self + .writer + .require_all(&[ + spirv::Capability::DotProduct, + spirv::Capability::DotProductInput4x8BitPacked, + ]) + .is_ok() { // Write optimized code using `PackedVectorFormat4x8Bit`. self.writer.use_extension("SPV_KHR_integer_dot_product"); diff --git a/naga/src/back/spv/layout.rs b/naga/src/back/spv/layout.rs index 177996741d..6652b33504 100644 --- a/naga/src/back/spv/layout.rs +++ b/naga/src/back/spv/layout.rs @@ -12,7 +12,8 @@ use alloc::format; const GENERATOR: Word = 28; impl PhysicalLayout { - pub(super) const fn new(version: Word) -> Self { + pub(super) const fn new(major_version: u8, minor_version: u8) -> Self { + let version = ((major_version as u32) << 16) | ((minor_version as u32) << 8); PhysicalLayout { magic_number: MAGIC_NUMBER, version, @@ -29,6 +30,13 @@ impl PhysicalLayout { sink.extend(iter::once(self.bound)); sink.extend(iter::once(self.instruction_schema)); } + + /// Returns `(major, minor)`. + pub(super) const fn lang_version(&self) -> (u8, u8) { + let major = (self.version >> 16) as u8; + let minor = (self.version >> 8) as u8; + (major, minor) + } } impl super::recyclable::Recyclable for PhysicalLayout { @@ -150,10 +158,13 @@ impl Instruction { #[test] fn test_physical_layout_in_words() { let bound = 5; - let version = 0x10203; + + // The least and most significant bytes of `version` must both be zero + // according to the SPIR-V spec. + let version = 0x0001_0200; let mut output = vec![]; - let mut layout = PhysicalLayout::new(version); + let mut layout = PhysicalLayout::new(1, 2); layout.bound = bound; layout.in_words(&mut output); diff --git a/naga/src/back/spv/writer.rs b/naga/src/back/spv/writer.rs index 3b79c91209..3819ed10e7 100644 --- a/naga/src/back/spv/writer.rs +++ b/naga/src/back/spv/writer.rs @@ -60,7 +60,6 @@ impl Writer { if major != 1 { return Err(Error::UnsupportedVersion(major, minor)); } - let raw_version = ((major as u32) << 16) | ((minor as u32) << 8); let mut capabilities_used = crate::FastIndexSet::default(); capabilities_used.insert(spirv::Capability::Shader); @@ -70,7 +69,7 @@ impl Writer { let void_type = id_gen.next(); Ok(Writer { - physical_layout: PhysicalLayout::new(raw_version), + physical_layout: PhysicalLayout::new(major, minor), logical_layout: LogicalLayout::default(), id_gen, capabilities_available: options.capabilities.clone(), @@ -99,6 +98,11 @@ impl Writer { }) } + /// Returns `(major, minor)` of the SPIR-V language version. + pub const fn lang_version(&self) -> (u8, u8) { + self.physical_layout.lang_version() + } + /// Reset `Writer` to its initial state, retaining any allocations. /// /// Why not just implement `Recyclable` for `Writer`? By design, diff --git a/naga/tests/in/wgsl/functions-optimized.toml b/naga/tests/in/wgsl/functions-optimized.toml index 457c80d16e..c8a7abfa09 100644 --- a/naga/tests/in/wgsl/functions-optimized.toml +++ b/naga/tests/in/wgsl/functions-optimized.toml @@ -5,6 +5,7 @@ targets = "SPIRV | HLSL" [spv] capabilities = ["DotProduct", "DotProductInput4x8BitPacked"] +version = [1, 6] [hlsl] shader_model = "V6_4" diff --git a/naga/tests/out/spv/wgsl-functions-optimized.spvasm b/naga/tests/out/spv/wgsl-functions-optimized.spvasm index 5366e0a9fc..7169a1e1e4 100644 --- a/naga/tests/out/spv/wgsl-functions-optimized.spvasm +++ b/naga/tests/out/spv/wgsl-functions-optimized.spvasm @@ -1,5 +1,5 @@ ; SPIR-V -; Version: 1.1 +; Version: 1.6 ; Generator: rspirv ; Bound: 30 OpCapability Shader diff --git a/naga/tests/out/spv/wgsl-functions.spvasm b/naga/tests/out/spv/wgsl-functions.spvasm index 35585e2a6c..8d271c22f2 100644 --- a/naga/tests/out/spv/wgsl-functions.spvasm +++ b/naga/tests/out/spv/wgsl-functions.spvasm @@ -1,15 +1,12 @@ ; SPIR-V ; Version: 1.1 ; Generator: rspirv -; Bound: 95 +; Bound: 162 OpCapability Shader -OpCapability DotProductKHR -OpCapability DotProductInput4x8BitPackedKHR -OpExtension "SPV_KHR_integer_dot_product" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 -OpEntryPoint GLCompute %89 "main" -OpExecutionMode %89 LocalSize 1 1 1 +OpEntryPoint GLCompute %156 "main" +OpExecutionMode %156 LocalSize 1 1 1 %2 = OpTypeVoid %4 = OpTypeFloat 32 %3 = OpTypeVector %4 2 @@ -42,7 +39,10 @@ OpExecutionMode %89 LocalSize 1 1 1 %76 = OpConstant %6 6 %77 = OpConstant %6 7 %78 = OpConstant %6 8 -%90 = OpTypeFunction %2 +%83 = OpConstant %6 0 +%84 = OpConstant %6 16 +%85 = OpConstant %6 24 +%157 = OpTypeFunction %2 %8 = OpFunction %3 None %9 %7 = OpLabel OpBranch %14 @@ -96,22 +96,86 @@ OpFunctionEnd %69 = OpLabel OpBranch %79 %79 = OpLabel -%80 = OpSDotKHR %5 %22 %72 PackedVectorFormat4x8BitKHR -%81 = OpUDotKHR %6 %73 %74 PackedVectorFormat4x8BitKHR -%82 = OpIAdd %6 %75 %81 -%83 = OpIAdd %6 %76 %81 -%84 = OpSDotKHR %5 %82 %83 PackedVectorFormat4x8BitKHR -%85 = OpIAdd %6 %77 %81 -%86 = OpIAdd %6 %78 %81 -%87 = OpUDotKHR %6 %85 %86 PackedVectorFormat4x8BitKHR -OpReturnValue %87 +%81 = OpBitcast %5 %22 +%82 = OpBitcast %5 %72 +%86 = OpBitFieldSExtract %5 %81 %83 %78 +%87 = OpBitFieldSExtract %5 %82 %83 %78 +%88 = OpIMul %5 %86 %87 +%89 = OpIAdd %5 %32 %88 +%90 = OpBitFieldSExtract %5 %81 %78 %78 +%91 = OpBitFieldSExtract %5 %82 %78 %78 +%92 = OpIMul %5 %90 %91 +%93 = OpIAdd %5 %89 %92 +%94 = OpBitFieldSExtract %5 %81 %84 %78 +%95 = OpBitFieldSExtract %5 %82 %84 %78 +%96 = OpIMul %5 %94 %95 +%97 = OpIAdd %5 %93 %96 +%98 = OpBitFieldSExtract %5 %81 %85 %78 +%99 = OpBitFieldSExtract %5 %82 %85 %78 +%100 = OpIMul %5 %98 %99 +%80 = OpIAdd %5 %97 %100 +%102 = OpBitFieldUExtract %6 %73 %83 %78 +%103 = OpBitFieldUExtract %6 %74 %83 %78 +%104 = OpIMul %6 %102 %103 +%105 = OpIAdd %6 %41 %104 +%106 = OpBitFieldUExtract %6 %73 %78 %78 +%107 = OpBitFieldUExtract %6 %74 %78 %78 +%108 = OpIMul %6 %106 %107 +%109 = OpIAdd %6 %105 %108 +%110 = OpBitFieldUExtract %6 %73 %84 %78 +%111 = OpBitFieldUExtract %6 %74 %84 %78 +%112 = OpIMul %6 %110 %111 +%113 = OpIAdd %6 %109 %112 +%114 = OpBitFieldUExtract %6 %73 %85 %78 +%115 = OpBitFieldUExtract %6 %74 %85 %78 +%116 = OpIMul %6 %114 %115 +%101 = OpIAdd %6 %113 %116 +%117 = OpIAdd %6 %75 %101 +%118 = OpIAdd %6 %76 %101 +%120 = OpBitcast %5 %117 +%121 = OpBitcast %5 %118 +%122 = OpBitFieldSExtract %5 %120 %83 %78 +%123 = OpBitFieldSExtract %5 %121 %83 %78 +%124 = OpIMul %5 %122 %123 +%125 = OpIAdd %5 %32 %124 +%126 = OpBitFieldSExtract %5 %120 %78 %78 +%127 = OpBitFieldSExtract %5 %121 %78 %78 +%128 = OpIMul %5 %126 %127 +%129 = OpIAdd %5 %125 %128 +%130 = OpBitFieldSExtract %5 %120 %84 %78 +%131 = OpBitFieldSExtract %5 %121 %84 %78 +%132 = OpIMul %5 %130 %131 +%133 = OpIAdd %5 %129 %132 +%134 = OpBitFieldSExtract %5 %120 %85 %78 +%135 = OpBitFieldSExtract %5 %121 %85 %78 +%136 = OpIMul %5 %134 %135 +%119 = OpIAdd %5 %133 %136 +%137 = OpIAdd %6 %77 %101 +%138 = OpIAdd %6 %78 %101 +%140 = OpBitFieldUExtract %6 %137 %83 %78 +%141 = OpBitFieldUExtract %6 %138 %83 %78 +%142 = OpIMul %6 %140 %141 +%143 = OpIAdd %6 %41 %142 +%144 = OpBitFieldUExtract %6 %137 %78 %78 +%145 = OpBitFieldUExtract %6 %138 %78 %78 +%146 = OpIMul %6 %144 %145 +%147 = OpIAdd %6 %143 %146 +%148 = OpBitFieldUExtract %6 %137 %84 %78 +%149 = OpBitFieldUExtract %6 %138 %84 %78 +%150 = OpIMul %6 %148 %149 +%151 = OpIAdd %6 %147 %150 +%152 = OpBitFieldUExtract %6 %137 %85 %78 +%153 = OpBitFieldUExtract %6 %138 %85 %78 +%154 = OpIMul %6 %152 %153 +%139 = OpIAdd %6 %151 %154 +OpReturnValue %139 OpFunctionEnd -%89 = OpFunction %2 None %90 -%88 = OpLabel -OpBranch %91 -%91 = OpLabel -%92 = OpFunctionCall %3 %8 -%93 = OpFunctionCall %5 %17 -%94 = OpFunctionCall %6 %70 +%156 = OpFunction %2 None %157 +%155 = OpLabel +OpBranch %158 +%158 = OpLabel +%159 = OpFunctionCall %3 %8 +%160 = OpFunctionCall %5 %17 +%161 = OpFunctionCall %6 %70 OpReturn OpFunctionEnd \ No newline at end of file