diff --git a/SPIRV/GlslangToSpv.cpp b/SPIRV/GlslangToSpv.cpp index 9888ce01a4..6dc4d710e9 100644 --- a/SPIRV/GlslangToSpv.cpp +++ b/SPIRV/GlslangToSpv.cpp @@ -1393,6 +1393,10 @@ spv::LoopControlMask TGlslangToSpvTraverser::TranslateLoopControl(const glslang: operands.push_back(loopNode.getPartialCount()); } } + if (loopNode.getMultipleWaitQueuesQCOM() != loopNode./*TIntermLoop::*/noMultipleQaitQueues) { + control = control | spv::LoopControlMask::MultipleWaitQueuesQCOM; + operands.push_back(loopNode.getMultipleWaitQueuesQCOM()); + } return control; } diff --git a/SPIRV/doc.cpp b/SPIRV/doc.cpp index a34a490d35..11f87eaf88 100644 --- a/SPIRV/doc.cpp +++ b/SPIRV/doc.cpp @@ -782,6 +782,7 @@ const char* LoopControlString(int cont) case (int)LoopControlShift::IterationMultiple: return "IterationMultiple"; case (int)LoopControlShift::PeelCount: return "PeelCount"; case (int)LoopControlShift::PartialCount: return "PartialCount"; + case (int)LoopControlShift::MultipleWaitQueuesQCOM: return "MultipeWaitQueuesQCOM"; case LoopControlCeiling: default: return "Bad"; diff --git a/SPIRV/spirv.hpp11 b/SPIRV/spirv.hpp11 index b35139e615..126566b952 100644 --- a/SPIRV/spirv.hpp11 +++ b/SPIRV/spirv.hpp11 @@ -847,6 +847,7 @@ enum class LoopControlShift : unsigned { NoFusionINTEL = 23, LoopCountINTEL = 24, MaxReinvocationDelayINTEL = 25, + MultipleWaitQueuesQCOM = 28, Max = 0x7fffffff, }; @@ -871,6 +872,7 @@ enum class LoopControlMask : unsigned { NoFusionINTEL = 0x00800000, LoopCountINTEL = 0x01000000, MaxReinvocationDelayINTEL = 0x02000000, + MultipleWaitQueuesQCOM = 0x10000000, }; enum class FunctionControlShift : unsigned { @@ -1122,6 +1124,7 @@ enum class Capability : unsigned { TileShadingQCOM = 4495, CooperativeMatrixConversionQCOM = 4496, TextureBlockMatch2QCOM = 4498, + MultipleWaitQueuesQCOM = 4539, Float16ImageAMD = 5008, ImageGatherBiasLodAMD = 5009, FragmentMaskAMD = 5010, @@ -4126,6 +4129,7 @@ inline const char* CapabilityToString(Capability value) { case Capability::TileShadingQCOM: return "TileShadingQCOM"; case Capability::CooperativeMatrixConversionQCOM: return "CooperativeMatrixConversionQCOM"; case Capability::TextureBlockMatch2QCOM: return "TextureBlockMatch2QCOM"; + case Capability::MultipleWaitQueuesQCOM: return "MultipleWaitQueuesQCOM"; case Capability::Float16ImageAMD: return "Float16ImageAMD"; case Capability::ImageGatherBiasLodAMD: return "ImageGatherBiasLodAMD"; case Capability::FragmentMaskAMD: return "FragmentMaskAMD"; diff --git a/Test/baseResults/460.frag.out b/Test/baseResults/460.frag.out index ced3bb842b..9e7bc28528 100644 --- a/Test/baseResults/460.frag.out +++ b/Test/baseResults/460.frag.out @@ -2,6 +2,7 @@ ERROR: 0:22: 'attribute' : required extension not requested: Possible extensions include: GL_EXT_control_flow_attributes GL_EXT_control_flow_attributes2 +GL_QCOM_multiple_wait_queues ERROR: 0:23: 'attribute' : required extension not requested: GL_EXT_control_flow_attributes ERROR: 0:30: 'dependency_length' : must be positive ERROR: 0:31: 'dependency_length' : must be positive diff --git a/Test/baseResults/spv.qcom.multiple_wait_queues.comp.out b/Test/baseResults/spv.qcom.multiple_wait_queues.comp.out new file mode 100644 index 0000000000..297816d981 --- /dev/null +++ b/Test/baseResults/spv.qcom.multiple_wait_queues.comp.out @@ -0,0 +1,216 @@ +spv.qcom.multiple_wait_queues.comp +// Module Version 10300 +// Generated by (magic number): 8000b +// Id's are bound by 115 + + Capability Shader + Capability VulkanMemoryModelKHR + Capability CooperativeMatrixKHR + Extension "SPV_KHR_cooperative_matrix" + Extension "SPV_KHR_vulkan_memory_model" + 1: ExtInstImport "GLSL.std.450" + MemoryModel Logical VulkanKHR + EntryPoint GLCompute 4 "main" + ExecutionMode 4 LocalSize 64 2 2 + Source GLSL 450 + SourceExtension "GL_KHR_cooperative_matrix" + SourceExtension "GL_KHR_memory_scope_semantics" + SourceExtension "GL_QCOM_multiple_wait_queues" + Name 4 "main" + Name 11 "coop_matmul(u1;u1;" + Name 9 "row" + Name 10 "col" + Name 13 "step" + Name 21 "TOTAL_K" + Name 24 "subMatrixAStart" + Name 30 "subMatrixBStart" + Name 37 "TILE_M" + Name 41 "matA" + Name 43 "InputA" + MemberName 43(InputA) 0 "x" + Name 45 "inputA" + Name 52 "TILE_N" + Name 56 "matB" + Name 58 "InputB" + MemberName 58(InputB) 0 "x" + Name 60 "inputB" + Name 68 "matC" + Name 73 "TILE_K" + Name 83 "subMatrixAStart" + Name 88 "subMatrixBStart" + Name 105 "param" + Name 106 "param" + Name 109 "InputC" + MemberName 109(InputC) 0 "x" + Name 111 "inputC" + Decorate 21(TOTAL_K) SpecId 0 + Decorate 37(TILE_M) SpecId 3 + Decorate 42 ArrayStride 4 + Decorate 43(InputA) Block + MemberDecorate 43(InputA) 0 NonWritable + MemberDecorate 43(InputA) 0 Offset 0 + Decorate 45(inputA) NonWritable + Decorate 45(inputA) Binding 0 + Decorate 45(inputA) DescriptorSet 0 + Decorate 52(TILE_N) SpecId 4 + Decorate 57 ArrayStride 4 + Decorate 58(InputB) Block + MemberDecorate 58(InputB) 0 NonWritable + MemberDecorate 58(InputB) 0 Offset 0 + Decorate 60(inputB) NonWritable + Decorate 60(inputB) Binding 1 + Decorate 60(inputB) DescriptorSet 0 + Decorate 73(TILE_K) SpecId 7 + Decorate 108 ArrayStride 4 + Decorate 109(InputC) Block + MemberDecorate 109(InputC) 0 NonWritable + MemberDecorate 109(InputC) 0 Offset 0 + Decorate 111(inputC) NonWritable + Decorate 111(inputC) Binding 2 + Decorate 111(inputC) DescriptorSet 0 + Decorate 114 BuiltIn WorkgroupSize + 2: TypeVoid + 3: TypeFunction 2 + 6: TypeInt 32 0 + 7: TypePointer Function 6(int) + 8: TypeFunction 2 7(ptr) 7(ptr) + 14: 6(int) Constant 0 + 21(TOTAL_K): 6(int) SpecConstant 1 + 22: TypeBool + 26: 6(int) Constant 16 + 35: TypeFloat 32 + 36: 6(int) Constant 3 + 37(TILE_M): 6(int) SpecConstant 1 + 38: 6(int) Constant 8 + 39: TypeCooperativeMatrixKHR 35(float) 36 37(TILE_M) 38 14 + 40: TypePointer Function 39 + 42: TypeRuntimeArray 35(float) + 43(InputA): TypeStruct 42 + 44: TypePointer StorageBuffer 43(InputA) + 45(inputA): 44(ptr) Variable StorageBuffer + 46: TypeInt 32 1 + 47: 46(int) Constant 0 + 49: TypePointer StorageBuffer 35(float) + 52(TILE_N): 6(int) SpecConstant 1 + 53: 6(int) Constant 1 + 54: TypeCooperativeMatrixKHR 35(float) 36 38 52(TILE_N) 53 + 55: TypePointer Function 54 + 57: TypeRuntimeArray 35(float) + 58(InputB): TypeStruct 57 + 59: TypePointer StorageBuffer 58(InputB) + 60(inputB): 59(ptr) Variable StorageBuffer + 63: 46(int) Constant 1 + 65: 6(int) Constant 2 + 66: TypeCooperativeMatrixKHR 35(float) 36 37(TILE_M) 52(TILE_N) 65 + 67: TypePointer Function 66 + 73(TILE_K): 6(int) SpecConstant 1 + 108: TypeRuntimeArray 35(float) + 109(InputC): TypeStruct 108 + 110: TypePointer StorageBuffer 109(InputC) + 111(inputC): 110(ptr) Variable StorageBuffer + 112: TypeVector 6(int) 3 + 113: 6(int) Constant 64 + 114: 112(ivec3) ConstantComposite 113 65 65 + 4(main): 2 Function None 3 + 5: Label + 105(param): 7(ptr) Variable Function + 106(param): 7(ptr) Variable Function + Store 105(param) 14 + Store 106(param) 14 + 107: 2 FunctionCall 11(coop_matmul(u1;u1;) 105(param) 106(param) + Return + FunctionEnd +11(coop_matmul(u1;u1;): 2 Function None 8 + 9(row): 7(ptr) FunctionParameter + 10(col): 7(ptr) FunctionParameter + 12: Label + 13(step): 7(ptr) Variable Function +24(subMatrixAStart): 7(ptr) Variable Function +30(subMatrixBStart): 7(ptr) Variable Function + 41(matA): 40(ptr) Variable Function + 56(matB): 55(ptr) Variable Function + 68(matC): 67(ptr) Variable Function +83(subMatrixAStart): 7(ptr) Variable Function +88(subMatrixBStart): 7(ptr) Variable Function + Store 13(step) 14 + Branch 15 + 15: Label + LoopMerge 17 18 0 + Branch 19 + 19: Label + 20: 6(int) Load 13(step) + 23: 22(bool) ULessThan 20 21(TOTAL_K) + BranchConditional 23 16 17 + 16: Label + 25: 6(int) Load 9(row) + 27: 6(int) IMul 25 26 + 28: 6(int) Load 13(step) + 29: 6(int) IAdd 27 28 + Store 24(subMatrixAStart) 29 + 31: 6(int) Load 10(col) + 32: 6(int) IMul 31 26 + 33: 6(int) Load 13(step) + 34: 6(int) IAdd 32 33 + Store 30(subMatrixBStart) 34 + 48: 6(int) Load 24(subMatrixAStart) + 50: 49(ptr) AccessChain 45(inputA) 47 48 + 51: 39 CooperativeMatrixLoadKHR 50 47 26 None + Store 41(matA) 51 + 61: 6(int) Load 30(subMatrixBStart) + 62: 49(ptr) AccessChain 60(inputB) 47 61 + 64: 54 CooperativeMatrixLoadKHR 62 63 26 None + Store 56(matB) 64 + 69: 39 Load 41(matA) + 70: 54 Load 56(matB) + 71: 66 Load 68(matC) + 72: 66 CooperativeMatrixMulAddKHR 69 70 71 + Store 68(matC) 72 + Branch 18 + 18: Label + 74: 6(int) Load 13(step) + 75: 6(int) IAdd 74 73(TILE_K) + Store 13(step) 75 + Branch 15 + 17: Label + Store 13(step) 14 + Branch 76 + 76: Label + LoopMerge 78 79 2 + Branch 80 + 80: Label + 81: 6(int) Load 13(step) + 82: 22(bool) ULessThan 81 21(TOTAL_K) + BranchConditional 82 77 78 + 77: Label + 84: 6(int) Load 9(row) + 85: 6(int) IMul 84 26 + 86: 6(int) Load 13(step) + 87: 6(int) IAdd 85 86 + Store 83(subMatrixAStart) 87 + 89: 6(int) Load 10(col) + 90: 6(int) IMul 89 26 + 91: 6(int) Load 13(step) + 92: 6(int) IAdd 90 91 + Store 88(subMatrixBStart) 92 + 93: 6(int) Load 83(subMatrixAStart) + 94: 49(ptr) AccessChain 45(inputA) 47 93 + 95: 39 CooperativeMatrixLoadKHR 94 47 26 None + Store 41(matA) 95 + 96: 6(int) Load 88(subMatrixBStart) + 97: 49(ptr) AccessChain 60(inputB) 47 96 + 98: 54 CooperativeMatrixLoadKHR 97 63 26 None + Store 56(matB) 98 + 99: 39 Load 41(matA) + 100: 54 Load 56(matB) + 101: 66 Load 68(matC) + 102: 66 CooperativeMatrixMulAddKHR 99 100 101 + Store 68(matC) 102 + Branch 79 + 79: Label + 103: 6(int) Load 13(step) + 104: 6(int) IAdd 103 73(TILE_K) + Store 13(step) 104 + Branch 76 + 78: Label + Return + FunctionEnd diff --git a/Test/spv.qcom.multiple_wait_queues.comp b/Test/spv.qcom.multiple_wait_queues.comp new file mode 100644 index 0000000000..584274d14d --- /dev/null +++ b/Test/spv.qcom.multiple_wait_queues.comp @@ -0,0 +1,55 @@ +#version 450 core +#extension GL_KHR_memory_scope_semantics : enable + +#extension GL_KHR_cooperative_matrix : enable +#extension GL_QCOM_multiple_wait_queues : enable + +layout(constant_id = 0) const uint TOTAL_K = 1; + +layout(constant_id = 3) const uint TILE_M = 1; +layout(constant_id = 4) const uint TILE_N = 1; +layout(constant_id = 7) const uint TILE_K = 1; + +layout(set=0, binding=0) readonly buffer InputA { float x[]; } inputA; +layout(set=0, binding=1) readonly buffer InputB { float x[]; } inputB; +layout(set=0, binding=2) readonly buffer InputC { float x[]; } inputC; + +layout(local_size_x = 64, local_size_y = 2, local_size_z = 2) in; + +#define STRIDE_A 16 +#define STRIDE_B 16 + +void coop_matmul(uint row, uint col) +{ + coopmat matA; + coopmat matB; + coopmat matC; + uint step; + + [[multiple_wait_queuesQCOM]] + for (step = 0; step < TOTAL_K; step += TILE_K) { + uint subMatrixAStart = row * STRIDE_A + step; + uint subMatrixBStart = col * STRIDE_B + step; + + coopMatLoad(matA, inputA.x, subMatrixAStart, STRIDE_A, gl_CooperativeMatrixLayoutRowMajor); // Global to CoopMat + coopMatLoad(matB, inputB.x, subMatrixBStart, STRIDE_B, gl_CooperativeMatrixLayoutColumnMajor); // Global to CoopMat + + matC = coopMatMulAdd(matA, matB, matC); + } + + [[multiple_wait_queuesQCOM(2)]] + for (step = 0; step < TOTAL_K; step += TILE_K) { + uint subMatrixAStart = row * STRIDE_A + step; + uint subMatrixBStart = col * STRIDE_B + step; + + coopMatLoad(matA, inputA.x, subMatrixAStart, STRIDE_A, gl_CooperativeMatrixLayoutRowMajor); // Global to CoopMat + coopMatLoad(matB, inputB.x, subMatrixBStart, STRIDE_B, gl_CooperativeMatrixLayoutColumnMajor); // Global to CoopMat + + matC = coopMatMulAdd(matA, matB, matC); + } +} + +void main() +{ + coop_matmul(0, 0); +} diff --git a/glslang/Include/intermediate.h b/glslang/Include/intermediate.h index 3c98309c60..c3f0c12798 100644 --- a/glslang/Include/intermediate.h +++ b/glslang/Include/intermediate.h @@ -1223,7 +1223,8 @@ class TIntermLoop : public TIntermNode { maxIterations(iterationsInfinite), iterationMultiple(1), peelCount(0), - partialCount(0) + partialCount(0), + multipleWaitQueuesQCOM(noMultipleQaitQueues) { } virtual TIntermLoop* getAsLoopNode() { return this; } @@ -1275,6 +1276,12 @@ class TIntermLoop : public TIntermNode { } unsigned int getPartialCount() const { return partialCount; } + static const unsigned int noMultipleQaitQueues = 0xFFFFFFFF; + void setMultipleWaitQueuesQCOM(unsigned int numQ) { + multipleWaitQueuesQCOM = numQ; + } + unsigned int getMultipleWaitQueuesQCOM() const { return multipleWaitQueuesQCOM; } + protected: TIntermNode* body; // code to loop over TIntermNode* test; // exit condition associated with loop, could be 0 for 'for' loops @@ -1288,6 +1295,7 @@ class TIntermLoop : public TIntermNode { unsigned int iterationMultiple; // as per the SPIR-V specification unsigned int peelCount; // as per the SPIR-V specification unsigned int partialCount; // as per the SPIR-V specification + unsigned int multipleWaitQueuesQCOM; }; // diff --git a/glslang/MachineIndependent/Versions.cpp b/glslang/MachineIndependent/Versions.cpp index 8f29b9eeee..3efa83f426 100644 --- a/glslang/MachineIndependent/Versions.cpp +++ b/glslang/MachineIndependent/Versions.cpp @@ -334,6 +334,7 @@ void TParseVersions::initializeExtensionBehavior() extensionBehavior[E_GL_QCOM_image_processing2] = EBhDisable; extensionBehavior[E_GL_QCOM_tile_shading] = EBhDisable; extensionBehavior[E_GL_QCOM_cooperative_matrix_conversion] = EBhDisable; + extensionBehavior[E_GL_QCOM_multiple_wait_queues] = EBhDisable; // AEP extensionBehavior[E_GL_ANDROID_extension_pack_es31a] = EBhDisable; @@ -484,6 +485,7 @@ void TParseVersions::getPreamble(std::string& preamble) "#define GL_QCOM_image_processing2 1\n" "#define GL_QCOM_tile_shading 1\n" "#define GL_QCOM_cooperative_matrix_conversion 1\n" + "#define GL_QCOM_multiple_wait_queues 1\n" ; if (version >= 300) { @@ -620,6 +622,7 @@ void TParseVersions::getPreamble(std::string& preamble) "#define GL_QCOM_image_processing2 1\n" "#define GL_QCOM_tile_shading 1\n" "#define GL_QCOM_cooperative_matrix_conversion 1\n" + "#define GL_QCOM_multiple_wait_queues 1\n" "#define GL_EXT_shader_explicit_arithmetic_types 1\n" "#define GL_EXT_shader_explicit_arithmetic_types_int8 1\n" diff --git a/glslang/MachineIndependent/Versions.h b/glslang/MachineIndependent/Versions.h index bccc55ab8a..784d25d999 100644 --- a/glslang/MachineIndependent/Versions.h +++ b/glslang/MachineIndependent/Versions.h @@ -317,6 +317,7 @@ const char* const E_GL_QCOM_image_processing = "GL_QCOM_image const char* const E_GL_QCOM_image_processing2 = "GL_QCOM_image_processing2"; const char* const E_GL_QCOM_tile_shading = "GL_QCOM_tile_shading"; const char* const E_GL_QCOM_cooperative_matrix_conversion = "GL_QCOM_cooperative_matrix_conversion"; +const char* const E_GL_QCOM_multiple_wait_queues = "GL_QCOM_multiple_wait_queues"; // AEP const char* const E_GL_ANDROID_extension_pack_es31a = "GL_ANDROID_extension_pack_es31a"; diff --git a/glslang/MachineIndependent/attribute.cpp b/glslang/MachineIndependent/attribute.cpp index 19e8faac33..5107a3a741 100644 --- a/glslang/MachineIndependent/attribute.cpp +++ b/glslang/MachineIndependent/attribute.cpp @@ -127,6 +127,8 @@ TAttributeType TParseContext::attributeFromName(const TString& name) const return EatExport; else if (name == "maximally_reconverges") return EatMaximallyReconverges; + else if (name == "multiple_wait_queuesQCOM") + return EatMultipleWaitQueuesQCOM; else return EatNone; } @@ -338,6 +340,14 @@ void TParseContext::handleLoopAttributes(const TAttributes& attributes, TIntermN if (unsignedArgument("partial_count", uiValue)) loop->setPartialCount(uiValue); break; + case EatMultipleWaitQueuesQCOM: + value = -1u; + if (it->size() == 0) + loop->setMultipleWaitQueuesQCOM(0); + else + if (positiveSignedArgument("multiple_wait_queuesQCOM", value)) + loop->setMultipleWaitQueuesQCOM(value); + break; default: warn(node->getLoc(), "attribute does not apply to a loop", "", ""); break; diff --git a/glslang/MachineIndependent/attribute.h b/glslang/MachineIndependent/attribute.h index 3b480c6f03..4cf5262d62 100644 --- a/glslang/MachineIndependent/attribute.h +++ b/glslang/MachineIndependent/attribute.h @@ -122,6 +122,7 @@ namespace glslang { EatSubgroupUniformControlFlow, EatExport, EatMaximallyReconverges, + EatMultipleWaitQueuesQCOM, }; class TIntermAggregate; diff --git a/glslang/MachineIndependent/glslang.y b/glslang/MachineIndependent/glslang.y index dc06aaca58..a012f95504 100644 --- a/glslang/MachineIndependent/glslang.y +++ b/glslang/MachineIndependent/glslang.y @@ -119,7 +119,7 @@ extern int yylex(YYSTYPE*, TParseContext&); %parse-param {glslang::TParseContext* pParseContext} %lex-param {parseContext} -%pure-parser // enable thread safety +%define api.pure // enable thread safety %expect 1 // One shift reduce conflict because of if | else %token CONST BOOL INT UINT FLOAT @@ -4158,8 +4158,8 @@ iteration_statement $$ = $1; } | attribute iteration_statement_nonattributed { - const char * extensions[2] = { E_GL_EXT_control_flow_attributes, E_GL_EXT_control_flow_attributes2 }; - parseContext.requireExtensions($2->getLoc(), 2, extensions, "attribute"); + const char * extensions[3] = { E_GL_EXT_control_flow_attributes, E_GL_EXT_control_flow_attributes2, E_GL_QCOM_multiple_wait_queues }; + parseContext.requireExtensions($2->getLoc(), 3, extensions, "attribute"); parseContext.handleLoopAttributes(*$1, $2); $$ = $2; } diff --git a/glslang/MachineIndependent/glslang_tab.cpp b/glslang/MachineIndependent/glslang_tab.cpp index 8b56f4d018..4d09496a35 100644 --- a/glslang/MachineIndependent/glslang_tab.cpp +++ b/glslang/MachineIndependent/glslang_tab.cpp @@ -12187,8 +12187,8 @@ YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); case 638: /* iteration_statement: attribute iteration_statement_nonattributed */ #line 4160 "MachineIndependent/glslang.y" { - const char * extensions[2] = { E_GL_EXT_control_flow_attributes, E_GL_EXT_control_flow_attributes2 }; - parseContext.requireExtensions((yyvsp[0].interm.intermNode)->getLoc(), 2, extensions, "attribute"); + const char * extensions[3] = { E_GL_EXT_control_flow_attributes, E_GL_EXT_control_flow_attributes2, E_GL_QCOM_multiple_wait_queues }; + parseContext.requireExtensions((yyvsp[0].interm.intermNode)->getLoc(), 3, extensions, "attribute"); parseContext.handleLoopAttributes(*(yyvsp[-1].interm.attributes), (yyvsp[0].interm.intermNode)); (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } diff --git a/gtests/Spv.FromFile.cpp b/gtests/Spv.FromFile.cpp index fddd7fce02..3a32dbcf95 100644 --- a/gtests/Spv.FromFile.cpp +++ b/gtests/Spv.FromFile.cpp @@ -747,6 +747,7 @@ INSTANTIATE_TEST_SUITE_P( "spv.atomicAdd.bufferReference.comp", "spv.nontemporalbuffer.frag", "spv.atomicFloat.comp", + "spv.qcom.multiple_wait_queues.comp", })), FileNameAsCustomTestSuffix ); diff --git a/known_good.json b/known_good.json index c8c961cfbe..13f80d17dd 100644 --- a/known_good.json +++ b/known_good.json @@ -5,7 +5,7 @@ "site" : "github", "subrepo" : "KhronosGroup/SPIRV-Tools", "subdir" : "External/spirv-tools", - "commit": "999eb5fa9f853ef3cff65616a67303ec38ce74cb" + "commit": "e3ec000a5b9ee9b18168d3210fab831467d23f43" }, { "name" : "spirv-tools/external/spirv-headers",