diff options
Diffstat (limited to 'source')
| -rw-r--r-- | source/slang/slang-capabilities.capdef | 13 | ||||
| -rw-r--r-- | source/slang/slang-capability.cpp | 38 | ||||
| -rw-r--r-- | source/slang/slang-capability.h | 18 | ||||
| -rw-r--r-- | source/slang/slang-check-shader.cpp | 4 | ||||
| -rwxr-xr-x | source/slang/slang-compiler.h | 2 | ||||
| -rw-r--r-- | source/slang/slang-diagnostic-defs.h | 3 | ||||
| -rw-r--r-- | source/slang/slang-emit-spirv-ops.h | 5 | ||||
| -rw-r--r-- | source/slang/slang-emit-spirv.cpp | 83 | ||||
| -rw-r--r-- | source/slang/slang-ir-glsl-legalize.cpp | 45 | ||||
| -rw-r--r-- | source/slang/slang-ir-inst-defs.h | 5 | ||||
| -rw-r--r-- | source/slang/slang-ir-insts.h | 17 | ||||
| -rw-r--r-- | source/slang/slang-ir-restructure.cpp | 1 | ||||
| -rw-r--r-- | source/slang/slang-ir-spirv-legalize.cpp | 122 | ||||
| -rw-r--r-- | source/slang/slang-ir-spirv-legalize.h | 21 | ||||
| -rw-r--r-- | source/slang/slang-ir-synthesize-active-mask.cpp | 19 | ||||
| -rw-r--r-- | source/slang/slang-ir.cpp | 2 | ||||
| -rw-r--r-- | source/slang/slang-lower-to-ir.cpp | 16 | ||||
| -rw-r--r-- | source/slang/slang-profile-defs.h | 16 | ||||
| -rw-r--r-- | source/slang/slang-spirv-val.cpp | 2 | ||||
| -rw-r--r-- | source/slang/slang.cpp | 50 |
20 files changed, 426 insertions, 56 deletions
diff --git a/source/slang/slang-capabilities.capdef b/source/slang/slang-capabilities.capdef index 7567dd617..6d4e5b3f4 100644 --- a/source/slang/slang-capabilities.capdef +++ b/source/slang/slang-capabilities.capdef @@ -129,6 +129,9 @@ def _GLSL_460 : _GLSL_450; // metal versions def metallib_2_3 : metal; def metallib_2_4 : metallib_2_3; +def metallib_3_0 : metallib_2_4; +def metallib_3_1 : metallib_3_0; +alias metallib_latest = metallib_3_1; // hlsl versions def _sm_4_0 : hlsl; @@ -221,6 +224,7 @@ def SPV_KHR_shader_clock : spirv_1_0; def SPV_NV_shader_image_footprint : spirv_1_0; def SPV_GOOGLE_user_type : spirv_1_0; def SPV_NV_compute_shader_derivatives : spirv_1_0; +def SPV_EXT_demote_to_helper_invocation : spirv_1_4; // SPIRV Capabilities. @@ -252,6 +256,7 @@ def spvRayQueryPositionFetchKHR : SPV_KHR_ray_tracing_position_fetch; def spvShaderInvocationReorderNV : SPV_NV_shader_invocation_reorder; def spvShaderClockKHR : SPV_KHR_shader_clock; def spvShaderNonUniform : spirv_1_5; +def spvDemoteToHelperInvocationEXT : SPV_EXT_demote_to_helper_invocation; // The following capabilities all pertain to how ray tracing shaders are translated // to GLSL, where there are two different extensions that can provide the core @@ -593,6 +598,14 @@ alias DX_6_7 = sm_6_7; alias METAL_2_3 = metallib_2_3; alias METAL_2_4 = metallib_2_4; +alias SPIRV_1_0 = spirv_1_0; +alias SPIRV_1_1 = spirv_1_1; +alias SPIRV_1_2 = spirv_1_2; +alias SPIRV_1_3 = spirv_1_3; +alias SPIRV_1_4 = spirv_1_4; +alias SPIRV_1_5 = spirv_1_5; +alias SPIRV_1_6 = spirv_1_6; + alias sm_2_0_GLSL_140 = _GLSL_140 + sm_4_0 | sm_4_0; alias sm_2_0_GLSL_400 = _GLSL_400 + sm_4_0 | sm_4_0; alias appendstructuredbuffer = sm_5_0 + raytracingstages_compute_fragment; diff --git a/source/slang/slang-capability.cpp b/source/slang/slang-capability.cpp index e77901b5b..c717a0ab9 100644 --- a/source/slang/slang-capability.cpp +++ b/source/slang/slang-capability.cpp @@ -112,6 +112,20 @@ bool isDirectChildOfAbstractAtom(CapabilityAtom name) return _getInfo(name).abstractBase != CapabilityName::Invalid; } +bool isTargetVersionAtom(CapabilityName name) +{ + if (name >= CapabilityName::spirv_1_0 && name <= getLatestSpirvAtom()) + return true; + if (name >= CapabilityName::metallib_2_3 && name <= getLatestMetalAtom()) + return true; + return false; +} + +bool isSpirvExtensionAtom(CapabilityName name) +{ + return UnownedStringSlice(_getInfo(name).name).startsWith("SPV_"); +} + bool lookupCapabilityName(const UnownedStringSlice& str, CapabilityName& value); CapabilityName findCapabilityName(UnownedStringSlice const& name) @@ -122,6 +136,30 @@ CapabilityName findCapabilityName(UnownedStringSlice const& name) return result; } +CapabilityName getLatestSpirvAtom() +{ + static CapabilityName result = CapabilityName::Invalid; + if (result == CapabilityName::Invalid) + { + CapabilitySet latestSpirvCapSet = CapabilitySet(CapabilityName::spirv_latest); + auto latestSpirvCapSetElements = latestSpirvCapSet.getAtomSets()->getElements<CapabilityAtom>(); + result = (CapabilityName)latestSpirvCapSetElements[latestSpirvCapSetElements.getCount() - 2]; //-1 gets shader stage + } + return result; +} + +CapabilityName getLatestMetalAtom() +{ + static CapabilityName result = CapabilityName::Invalid; + if (result == CapabilityName::Invalid) + { + CapabilitySet latestMetalCapSet = CapabilitySet(CapabilityName::metallib_latest); + auto latestMetalCapSetElements = latestMetalCapSet.getAtomSets()->getElements<CapabilityAtom>(); + result = (CapabilityName)latestMetalCapSetElements[latestMetalCapSetElements.getCount() - 2]; //-1 gets shader stage + } + return result; +} + bool isCapabilityDerivedFrom(CapabilityAtom atom, CapabilityAtom base) { if (atom == base) diff --git a/source/slang/slang-capability.h b/source/slang/slang-capability.h index 8bd03147e..5ee79670b 100644 --- a/source/slang/slang-capability.h +++ b/source/slang/slang-capability.h @@ -191,7 +191,7 @@ public: const CapabilityTargetSets* context; CapabilityTargetSets::ConstIterator targetNode{}; CapabilityStageSets::ConstIterator stageNode{}; - const std::optional<CapabilityAtomSet>* atomSetNode; + const std::optional<CapabilityAtomSet>* atomSetNode = {}; public: operator bool() const @@ -258,10 +258,12 @@ public: if (tmp.targetNode == this->context->end()) return tmp; tmp.stageNode = (*tmp.targetNode).second.shaderStageSets.begin(); - if (tmp.stageNode == (*tmp.targetNode).second.shaderStageSets.end()) + while (tmp.stageNode == (*tmp.targetNode).second.shaderStageSets.end()) { - tmp++; - return tmp; + tmp.targetNode++; + if (tmp.targetNode == this->context->end()) + return end(); + tmp.stageNode = (*tmp.targetNode).second.shaderStageSets.begin(); } tmp.atomSetNode = &(*tmp.stageNode).second.atomSet; if (!tmp.atomSetNode->has_value()) @@ -298,6 +300,9 @@ bool isCapabilityDerivedFrom(CapabilityAtom atom, CapabilityAtom base); /// Find a capability atom with the given `name`, or return CapabilityAtom::Invalid. CapabilityName findCapabilityName(UnownedStringSlice const& name); +CapabilityName getLatestSpirvAtom(); +CapabilityName getLatestMetalAtom(); + /// Gets the capability names. void getCapabilityNames(List<UnownedStringSlice>& ioNames); @@ -305,6 +310,11 @@ UnownedStringSlice capabilityNameToString(CapabilityName name); bool isDirectChildOfAbstractAtom(CapabilityAtom name); + + /// Return true if `name` represents an atom for a target version, e.g. spirv_1_5. +bool isTargetVersionAtom(CapabilityName name); +bool isSpirvExtensionAtom(CapabilityName name); + void printDiagnosticArg(StringBuilder& sb, CapabilityAtom atom); void printDiagnosticArg(StringBuilder& sb, CapabilityName name); diff --git a/source/slang/slang-check-shader.cpp b/source/slang/slang-check-shader.cpp index 7a6deed5c..43f8b55e8 100644 --- a/source/slang/slang-check-shader.cpp +++ b/source/slang/slang-check-shader.cpp @@ -526,8 +526,8 @@ namespace Slang // TODO: provedence should have a way to filter out for provenance that are missing X capabilitySet from their caps, else in big functions we get junk errors // This is specifically a problem for when a function is missing a target but otherwise has identical capabilities. - const auto& interredCapConjunctions = entryPointFuncDecl->inferredCapabilityRequirements.getAtomSets(); - const auto& compileCaps = targetCaps.getAtomSets(); + const auto interredCapConjunctions = entryPointFuncDecl->inferredCapabilityRequirements.getAtomSets(); + const auto compileCaps = targetCaps.getAtomSets(); if (compileCaps && interredCapConjunctions) { for (auto inferredAtom : *interredCapConjunctions.begin()) diff --git a/source/slang/slang-compiler.h b/source/slang/slang-compiler.h index ef8bc45d5..fd12a71a3 100755 --- a/source/slang/slang-compiler.h +++ b/source/slang/slang-compiler.h @@ -1739,6 +1739,8 @@ namespace Slang CapabilitySet getTargetCaps(); + void setTargetCaps(CapabilitySet capSet); + HLSLToVulkanLayoutOptions* getHLSLToVulkanLayoutOptions(); private: diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h index 9ba1e4724..7385d5e33 100644 --- a/source/slang/slang-diagnostic-defs.h +++ b/source/slang/slang-diagnostic-defs.h @@ -763,7 +763,8 @@ DIAGNOSTIC(41300, Error, byteAddressBufferUnaligned, "invalid alignment `$0` spe // DIAGNOSTIC(50010, Internal, missingExistentialBindingsForParameter, "missing argument for existential parameter slot") - +DIAGNOSTIC(50011, Warning, spirvVersionNotSupported, "Slang's SPIR-V backend only supports SPIR-V version 1.3 and later." + " Use `-emit-spirv-via-glsl` option to produce SPIR-V 1.0 through 1.2.") DIAGNOSTIC(50020, Error, invalidTessCoordType, "TessCoord must have vec2 or vec3 type.") DIAGNOSTIC(50020, Error, invalidFragCoordType, "FragCoord must be a vec4.") DIAGNOSTIC(50020, Error, invalidInvocationIdType, "InvocationId must have int type.") diff --git a/source/slang/slang-emit-spirv-ops.h b/source/slang/slang-emit-spirv-ops.h index d24153aa5..fa5fc7cd1 100644 --- a/source/slang/slang-emit-spirv-ops.h +++ b/source/slang/slang-emit-spirv-ops.h @@ -2235,6 +2235,11 @@ SpvInst* emitOpKill(SpvInstParent* parent, IRInst* inst) return emitInst(parent, inst, SpvOpKill); } +SpvInst* emitOpDemoteToHelperInvocation(SpvInstParent* parent, IRInst* inst) +{ + return emitInst(parent, inst, SpvOpDemoteToHelperInvocation); +} + // https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#OpReturn SpvInst* emitOpReturn(SpvInstParent* parent, IRInst* inst) { diff --git a/source/slang/slang-emit-spirv.cpp b/source/slang/slang-emit-spirv.cpp index 9ad8513fc..d3d0b16c5 100644 --- a/source/slang/slang-emit-spirv.cpp +++ b/source/slang/slang-emit-spirv.cpp @@ -521,11 +521,7 @@ struct SPIRVEmitContext // > Version nuumber // - - // We are targeting SPIRV 1.5 for now. - - static const uint32_t spvVersion1_5_0 = 0x00010500; - m_words.add(spvVersion1_5_0); + m_words.add(m_spvVersion); // > Generator's magic number. // @@ -1280,6 +1276,27 @@ struct SPIRVEmitContext return result; } + SpvInst* ensureExtensionDeclarationBeforeSpv14(UnownedStringSlice name) + { + if (isSpirv14OrLater()) + return nullptr; + return ensureExtensionDeclaration(name); + } + + SpvInst* ensureExtensionDeclarationBeforeSpv15(UnownedStringSlice name) + { + if (isSpirv15OrLater()) + return nullptr; + return ensureExtensionDeclaration(name); + } + + SpvInst* ensureExtensionDeclarationBeforeSpv16(UnownedStringSlice name) + { + if (isSpirv16OrLater()) + return nullptr; + return ensureExtensionDeclaration(name); + } + bool hasExtensionDeclaration(const UnownedStringSlice& name) { return m_extensionInsts.containsKey(name); @@ -1403,6 +1420,7 @@ struct SPIRVEmitContext auto spvValueType = ensureInst(valueType); valueTypeId = getID(spvValueType); } + auto resultSpvType = emitOpTypePointer( inst, storageClass, @@ -1463,8 +1481,11 @@ struct SPIRVEmitContext if (structSize >= (uint64_t)IRSizeAndAlignment::kIndeterminateSize) { IRBuilder builder(inst); - auto decoration = builder.addDecoration(inst, kIROp_SPIRVBlockDecoration); - emitDecoration(getID(spvStructType), decoration); + if (isSpirv14OrLater() || !inst->findDecorationImpl(kIROp_SPIRVBufferBlockDecoration)) + { + auto decoration = builder.addDecoration(inst, kIROp_SPIRVBlockDecoration); + emitDecoration(getID(spvStructType), decoration); + } } emitLayoutDecorations(as<IRStructType>(inst), getID(spvStructType)); return spvStructType; @@ -2248,7 +2269,7 @@ struct SPIRVEmitContext emitOpDecorate(getSection(SpvLogicalSectionID::Annotations), decor, varInst, SpvDecorationPerPrimitiveEXT); break; case kIROp_RequireSPIRVDescriptorIndexingExtensionDecoration: - ensureExtensionDeclaration(UnownedStringSlice("SPV_EXT_descriptor_indexing")); + ensureExtensionDeclarationBeforeSpv15(UnownedStringSlice("SPV_EXT_descriptor_indexing")); requireSPIRVCapability(SpvCapabilityRuntimeDescriptorArray); break; } @@ -2540,6 +2561,12 @@ struct SPIRVEmitContext emitLocalInst(spvBlock, irInst); if (irInst->getOp() == kIROp_loop) pendingLoopInsts.add(as<IRLoop>(irInst)); + if (irInst->getOp() == kIROp_discard && !shouldEmitDiscardAsDemote()) + { + // If we emitted OpKill for discard, we should stop emitting anything + // after this inst in the block, because OpKill is a terminator inst. + break; + } } } @@ -2586,6 +2613,11 @@ struct SPIRVEmitContext return false; } + bool shouldEmitDiscardAsDemote() + { + return (isSpirv16OrLater() || m_useDemoteToHelperInvocationExtension); + } + // The instructions that appear inside the basic blocks of // functions are what we will call "local" instructions. // @@ -2785,7 +2817,16 @@ struct SPIRVEmitContext result = emitOpReturnValue(parent, inst, as<IRReturn>(inst)->getVal()); break; case kIROp_discard: - result = emitOpKill(parent, inst); + if (shouldEmitDiscardAsDemote()) + { + ensureExtensionDeclarationBeforeSpv16(toSlice("SPV_EXT_demote_to_helper_invocation")); + requireSPIRVCapability(SpvCapabilityDemoteToHelperInvocation); + result = emitOpDemoteToHelperInvocation(parent, inst); + } + else + { + result = emitOpKill(parent, inst); + } break; case kIROp_BeginFragmentShaderInterlock: ensureExtensionDeclaration(UnownedStringSlice("SPV_EXT_fragment_shader_interlock")); @@ -3224,6 +3265,17 @@ struct SPIRVEmitContext auto refSet = m_referencingEntryPoints.tryGetValue(globalInst); if (refSet && refSet->contains(entryPoint)) { + if (!isSpirv14OrLater()) + { + // Prior to SPIRV 1.4, we can only reference In and Out variables + // in the interface part. + if (auto ptrType = as<IRPtrTypeBase>(globalInst->getDataType())) + { + auto addrSpace = ptrType->getAddressSpace(); + if (addrSpace != SpvStorageClassInput && addrSpace != SpvStorageClassOutput) + continue; + } + } paramsSet.add(spvGlobalInst); params.add(spvGlobalInst); referencedBuiltinIRVars.add(globalInst); @@ -3406,6 +3458,8 @@ struct SPIRVEmitContext case kIROp_SPIRVNonUniformResourceDecoration: { + ensureExtensionDeclarationBeforeSpv15(toSlice("SPV_EXT_descriptor_indexing")); + requireSPIRVCapability(SpvCapabilityShaderNonUniform); emitOpDecorate( getSection(SpvLogicalSectionID::Annotations), @@ -3548,6 +3602,7 @@ struct SPIRVEmitContext break; case kIROp_SemanticDecoration: { + ensureExtensionDeclarationBeforeSpv14(toSlice("SPV_GOOGLE_hlsl_functionality1")); emitOpDecorateString(getSection(SpvLogicalSectionID::Annotations), decoration, dstID, @@ -3558,6 +3613,7 @@ struct SPIRVEmitContext case kIROp_UserTypeNameDecoration: { ensureExtensionDeclaration(toSlice("SPV_GOOGLE_user_type")); + ensureExtensionDeclarationBeforeSpv14(toSlice("SPV_GOOGLE_hlsl_functionality1")); emitOpDecorateString(getSection(SpvLogicalSectionID::Annotations), decoration, dstID, @@ -3676,6 +3732,7 @@ struct SPIRVEmitContext { if (shouldEmitSPIRVReflectionInfo()) { + ensureExtensionDeclarationBeforeSpv14(toSlice("SPV_GOOGLE_hlsl_functionality1")); emitOpMemberDecorateString( getSection(SpvLogicalSectionID::Annotations), nullptr, @@ -3980,7 +4037,10 @@ struct SPIRVEmitContext } else if (semanticName == "sv_rendertargetarrayindex") { - requireSPIRVCapability(SpvCapabilityShaderLayer); + if (isSpirv14OrLater()) + requireSPIRVCapability(SpvCapabilityShaderLayer); + else + requireSPIRVCapability(SpvCapabilityGeometry); return getBuiltinGlobalVar(inst->getFullType(), SpvBuiltInLayer); } else if (semanticName == "sv_sampleindex") @@ -4840,11 +4900,12 @@ struct SPIRVEmitContext { //"%addr = OpAccessChain resultType*StorageBuffer resultId _0 const(int, 0) _1;" IRBuilder builder(inst); + auto storageClass = isSpirv14OrLater()? SpvStorageClassStorageBuffer : SpvStorageClassUniform; return emitOpAccessChain( parent, inst, // Make sure the resulting pointer has the correct storage class - getPtrTypeWithAddressSpace(cast<IRPtrTypeBase>(inst->getDataType()), SpvStorageClassStorageBuffer), + getPtrTypeWithAddressSpace(cast<IRPtrTypeBase>(inst->getDataType()), storageClass), inst->getOperand(0), makeArray(emitIntConstant(0, builder.getIntType()), ensureInst(inst->getOperand(1))) ); diff --git a/source/slang/slang-ir-glsl-legalize.cpp b/source/slang/slang-ir-glsl-legalize.cpp index 7a970589e..eb5905dfe 100644 --- a/source/slang/slang-ir-glsl-legalize.cpp +++ b/source/slang/slang-ir-glsl-legalize.cpp @@ -808,6 +808,7 @@ GLSLSystemValueInfo* getGLSLSystemValueInfo( default: context->requireGLSLVersion(ProfileVersion::GLSL_450); + context->requireSPIRVVersion(SemanticVersion(1, 5, 0)); context->requireGLSLExtension(UnownedStringSlice::fromLiteral("GL_ARB_shader_viewport_layer_array")); break; } @@ -2945,6 +2946,23 @@ void legalizeEntryPointForGLSL( // and as an ordinary function. SLANG_ASSERT(!func->firstUse); + // Require SPIRV version based on the stage. + switch (stage) + { + case Stage::Mesh: + case Stage::Amplification: + glslExtensionTracker->requireSPIRVVersion(SemanticVersion(1, 4, 0)); + break; + case Stage::AnyHit: + case Stage::Callable: + case Stage::Miss: + case Stage::RayGeneration: + case Stage::Intersection: + case Stage::ClosestHit: + glslExtensionTracker->requireSPIRVVersion(SemanticVersion(1, 4, 0)); + break; + } + // We create a dummy IR builder, since some of // the functions require it. // @@ -3137,6 +3155,31 @@ void legalizeEntryPointForGLSL( } } +void decorateModuleWithSPIRVVersion(IRModule* module, SemanticVersion spirvVersion) +{ + CapabilityName atom = CapabilityName::spirv_1_0; + switch (spirvVersion.m_major) + { + case 1: + { + switch (spirvVersion.m_minor) + { + case 0: atom = CapabilityName::spirv_1_0; break; + case 1: atom = CapabilityName::spirv_1_1; break; + case 2: atom = CapabilityName::spirv_1_2; break; + case 3: atom = CapabilityName::spirv_1_3; break; + case 4: atom = CapabilityName::spirv_1_4; break; + case 5: atom = CapabilityName::spirv_1_5; break; + case 6: atom = CapabilityName::spirv_1_6; break; + default: SLANG_UNEXPECTED("Unknown SPIRV version"); + } + break; + } + } + IRBuilder builder(module); + builder.addRequireCapabilityAtomDecoration(module->getModuleInst(), atom); +} + void legalizeEntryPointsForGLSL( Session* session, IRModule* module, @@ -3150,6 +3193,8 @@ void legalizeEntryPointsForGLSL( } assignRayPayloadHitObjectAttributeLocations(module); + + decorateModuleWithSPIRVVersion(module, glslExtensionTracker->getSPIRVVersion()); } void legalizeConstantBufferLoadForGLSL(IRModule* module) diff --git a/source/slang/slang-ir-inst-defs.h b/source/slang/slang-ir-inst-defs.h index b7873b8bd..9ab8e9a02 100644 --- a/source/slang/slang-ir-inst-defs.h +++ b/source/slang/slang-ir-inst-defs.h @@ -585,8 +585,6 @@ INST(TargetSwitch, targetSwitch, 1, 0) // A generic asm inst has an return semantics that terminates the control flow. INST(GenericAsm, GenericAsm, 1, 0) -INST(discard, discard, 0, 0) - /* IRUnreachable */ INST(MissingReturn, missingReturn, 0, 0) INST(Unreachable, unreachable, 0, 0) @@ -594,6 +592,8 @@ INST_RANGE(Unreachable, MissingReturn, Unreachable) INST_RANGE(TerminatorInst, Return, Unreachable) +INST(discard, discard, 0, 0) + INST(RequirePrelude, RequirePrelude, 1, 0) INST(RequireGLSLExtension, RequireGLSLExtension, 1, 0) INST(RequireComputeDerivative, RequireComputeDerivative, 0, 0) @@ -735,6 +735,7 @@ INST_RANGE(BindingQuery, GetRegisterIndex, GetRegisterSpace) INST(RequireGLSLVersionDecoration, requireGLSLVersion, 1, 0) INST(RequireGLSLExtensionDecoration, requireGLSLExtension, 1, 0) INST(RequireCUDASMVersionDecoration, requireCUDASMVersion, 1, 0) + INST(RequireCapabilityAtomDecoration, requireCapabilityAtom, 1, 0) INST(HasExplicitHLSLBindingDecoration, HasExplicitHLSLBinding, 0, 0) diff --git a/source/slang/slang-ir-insts.h b/source/slang/slang-ir-insts.h index 8d1880366..93f3e1227 100644 --- a/source/slang/slang-ir-insts.h +++ b/source/slang/slang-ir-insts.h @@ -351,6 +351,18 @@ struct IRRequireSPIRVVersionDecoration : IRDecoration } }; +struct IRRequireCapabilityAtomDecoration : IRDecoration +{ + enum { kOp = kIROp_RequireCapabilityAtomDecoration }; + IR_LEAF_ISA(RequireCapabilityAtomDecoration) + + IRConstant* getCapabilityAtomOperand() { return cast<IRConstant>(getOperand(0)); } + CapabilityName getAtom() + { + return (CapabilityName)getCapabilityAtomOperand()->value.intVal; + } +}; + struct IRRequireCUDASMVersionDecoration : IRDecoration { enum { kOp = kIROp_RequireCUDASMVersionDecoration }; @@ -4529,6 +4541,11 @@ public: addDecoration(value, kIROp_RequireCUDASMVersionDecoration, getIntValue(getBasicType(BaseType::UInt64), intValue)); } + void addRequireCapabilityAtomDecoration(IRInst* value, CapabilityName atom) + { + addDecoration(value, kIROp_RequireCapabilityAtomDecoration, getIntValue(getUIntType(), IRIntegerValue(atom))); + } + void addPatchConstantFuncDecoration(IRInst* value, IRInst* patchConstantFunc) { addDecoration(value, kIROp_PatchConstantFuncDecoration, patchConstantFunc); diff --git a/source/slang/slang-ir-restructure.cpp b/source/slang/slang-ir-restructure.cpp index cfd1d4597..68be41fdc 100644 --- a/source/slang/slang-ir-restructure.cpp +++ b/source/slang/slang-ir-restructure.cpp @@ -255,7 +255,6 @@ namespace Slang case kIROp_Unreachable: case kIROp_MissingReturn: case kIROp_Return: - case kIROp_discard: case kIROp_GenericAsm: // These cases are all simple terminators that can be handled as-is // without needing to construct a separate `Region` to encapsulate them. diff --git a/source/slang/slang-ir-spirv-legalize.cpp b/source/slang/slang-ir-spirv-legalize.cpp index c023537b9..0965377c8 100644 --- a/source/slang/slang-ir-spirv-legalize.cpp +++ b/source/slang/slang-ir-spirv-legalize.cpp @@ -136,7 +136,10 @@ struct SPIRVLegalizationContext : public SourceEmitterBase break; } builder.addNameHintDecoration(structType, nameSb.getUnownedSlice()); - builder.addDecoration(structType, kIROp_SPIRVBlockDecoration); + if (m_sharedContext->isSpirv14OrLater()) + builder.addDecoration(structType, kIROp_SPIRVBlockDecoration); + else + builder.addDecoration(structType, kIROp_SPIRVBufferBlockDecoration); result.structType = structType; result.arrayKey = arrayKey; @@ -628,7 +631,7 @@ struct SPIRVLegalizationContext : public SourceEmitterBase else if (auto structuredBufferType = as<IRHLSLStructuredBufferTypeBase>(innerType)) { innerType = lowerStructuredBufferType(structuredBufferType).structType; - storageClass = SpvStorageClassStorageBuffer; + storageClass = getStorageBufferStorageClass(); needLoad = false; auto memoryFlags = MemoryQualifierSetModifier::Flags::kNone; @@ -645,8 +648,16 @@ struct SPIRVLegalizationContext : public SourceEmitterBase else if (auto glslShaderStorageBufferType = as<IRGLSLShaderStorageBufferType>(innerType)) { innerType = glslShaderStorageBufferType->getElementType(); - builder.addDecoration(innerType, kIROp_SPIRVBlockDecoration); - storageClass = SpvStorageClassStorageBuffer; + if (m_sharedContext->isSpirv14OrLater()) + { + builder.addDecoration(innerType, kIROp_SPIRVBlockDecoration); + storageClass = SpvStorageClassStorageBuffer; + } + else + { + builder.addDecoration(innerType, kIROp_SPIRVBufferBlockDecoration); + storageClass = SpvStorageClassUniform; + } needLoad = false; } @@ -711,7 +722,7 @@ struct SPIRVLegalizationContext : public SourceEmitterBase break; case LayoutResourceKind::ShaderResource: case LayoutResourceKind::UnorderedAccess: - storageClass = SpvStorageClassStorageBuffer; + storageClass = getStorageBufferStorageClass(); break; case LayoutResourceKind::PushConstantBuffer: storageClass = SpvStorageClassPushConstant; @@ -1144,6 +1155,11 @@ struct SPIRVLegalizationContext : public SourceEmitterBase } } + SpvStorageClass getStorageBufferStorageClass() + { + return m_sharedContext->isSpirv14OrLater() ? SpvStorageClassStorageBuffer : SpvStorageClassUniform; + } + void processStructuredBufferLoad(IRInst* loadInst) { auto sb = loadInst->getOperand(0); @@ -1152,7 +1168,7 @@ struct SPIRVLegalizationContext : public SourceEmitterBase builder.setInsertBefore(loadInst); IRInst* args[] = { sb, index }; auto addrInst = builder.emitIntrinsicInst( - builder.getPtrType(kIROp_PtrType, translateToStorageBufferPointer(loadInst->getFullType()), SpvStorageClassStorageBuffer), + builder.getPtrType(kIROp_PtrType, translateToStorageBufferPointer(loadInst->getFullType()), getStorageBufferStorageClass()), kIROp_RWStructuredBufferGetElementPtr, 2, args); @@ -1171,7 +1187,7 @@ struct SPIRVLegalizationContext : public SourceEmitterBase builder.setInsertBefore(storeInst); IRInst* args[] = { sb, index }; auto addrInst = builder.emitIntrinsicInst( - builder.getPtrType(kIROp_PtrType, value->getFullType(), SpvStorageClassStorageBuffer), + builder.getPtrType(kIROp_PtrType, value->getFullType(), getStorageBufferStorageClass()), kIROp_RWStructuredBufferGetElementPtr, 2, args); @@ -2078,8 +2094,95 @@ struct SPIRVLegalizationContext : public SourceEmitterBase builder.setInsertBefore(beforeInst); } + void determineSpirvVersion() + { + // Determine minimum spirv version from target request. + auto targetCaps = m_sharedContext->m_targetProgram->getTargetReq()->getTargetCaps(); + for (auto targetAtomSet : targetCaps.getAtomSets()) + { + for (auto atom : targetAtomSet) + { + auto spirvAtom = ((CapabilityName)atom); + switch (spirvAtom) + { + case CapabilityName::spirv_1_0: + m_sharedContext->requireSpirvVersion(0x10000); + break; + case CapabilityName::spirv_1_1: + m_sharedContext->requireSpirvVersion(0x10100); + break; + case CapabilityName::spirv_1_2: + m_sharedContext->requireSpirvVersion(0x10200); + break; + case CapabilityName::spirv_1_3: + m_sharedContext->requireSpirvVersion(0x10300); + break; + case CapabilityName::spirv_1_4: + m_sharedContext->requireSpirvVersion(0x10400); + break; + case CapabilityName::spirv_1_5: + m_sharedContext->requireSpirvVersion(0x10500); + break; + case CapabilityName::spirv_1_6: + m_sharedContext->requireSpirvVersion(0x10600); + break; + case CapabilityName::SPV_EXT_demote_to_helper_invocation: + m_sharedContext->m_useDemoteToHelperInvocationExtension = true; + break; + default: + break; + } + } + } + // Scan through the entry points and find the max version required. + auto processInst = [&](IRInst* globalInst) + { + for (auto decor : globalInst->getDecorations()) + { + switch (decor->getOp()) + { + case kIROp_RequireCapabilityAtomDecoration: + { + auto atomDecor = as<IRRequireCapabilityAtomDecoration>(decor); + switch (atomDecor->getAtom()) + { + case CapabilityName::spirv_1_3: + m_sharedContext->requireSpirvVersion(0X10300); + break; + case CapabilityName::spirv_1_4: + m_sharedContext->requireSpirvVersion(0X10400); + break; + case CapabilityName::spirv_1_5: + m_sharedContext->requireSpirvVersion(0X10500); + break; + case CapabilityName::spirv_1_6: + m_sharedContext->requireSpirvVersion(0X10600); + break; + } + break; + } + } + } + }; + + processInst(m_module->getModuleInst()); + for (auto globalInst : m_module->getGlobalInsts()) + { + processInst(globalInst); + } + + if (m_sharedContext->m_spvVersion < 0x10300) + { + // Direct SPIRV backend does not support generating SPIRV before 1.3, + // we will issue an error message here. + m_sharedContext->m_sink->diagnose(SourceLoc(), Diagnostics::spirvVersionNotSupported); + } + } + void processModule() { + determineSpirvVersion(); + // Process global params before anything else, so we don't generate inefficient // array marhalling code for array-typed global params. for (auto globalInst : m_module->getGlobalInsts()) @@ -2118,7 +2221,7 @@ struct SPIRVLegalizationContext : public SourceEmitterBase auto lowered = lowerStructuredBufferType(t); IRBuilder builder(t); builder.setInsertBefore(t); - t->replaceUsesWith(builder.getPtrType(kIROp_PtrType, lowered.structType, SpvStorageClassStorageBuffer)); + t->replaceUsesWith(builder.getPtrType(kIROp_PtrType, lowered.structType, getStorageBufferStorageClass())); } for (auto t : textureFootprintTypes) { @@ -2374,7 +2477,8 @@ void insertFragmentShaderInterlock(SPIRVEmitSharedContext* context, IRModule* mo { if (auto inst = block->getTerminator()) { - if (inst->getOp() == kIROp_Return || inst->getOp() == kIROp_discard) + if (inst->getOp() == kIROp_Return || + !context->isSpirv16OrLater() && inst->getOp() == kIROp_discard) { builder.setInsertBefore(inst); builder.emitEndFragmentShaderInterlock(); diff --git a/source/slang/slang-ir-spirv-legalize.h b/source/slang/slang-ir-spirv-legalize.h index 431c9627d..2aa55bb12 100644 --- a/source/slang/slang-ir-spirv-legalize.h +++ b/source/slang/slang-ir-spirv-legalize.h @@ -26,6 +26,27 @@ struct SPIRVEmitSharedContext const SPIRVCoreGrammarInfo* m_grammarInfo; IRInst* m_voidType; + unsigned int m_spvVersion = 0x10000; + bool m_useDemoteToHelperInvocationExtension = false; + + bool isSpirv14OrLater() + { + return m_spvVersion >= 0x10400; + } + bool isSpirv15OrLater() + { + return m_spvVersion >= 0x10500; + } + bool isSpirv16OrLater() + { + return m_spvVersion >= 0x10600; + } + + void requireSpirvVersion(unsigned int version) + { + m_spvVersion = Math::Max(m_spvVersion, version); + } + SPIRVEmitSharedContext(IRModule* module, TargetProgram* program, DiagnosticSink* sink) : m_irModule(module), m_targetProgram(program), diff --git a/source/slang/slang-ir-synthesize-active-mask.cpp b/source/slang/slang-ir-synthesize-active-mask.cpp index 60e13b418..c3d772615 100644 --- a/source/slang/slang-ir-synthesize-active-mask.cpp +++ b/source/slang/slang-ir-synthesize-active-mask.cpp @@ -1047,25 +1047,6 @@ struct SynthesizeActiveMaskForFunctionContext // case kIROp_conditionalBranch: // - // A `discard` instruction can only appear in fragment shaders, - // and this pass is currently only being applied for the CUDA - // target (which doesn't support rasterization stages). - // - // The difficulty with `discard` is that a `discard` operation - // in a subroutine can affect the active mask seen by code - // after a call to that subroutine. Thus, the `discard` operation - // would violate our assumption that the active mask cannot change - // inside of a single basic block. - // - // Devising a solution to synthesize the active mask in the presence - // of `discard` would seem to require a different approach to this - // pass *or* some new constraints on the front-end representation - // (e.g., a `discard` is really more akin to a `throw` or other - // error-propagation operation, and might need to be explicit in - // the signature of any function that may `discard`). - // - case kIROp_discard: - // // Finally, we also don't handle any control-flow op we might not // have introduced at the time this pass was created. // diff --git a/source/slang/slang-ir.cpp b/source/slang/slang-ir.cpp index cc095a0cb..3e96d61eb 100644 --- a/source/slang/slang-ir.cpp +++ b/source/slang/slang-ir.cpp @@ -558,7 +558,6 @@ namespace Slang case kIROp_Return: case kIROp_Unreachable: case kIROp_MissingReturn: - case kIROp_discard: case kIROp_GenericAsm: break; @@ -854,7 +853,6 @@ namespace Slang case kIROp_conditionalBranch: case kIROp_loop: case kIROp_ifElse: - case kIROp_discard: case kIROp_Switch: case kIROp_Unreachable: case kIROp_MissingReturn: diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp index 80e73fe2b..403e2a921 100644 --- a/source/slang/slang-lower-to-ir.cpp +++ b/source/slang/slang-lower-to-ir.cpp @@ -11599,6 +11599,9 @@ RefPtr<IRModule> TargetProgram::createIRModuleForLayout(DiagnosticSink* sink) builder->addLayoutDecoration(irModule->getModuleInst(), irGlobalScopeVarLayout); + auto latestSpirvAtom = getLatestSpirvAtom(); + auto latestMetalAtom = getLatestMetalAtom(); + for( auto entryPointLayout : programLayout->entryPoints ) { auto funcDeclRef = entryPointLayout->entryPoint; @@ -11617,6 +11620,19 @@ RefPtr<IRModule> TargetProgram::createIRModuleForLayout(DiagnosticSink* sink) builder->addImportDecoration(irFunc, getMangledName(astBuilder, funcDeclRef).getUnownedSlice()); } + for (auto atomSet : as<FuncDecl>(funcDeclRef.getDecl())->inferredCapabilityRequirements.getAtomSets()) + { + for (auto atomVal : atomSet) + { + auto atom = (CapabilityName)atomVal; + if (atom >= CapabilityName::spirv_1_0 && atom <= latestSpirvAtom || + atom >= CapabilityName::metallib_2_3 && atom <= latestMetalAtom) + { + builder->addRequireCapabilityAtomDecoration(irFunc, atom); + } + } + } + auto irEntryPointLayout = lowerEntryPointLayout(context, entryPointLayout); builder->addLayoutDecoration(irFunc, irEntryPointLayout); diff --git a/source/slang/slang-profile-defs.h b/source/slang/slang-profile-defs.h index 826555d1b..3b9ee27f5 100644 --- a/source/slang/slang-profile-defs.h +++ b/source/slang/slang-profile-defs.h @@ -87,6 +87,7 @@ PROFILE_STAGE_ALIAS(Fragment, fragment, Pixel) PROFILE_FAMILY(DX) PROFILE_FAMILY(GLSL) PROFILE_FAMILY(METAL) +PROFILE_FAMILY(SPIRV) // Profile versions PROFILE_VERSION(DX_4_0, DX) @@ -115,6 +116,13 @@ PROFILE_VERSION(GLSL_460, GLSL) PROFILE_VERSION(METAL_2_3, METAL) PROFILE_VERSION(METAL_2_4, METAL) +PROFILE_VERSION(SPIRV_1_0, SPIRV) +PROFILE_VERSION(SPIRV_1_1, SPIRV) +PROFILE_VERSION(SPIRV_1_2, SPIRV) +PROFILE_VERSION(SPIRV_1_3, SPIRV) +PROFILE_VERSION(SPIRV_1_4, SPIRV) +PROFILE_VERSION(SPIRV_1_5, SPIRV) +PROFILE_VERSION(SPIRV_1_6, SPIRV) // Specific profiles PROFILE(DX_Compute_4_0, cs_4_0, Compute, DX_4_0) @@ -236,6 +244,14 @@ PROFILE_ALIAS(DX_None_6_7, DX_Lib_6_7, sm_6_7) PROFILE(METAL_LIB_2_3, metallib_2_3, Unknown, METAL_2_3) PROFILE(METAL_LIB_2_4, metallib_2_4, Unknown, METAL_2_4) +PROFILE(SPIRV_LIB_1_0, spirv_1_0, Unknown, SPIRV_1_0) +PROFILE(SPIRV_LIB_1_1, spirv_1_1, Unknown, SPIRV_1_1) +PROFILE(SPIRV_LIB_1_2, spirv_1_2, Unknown, SPIRV_1_2) +PROFILE(SPIRV_LIB_1_3, spirv_1_3, Unknown, SPIRV_1_3) +PROFILE(SPIRV_LIB_1_4, spirv_1_4, Unknown, SPIRV_1_4) +PROFILE(SPIRV_LIB_1_5, spirv_1_5, Unknown, SPIRV_1_5) +PROFILE(SPIRV_LIB_1_6, spirv_1_6, Unknown, SPIRV_1_6) + // Define all the GLSL profiles PROFILE(GLSL_None_150, glsl_150, Unknown, GLSL_150) diff --git a/source/slang/slang-spirv-val.cpp b/source/slang/slang-spirv-val.cpp index 8b638da94..faacb9c59 100644 --- a/source/slang/slang-spirv-val.cpp +++ b/source/slang/slang-spirv-val.cpp @@ -44,7 +44,7 @@ SlangResult debugValidateSPIRV(const List<uint8_t>& spirv) CommandLine commandLine; commandLine.m_executableLocation.setName("spirv-val"); commandLine.addArg("--target-env"); - commandLine.addArg("vulkan1.2"); + commandLine.addArg("vulkan1.3"); commandLine.addArg("--scalar-block-layout"); RefPtr<Process> p; const auto createResult = Process::create(commandLine, 0, p); diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp index 8b0012f1d..66e75a282 100644 --- a/source/slang/slang.cpp +++ b/source/slang/slang.cpp @@ -1666,6 +1666,12 @@ HLSLToVulkanLayoutOptions* TargetRequest::getHLSLToVulkanLayoutOptions() return hlslToVulkanOptions.get(); } +void TargetRequest::setTargetCaps(CapabilitySet capSet) +{ + cookedCapabilities = capSet; + +} + CapabilitySet TargetRequest::getTargetCaps() { if(!cookedCapabilities.isEmpty()) @@ -1691,6 +1697,11 @@ CapabilitySet TargetRequest::getTargetCaps() // are available where can be directly encoded on the declarations. List<CapabilityName> atoms; + + // If the user specified a explicit profile, we should pull + // a corresponding atom representing the target version from the profile. + CapabilitySet profileCaps = CapabilitySet(optionSet.getProfile().getCapabilityName()); + bool isGLSLTarget = false; switch(getTarget()) { @@ -1704,7 +1715,40 @@ CapabilitySet TargetRequest::getTargetCaps() case CodeGenTarget::SPIRVAssembly: if (getOptionSet().shouldEmitSPIRVDirectly()) { - atoms.add(CapabilityName::spirv_1_5); + // Default to SPIRV 1.5 if the user has not specified a target version. + bool hasTargetVersionAtom = false; + if (!profileCaps.isEmpty()) + { + profileCaps.join(CapabilitySet(CapabilityName::spirv_1_0)); + for (auto profileCapAtomSet : profileCaps.getAtomSets()) + { + for (auto atom : profileCapAtomSet) + { + if (isTargetVersionAtom((CapabilityName)atom)) + { + atoms.add((CapabilityName)atom); + hasTargetVersionAtom = true; + } + } + } + } + if (!hasTargetVersionAtom) + { + atoms.add(CapabilityName::spirv_1_5); + } + // If the user specified any SPIR-V extensions in the profile, + // pull them in. + for (auto profileCapAtomSet : profileCaps.getAtomSets()) + { + for (auto atom : profileCapAtomSet) + { + if (isSpirvExtensionAtom((CapabilityName)atom)) + { + atoms.add((CapabilityName)atom); + hasTargetVersionAtom = true; + } + } + } } else { @@ -1752,9 +1796,7 @@ CapabilitySet TargetRequest::getTargetCaps() CapabilitySet targetCap = CapabilitySet(atoms); - CapabilitySet latestSpirvCapSet = CapabilitySet(CapabilityName::spirv_latest); - auto latestSpirvCapSetElements = latestSpirvCapSet.getAtomSets()->getElements<CapabilityAtom>(); - CapabilityName latestSpirvAtom = (CapabilityName)latestSpirvCapSetElements[latestSpirvCapSetElements.getCount()-2]; //-1 gets shader stage + CapabilityName latestSpirvAtom = getLatestSpirvAtom(); for (auto atomVal : optionSet.getArray(CompilerOptionName::Capability)) { |
