diff options
| author | jsmall-nvidia <jsmall@nvidia.com> | 2023-07-19 18:35:37 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-07-19 15:35:37 -0700 |
| commit | a5987aad211d2e0b9391bdda4b67873ec9873074 (patch) | |
| tree | dea1074aa3382c9c047d0102e41a82187426bcf8 /source/slang | |
| parent | 1cfb1c85b52e00cde2d21874a88cda2c22d18b62 (diff) | |
Support for vk-shift-* without explicit bindings (#3000)
* Improvements to HLSLToVulkanLayoutOptions.
* WIP vk-shift-* with HLSL like binding.
Detecting clashes.
* Shift example seems to be working correctly.
One oddness is that "used" data is now reflected, as we only enable for D3D shader resource types. Now we use those with inferred VK mode they appear.
* Implicit seems to work.
* Disable inference with Sampler/CombinedTextureSampler.
I guess? we could just use the HLSL texture register binding to infer.
* Report overlapping ranges in diagnostic.
The hlsl-to-vulkan-shift-diagnostic result might be surprising but it is correct, because u is automatically laid out so consumes DescriptorSlot 0, but that's already consumed by c.
* First attempt at array layout with infer on Vulkan.
* Fix the vulkan shift output.
* Array example.
Diffstat (limited to 'source/slang')
| -rw-r--r-- | source/slang/slang-diagnostic-defs.h | 2 | ||||
| -rw-r--r-- | source/slang/slang-hlsl-to-vulkan-layout-options.cpp | 56 | ||||
| -rw-r--r-- | source/slang/slang-hlsl-to-vulkan-layout-options.h | 38 | ||||
| -rw-r--r-- | source/slang/slang-parameter-binding.cpp | 307 | ||||
| -rw-r--r-- | source/slang/slang-type-layout.cpp | 132 | ||||
| -rw-r--r-- | source/slang/slang-type-layout.h | 14 |
6 files changed, 420 insertions, 129 deletions
diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h index 463c6f525..286551716 100644 --- a/source/slang/slang-diagnostic-defs.h +++ b/source/slang/slang-diagnostic-defs.h @@ -582,6 +582,8 @@ DIAGNOSTIC(39023, Error, mixingImplicitAndExplicitBindingForVaryingParams, "mixi DIAGNOSTIC(39024, Warning, cannotInferVulkanBindingWithoutRegisterModifier, "shader parameter '$0' doesn't have a 'register' specified, automatic layout will be used") +DIAGNOSTIC(39025, Error, conflictingVulkanInferredBindingForParameter, "conflicting vulkan inferred binding for parameter '$0' overlap is $1 and $2") + // // 4xxxx - IL code generation. diff --git a/source/slang/slang-hlsl-to-vulkan-layout-options.cpp b/source/slang/slang-hlsl-to-vulkan-layout-options.cpp index 1033c0f56..74a3313da 100644 --- a/source/slang/slang-hlsl-to-vulkan-layout-options.cpp +++ b/source/slang/slang-hlsl-to-vulkan-layout-options.cpp @@ -29,7 +29,12 @@ static NamesDescriptionValue s_vulkanShiftKinds[] = HLSLToVulkanLayoutOptions::HLSLToVulkanLayoutOptions() { - reset(); + // Clear the all shifts + for (auto& shift : m_allShifts) + { + shift = kInvalidShift; + } + SLANG_ASSERT(isReset()); } @@ -40,6 +45,8 @@ void HLSLToVulkanLayoutOptions::setGlobalsBinding(const Binding& binding) void HLSLToVulkanLayoutOptions::reset() { + m_kindShiftEnabledFlags = 0; + for (auto& shift : m_allShifts) { shift = kInvalidShift; @@ -50,25 +57,10 @@ void HLSLToVulkanLayoutOptions::reset() void HLSLToVulkanLayoutOptions::setAllShift(Kind kind, Index shift) { - // We try to follow the convention, of the *last* entry set is the one used. - // If there a "all" set, we remove everything for the kind. - - // Find all the entries for the kind - List<Key> keys; - for (auto& pair : m_shifts) - { - if (pair.key.kind == kind) - { - keys.add(pair.key); - } - } - // Remove them all - for (auto& key : keys) - { - m_shifts.remove(key); - } + SLANG_ASSERT(shift != kInvalidShift); m_allShifts[Index(kind)] = shift; + _enableShiftForKind(kind); } void HLSLToVulkanLayoutOptions::setShift(Kind kind, Index set, Index shift) @@ -76,31 +68,25 @@ void HLSLToVulkanLayoutOptions::setShift(Kind kind, Index set, Index shift) SLANG_ASSERT(shift != kInvalidShift); Key key{ kind, set }; - m_shifts.add(key, shift); + m_shifts.set(key, shift); + _enableShiftForKind(kind); } Index HLSLToVulkanLayoutOptions::getShift(Kind kind, Index set) const { - if (auto ptr = m_shifts.tryGetValue(Key{ kind, set })) + if (canInferBindingForKind(kind)) { - return *ptr; - } - - return m_allShifts[Index(kind)]; -} - -bool HLSLToVulkanLayoutOptions::canInferBindings() const -{ - // If any all shift is set it's not default - for (auto shift : m_allShifts) - { - if (shift != kInvalidShift) + // We lookup a shift for a set first as this shift is "more specific" and + // is seen as taken precedent over the "all" scenario + if (auto ptr = m_shifts.tryGetValue(Key{ kind, set })) { - return true; + return *ptr; } - } - return m_shifts.getCount() > 0; + // Must have an `all` shift + return m_allShifts[Index(kind)]; + } + return kInvalidShift; } bool HLSLToVulkanLayoutOptions::hasState() const diff --git a/source/slang/slang-hlsl-to-vulkan-layout-options.h b/source/slang/slang-hlsl-to-vulkan-layout-options.h index b9eb3046d..e16989810 100644 --- a/source/slang/slang-hlsl-to-vulkan-layout-options.h +++ b/source/slang/slang-hlsl-to-vulkan-layout-options.h @@ -44,7 +44,7 @@ public: /// Append/ConsumeStructuredBuffer /// RWBuffer /// RWTextureXD/Array - UnorderedAccess, + UnorderedAccess = 0, /// Sampler (s) /// @@ -66,6 +66,22 @@ public: CountOf, }; + // A flag for each kind + typedef uint32_t KindFlags; + struct KindFlag + { + enum Enum : KindFlags + { + UnorderedAccess = KindFlags(1) << Index(Kind::UnorderedAccess), + Sampler = KindFlags(1) << Index(Kind::Sampler), + ShaderResource = KindFlags(1) << Index(Kind::ShaderResource), + ConstantBuffer = KindFlags(1) << Index(Kind::ConstantBuffer), + }; + }; + + /// Get a kind flag from a kind + SLANG_FORCE_INLINE static KindFlag::Enum getKindFlag(Kind kind) { SLANG_ASSERT(kind != Kind::Invalid); return KindFlag::Enum(KindFlags(1) << Index(kind)); } + struct Key { typedef Key ThisType; @@ -79,7 +95,7 @@ public: Index set; ///< The set this shift is associated with }; - /// Set the the all option for the kind + /// Set the the all option for the kind. void setAllShift(Kind kind, Index shift); /// Set the shift for kind/set @@ -92,12 +108,21 @@ public: bool hasGlobalsBinding() const { return m_globalsBinding.isSet(); } /// True if holds state such that vulkan bindings can be inferred from HLSL bindings - bool canInferBindings() const; + bool canInferBindings() const { return m_kindShiftEnabledFlags != 0; } + + /// True if the kind/set can be inferred + bool canInfer(Kind kind, Index set) const { return getShift(kind, set) != kInvalidShift; } + + /// True if can infer a binding for a kind + bool canInferBindingForKind(Kind kind) const { return (m_kindShiftEnabledFlags & getKindFlag(kind)) != 0; } /// Given an kind and a binding infer the vulkan binding. /// Will return an invalid binding if one is not found Binding inferBinding(Kind kind, const Binding& inBinding) const; + /// Returns flags indicating for each kind if there is shift inference + KindFlags getKindShiftEnabledFlags() const { return m_kindShiftEnabledFlags; } + /// Reset state such that all options are set to their default. The same state as when /// originally constructed void reset(); @@ -125,10 +150,17 @@ public: static Kind getKind(slang::ParameterCategory param); protected: + /// Marks that a shift is enabled for the kind + void _enableShiftForKind(Kind kind) { m_kindShiftEnabledFlags |= getKindFlag(kind); } + Binding m_globalsBinding; + /// The `all` shifts Index m_allShifts[Count(Kind::CountOf)]; + /// Holds a bit for each kind that has a shift enabled + KindFlags m_kindShiftEnabledFlags = 0; + /// Maps a key to the amount of shift Dictionary<Key, Index> m_shifts; }; diff --git a/source/slang/slang-parameter-binding.cpp b/source/slang/slang-parameter-binding.cpp index 1d38bc2f0..51279b605 100644 --- a/source/slang/slang-parameter-binding.cpp +++ b/source/slang/slang-parameter-binding.cpp @@ -232,22 +232,70 @@ struct UsedRanges return Add(range); } - bool contains(UInt index) + /// Finds the range that contains the index + /// Returns -1 if not found + Index findRangeContaining(UInt index) const { - for (auto rr : ranges) + const auto rangeCount = ranges.getCount(); + for (Index i = 0; i < rangeCount; ++i) { - if (index < rr.begin) - return false; + const auto& rr = ranges[i]; + if (index >= rr.begin && index < rr.end) + { + return index; + } + } + return -1; + } + /// Finds the range index that contains the range passed in. + /// Returns -1 if not found + Index findRangeContaining(UInt index, UInt count) const + { + const auto start = index; + const auto end = index + count; - if (index >= rr.end) - continue; + const auto rangeCount = ranges.getCount(); + for (Index i = 0; i < rangeCount; ++i) + { + const auto& rr = ranges[i]; - return true; + if (!(end <= rr.begin || start >= rr.end)) + { + return i; + } } + return -1; + } - return false; + Index findRangeContaining(UInt index, LayoutSize size) const + { + if (size.isFinite()) + { + const auto count = size.getFiniteValue(); + if (count > 0) + { + return (count == 1) ? + findRangeContaining(index) : + findRangeContaining(index, count); + } + } + else + { + // The size is infinite... + const auto rangeCount = ranges.getCount(); + for (Index i = 0; i < rangeCount; ++i) + { + // If the range end is part start index it's a hit + if (ranges[i].end > index) + { + return i; + } + } + } + return -1; } + bool contains(UInt index) const { return findRangeContaining(index) >= 0; } // Try to find space for `count` entries UInt Allocate(VarLayout* param, UInt count) @@ -285,7 +333,7 @@ struct ParameterBindingInfo { size_t space = 0; size_t index = 0; - LayoutSize count; + LayoutSize count = 0; }; struct ParameterBindingAndKindInfo : ParameterBindingInfo @@ -312,18 +360,6 @@ struct ParameterInfo : RefObject RefPtr<VarLayout> varLayout; ParameterBindingInfo bindingInfo[kLayoutResourceKindCount]; - - // The translation unit this parameter is specific to, if any -// TranslationUnitRequest* translationUnit = nullptr; - - ParameterInfo() - { - // Make sure we aren't claiming any resources yet - for( int ii = 0; ii < kLayoutResourceKindCount; ++ii ) - { - bindingInfo[ii].count = 0; - } - } }; struct EntryPointParameterBindingContext @@ -766,17 +802,31 @@ static void collectGlobalScopeParameter( parameterInfo->varLayout = varLayout; } -static RefPtr<UsedRangeSet> findUsedRangeSetForSpace( +static UsedRangeSet* _getOrCreateUsedRangeSetForSpace( ParameterBindingContext* context, UInt space) { - RefPtr<UsedRangeSet> usedRangeSet; - if (context->shared->globalSpaceUsedRangeSets.tryGetValue(space, usedRangeSet)) - return usedRangeSet; + auto& globalSpaceUsedRangeSets = context->shared->globalSpaceUsedRangeSets; - usedRangeSet = new UsedRangeSet(); - context->shared->globalSpaceUsedRangeSets.add(space, usedRangeSet); - return usedRangeSet; + auto& value = globalSpaceUsedRangeSets.getOrAddValue(space, RefPtr<UsedRangeSet>()); + if (!value) + { + value = new UsedRangeSet(); + } + return value; +} + +static UsedRangeSet* _getUsedRangeSetForSpace( + ParameterBindingContext* context, + UInt space) +{ + auto& globalSpaceUsedRangeSets = context->shared->globalSpaceUsedRangeSets; + + if (auto usedRangeSetPtr = globalSpaceUsedRangeSets.tryGetValue(space)) + { + return *usedRangeSetPtr; + } + return nullptr; } // Record that a particular register space (or set, in the GLSL case) @@ -860,7 +910,7 @@ static void addExplicitParameterBinding( } else { - auto usedRangeSet = findUsedRangeSetForSpace(context, semanticInfo.space); + auto usedRangeSet = _getOrCreateUsedRangeSetForSpace(context, semanticInfo.space); // Record that the particular binding space was // used by an explicit binding, so that we don't @@ -1065,50 +1115,51 @@ static void addExplicitParameterBindings_GLSL( // We need an HLSL register semantic to to infer from auto hlslRegSemantic = varDecl.getDecl()->findModifier<HLSLRegisterSemantic>(); - if (hlslRegSemantic == nullptr) + if (!hlslRegSemantic) { - // We don't have a HLSL binding, so no inferance can occur, issue a warning - // - // TODO(JS): I suppose there is some ambiguity here, because if we did a semantic lookup, and it didn't have a vulkanKind - // or didn't parse correctly we wouldn't issue this message. - getSink(context)->diagnose(varDecl.getDecl(), Diagnostics::cannotInferVulkanBindingWithoutRegisterModifier, varDecl); + // We'll use inference from the HLSL like layout that will happen elsewhere return; } - - // Get the HLSL binding info + + const auto hlslInfo = _extractLayoutSemanticInfo(context, hlslRegSemantic); if (hlslInfo.kind == LayoutResourceKind::None) { - // Is no hlsl resource binding + // Doesn't have an HLSL resource consumption, so we are done return; } - // We need to map to the GLSL binding types - HLSLToVulkanLayoutOptions::Kind vulkanKind = HLSLToVulkanLayoutOptions::getKind(hlslInfo.kind); - if (vulkanKind == HLSLToVulkanLayoutOptions::Kind::Invalid) + // We can't infer TextureSampler from HLSL (it's not an HLSL concept) + // So use default layout + auto varType = varDecl.getDecl()->getType(); + if (as<TextureSamplerType>(varType)) { - // The binding is not "inferable" so we are done return; } - - const auto hlslBinding = HLSLToVulkanLayoutOptions::Binding{ Index(hlslInfo.space), Index(hlslInfo.index) }; - const auto vulkanBinding = hlslToVulkanLayoutOptions->inferBinding(vulkanKind, hlslBinding); - if (vulkanBinding.isInvalid()) + // Can we map to a Vulkan kind in principal? + const HLSLToVulkanLayoutOptions::Kind vulkanKind = HLSLToVulkanLayoutOptions::getKind(hlslInfo.kind); + if (vulkanKind == HLSLToVulkanLayoutOptions::Kind::Invalid) { - // If we made it here, there are shift options, but there isn't one for the space/kind specified - // That could be a problem and unexpected, so issue a warning - getSink(context)->diagnose(varDecl.getDecl(), Diagnostics::hlslToVulkanMappingNotFound, varDecl); + // If we can't use inference, for the kind we'll use other mechanisms so we are done return; } - // Add for descriptor slot - resInfo = typeLayout->findOrAddResourceInfo(LayoutResourceKind::DescriptorTableSlot); + // If inference is not enabled for this kind, we can issue a warning + if (!hlslToVulkanLayoutOptions->canInfer(vulkanKind, hlslInfo.space)) + { + _maybeDiagnoseMissingVulkanLayoutModifier(context, varDecl); + return; + } + // We use the HLSL binding directly (even though this notionally for GLSL/Vulkan) + // We'll do the shifting at later later point in _maybeApplyHLSLToVulkanShifts + resInfo = typeLayout->findOrAddResourceInfo(hlslInfo.kind); + semanticInfo.kind = resInfo->kind; - semanticInfo.index = UInt(vulkanBinding.index); - semanticInfo.space = UInt(vulkanBinding.set); - + semanticInfo.index = UInt(hlslInfo.index); + semanticInfo.space = UInt(hlslInfo.space); + const LayoutSize count = resInfo->count; addExplicitParameterBinding(context, parameterInfo, varDecl.getDecl(), semanticInfo, count); @@ -1116,7 +1167,7 @@ static void addExplicitParameterBindings_GLSL( // Given a single parameter, collect whatever information we have on // how it has been explicitly bound, which may come from multiple declarations -void generateParameterBindings( +void _generateParameterBindings( ParameterBindingContext* context, RefPtr<ParameterInfo> parameterInfo) { @@ -1330,7 +1381,7 @@ static void completeBindingsForParameterImpl( // space. UInt space = context->shared->defaultSpace; - RefPtr<UsedRangeSet> usedRangeSet = findUsedRangeSetForSpace(context, space); + RefPtr<UsedRangeSet> usedRangeSet = _getOrCreateUsedRangeSetForSpace(context, space); bindingInfo.count = count; bindingInfo.index = usedRangeSet->usedResourceRanges[(int)kind].Allocate(firstVarLayout, count.getFiniteValue()); @@ -1518,7 +1569,7 @@ static RefPtr<TypeLayout> processSimpleEntryPointParameter( // TODO: construct a `ParameterInfo` we can use here so that // overlapped layout errors get reported nicely. // - auto usedResourceSet = findUsedRangeSetForSpace(context, 0); + auto usedResourceSet = _getOrCreateUsedRangeSetForSpace(context, 0); usedResourceSet->usedResourceRanges[int(LayoutResourceKind::UnorderedAccess)].Add(nullptr, semanticIndex, semanticIndex + semanticSlotCount); } } @@ -2528,11 +2579,11 @@ static ParameterBindingAndKindInfo _allocateConstantBufferBinding( ParameterBindingContext* context) { UInt space = context->shared->defaultSpace; - auto usedRangeSet = findUsedRangeSetForSpace(context, space); + auto usedRangeSet = _getOrCreateUsedRangeSetForSpace(context, space); auto layoutInfo = context->getRulesFamily() ->getConstantBufferRules(context->getTargetRequest()) - ->GetObjectLayout(ShaderParameterKind::ConstantBuffer); + ->GetObjectLayout(ShaderParameterKind::ConstantBuffer, context->layoutContext.objectLayoutOptions); ParameterBindingAndKindInfo info; info.kind = layoutInfo.kind; @@ -2548,11 +2599,11 @@ static ParameterBindingAndKindInfo _assignConstantBufferBinding( UInt space, UInt index) { - auto usedRangeSet = findUsedRangeSetForSpace(context, space); + auto usedRangeSet = _getOrCreateUsedRangeSetForSpace(context, space); auto layoutInfo = context->getRulesFamily() ->getConstantBufferRules(context->getTargetRequest()) - ->GetObjectLayout(ShaderParameterKind::ConstantBuffer); + ->GetObjectLayout(ShaderParameterKind::ConstantBuffer, context->layoutContext.objectLayoutOptions); const Index count = Index(layoutInfo.size.getFiniteValue()); @@ -3561,6 +3612,133 @@ static bool _calcNeedsDefaultSpace(SharedParameterBindingContext& sharedContext) return false; } +static void _appendRange(Index start, LayoutSize size, StringBuilder& ioBuf) +{ + if (size == 1) + { + // If it's in effect a single index, just append like that. + ioBuf << start; + } + else + { + ioBuf << "[ " << start << " ... "; + if (size.isFinite()) + { + ioBuf << start + size.getFiniteValue() << ")"; + } + else + { + ioBuf << "inf )"; + } + } +} + +static void _maybeApplyHLSLToVulkanShifts( + ParameterBindingContext* paramContext, + TargetRequest* targetReq, + DiagnosticSink* sink) +{ + SLANG_UNUSED(sink); + + SharedParameterBindingContext& sharedContext = *paramContext->shared; + + // We may need to finally do any shifting if we have HLSLToVulkanLayoutOptions + auto vulkanOptions = targetReq->getHLSLToVulkanLayoutOptions(); + if (!vulkanOptions) + { + return; + } + + // We only need to do this if there is some inferance + if (!vulkanOptions->canInferBindings()) + { + return; + } + + for (ParameterInfo* parameterInfo : sharedContext.parameters) + { + auto varLayout = parameterInfo->varLayout; + SLANG_RELEASE_ASSERT(varLayout); + + // Iterate over all of the resourceInfos + for (auto& resourceInfo : varLayout->resourceInfos) + { + // Get the hlslToVulkanKind, and apply if valid + auto hlslToVulkanKind = HLSLToVulkanLayoutOptions::getKind(resourceInfo.kind); + if (hlslToVulkanKind != HLSLToVulkanLayoutOptions::Kind::Invalid) + { + auto& bindingInfo = parameterInfo->bindingInfo[Index(resourceInfo.kind)]; + + // Lookup the shift for the kind/space + const auto shift = vulkanOptions->getShift(hlslToVulkanKind, resourceInfo.space); + // If the shift is valid.. + if (shift != HLSLToVulkanLayoutOptions::kInvalidShift) + { + // Apply the shift + resourceInfo.index += shift; + + // Fix the parameter binding info + bindingInfo.index += shift; + + // Presumably they should both match + SLANG_ASSERT(bindingInfo.index == resourceInfo.index); + SLANG_ASSERT(bindingInfo.space == resourceInfo.space); + } + + // We should go looking for overlaps. + // In essence we need to look for HLSL kinds which have inferance. + // We assume all map to Descriptor, and look for descriptor overlaps + + // We know there can't be a clash of HLSL layout kinds previously, otherwise that would have already produced an a warning. + // We also know the only change is either *all* of a set is shifted or none. + // That means post a shift there still can't be clash between HLSL types. + + // So clashes can only be between HLSL types and other bindings (regardless) + + + auto usedSpace = _getUsedRangeSetForSpace(paramContext, bindingInfo.space); + + // The space must exist, because we are already consuming resources on it + SLANG_ASSERT(usedSpace); + + const auto& usedRanges = usedSpace->usedResourceRanges; + + // All the HLSL like bindings, we in actuality be DescriptorSlots on Vulkan + const auto& usedRange = usedRanges[Index(LayoutResourceKind::DescriptorTableSlot)]; + + // We need to get the count for the amount of slots consumed for this type + auto typeLayout = varLayout->getTypeLayout(); + + if (auto resInfo = typeLayout->FindResourceInfo(resourceInfo.kind)) + { + const auto rangeIndex = usedRange.findRangeContaining(bindingInfo.index, resInfo->count); + if (rangeIndex >= 0) + { + // We found a clash. + + const auto& clashRange = usedRange.ranges[rangeIndex]; + + // Get the var we are clashing with + auto clashingVarLayout = clashRange.parameter; + + // Get the var that we are currently looking at + auto curVar = parameterInfo->varLayout->getVariable(); + + StringBuilder curRangeBuf; + _appendRange(bindingInfo.index, resInfo->count, curRangeBuf); + + StringBuilder clashRangeBuf; + _appendRange(clashRange.begin, LayoutSize(clashRange.end), clashRangeBuf); + + // Report the clash. + sink->diagnose(curVar, Diagnostics::conflictingVulkanInferredBindingForParameter, getReflectionName(clashingVarLayout->getVariable()), curRangeBuf, clashRangeBuf); + } + } + } + } + } +} + RefPtr<ProgramLayout> generateParameterBindings( TargetProgram* targetProgram, DiagnosticSink* sink) @@ -3644,7 +3822,7 @@ RefPtr<ProgramLayout> generateParameterBindings( // for( auto& parameter : sharedContext.parameters ) { - generateParameterBindings(&context, parameter); + _generateParameterBindings(&context, parameter); } // It is possible that code has specified an explicit location @@ -3714,7 +3892,7 @@ RefPtr<ProgramLayout> generateParameterBindings( // kind of a mess, but also seems to be the best possible // answer given the constraints. // - auto usedRangeSet = findUsedRangeSetForSpace(&context, info.space); + auto usedRangeSet = _getOrCreateUsedRangeSetForSpace(&context, info.space); markSpaceUsed(&context, nullptr, info.space); usedRangeSet->usedResourceRanges[(int)kind].Add( nullptr, @@ -3856,6 +4034,9 @@ RefPtr<ProgramLayout> generateParameterBindings( // _completeBindings(&context, program); + // We may need to finally do any shifting if we have HLSLToVulkanLayoutOptions + _maybeApplyHLSLToVulkanShifts(&context, targetReq, sink); + // Next we need to create a type layout to reflect the information // we have collected, and we will use the `ScopeLayoutBuilder` // to encapsulate the logic that can be shared with the entry-point diff --git a/source/slang/slang-type-layout.cpp b/source/slang/slang-type-layout.cpp index c933c5bb5..5cf8d2350 100644 --- a/source/slang/slang-type-layout.cpp +++ b/source/slang/slang-type-layout.cpp @@ -696,12 +696,60 @@ struct GLSLSpecializationConstantLayoutRulesImpl : DefaultLayoutRulesImpl GLSLSpecializationConstantLayoutRulesImpl kGLSLSpecializationConstantLayoutRulesImpl; -// +// Given a ShaderParamKind returns the equivalent LayoutResourceKind/ParameterCategory/SlangParameterCategory +static LayoutResourceKind _getHLSLLayoutResourceKind(ShaderParameterKind kind) +{ + switch (kind) + { + case ShaderParameterKind::ConstantBuffer: + return LayoutResourceKind::ConstantBuffer; + + case ShaderParameterKind::TextureUniformBuffer: + case ShaderParameterKind::StructuredBuffer: + case ShaderParameterKind::RawBuffer: + case ShaderParameterKind::Buffer: + case ShaderParameterKind::Texture: + return LayoutResourceKind::ShaderResource; + + case ShaderParameterKind::MutableStructuredBuffer: + case ShaderParameterKind::MutableRawBuffer: + case ShaderParameterKind::MutableBuffer: + case ShaderParameterKind::MutableTexture: + return LayoutResourceKind::UnorderedAccess; + + case ShaderParameterKind::SamplerState: + return LayoutResourceKind::SamplerState; + default: + return LayoutResourceKind::None; + } +} struct GLSLObjectLayoutRulesImpl : ObjectLayoutRulesImpl { - virtual SimpleLayoutInfo GetObjectLayout(ShaderParameterKind) override + virtual SimpleLayoutInfo GetObjectLayout(ShaderParameterKind kind, const Options& options) override { + if (options.hlslToVulkanKindFlags) + { + // Is this an HLSL kind that might be shifted + + // Get as hlslLayoutKind + const auto hlslLayoutKind = _getHLSLLayoutResourceKind(kind); + + // Get as hlslToVulkanKind + const auto hlslToVulkanKind = HLSLToVulkanLayoutOptions::getKind(hlslLayoutKind); + + if (hlslToVulkanKind != HLSLToVulkanLayoutOptions::Kind::Invalid) + { + // Is this kind enabled for shift? + if (options.hlslToVulkanKindFlags & HLSLToVulkanLayoutOptions::getKindFlag(hlslToVulkanKind)) + { + // We are going to consume a HLSL layout kind + // Later we will do shifting as necessary + return SimpleLayoutInfo(hlslLayoutKind, 1); + } + } + } + // In Vulkan GLSL, pretty much every object is just a descriptor-table slot. // We can refine this method once we support a case where this isn't true. return SimpleLayoutInfo(LayoutResourceKind::DescriptorTableSlot, 1); @@ -711,7 +759,7 @@ GLSLObjectLayoutRulesImpl kGLSLObjectLayoutRulesImpl; struct GLSLPushConstantBufferObjectLayoutRulesImpl : GLSLObjectLayoutRulesImpl { - virtual SimpleLayoutInfo GetObjectLayout(ShaderParameterKind /*kind*/) override + virtual SimpleLayoutInfo GetObjectLayout(ShaderParameterKind /*kind*/, const Options& /* options */) override { // Special-case the layout for a constant-buffer, because we don't // want it to allocate a descriptor-table slot @@ -722,7 +770,7 @@ GLSLPushConstantBufferObjectLayoutRulesImpl kGLSLPushConstantBufferObjectLayoutR struct GLSLShaderRecordConstantBufferObjectLayoutRulesImpl : GLSLObjectLayoutRulesImpl { - virtual SimpleLayoutInfo GetObjectLayout(ShaderParameterKind /*kind*/) override + virtual SimpleLayoutInfo GetObjectLayout(ShaderParameterKind /*kind*/, const Options& /* options */) override { // Special-case the layout for a constant-buffer, because we don't // want it to allocate a descriptor-table slot @@ -733,7 +781,7 @@ GLSLShaderRecordConstantBufferObjectLayoutRulesImpl kGLSLShaderRecordConstantBuf struct HLSLObjectLayoutRulesImpl : ObjectLayoutRulesImpl { - virtual SimpleLayoutInfo GetObjectLayout(ShaderParameterKind kind) override + virtual SimpleLayoutInfo GetObjectLayout(ShaderParameterKind kind, const Options& /* options */) override { switch( kind ) { @@ -910,7 +958,7 @@ CUDALayoutRulesFamilyImpl kCUDALayoutRulesFamilyImpl; struct CPUObjectLayoutRulesImpl : ObjectLayoutRulesImpl { - virtual SimpleLayoutInfo GetObjectLayout(ShaderParameterKind kind) override + virtual SimpleLayoutInfo GetObjectLayout(ShaderParameterKind kind, const Options& /* options */) override { switch (kind) { @@ -962,7 +1010,7 @@ struct CUDAObjectLayoutRulesImpl : CPUObjectLayoutRulesImpl // of opaque handle (as opposed to a pointer) such as CUsurfObject, CUtexObject typedef unsigned long long ObjectHandle; - virtual SimpleLayoutInfo GetObjectLayout(ShaderParameterKind kind) override + virtual SimpleLayoutInfo GetObjectLayout(ShaderParameterKind kind, const Options & /* options */) override { switch (kind) { @@ -1501,6 +1549,11 @@ TypeLayoutContext getInitialLayoutContextForTarget(TargetRequest* targetReq, Pro context.rules = nullptr; context.matrixLayoutMode = targetReq->getDefaultMatrixLayoutMode(); + if (auto hlslToVulkanLayoutOptions = targetReq->getHLSLToVulkanLayoutOptions()) + { + context.objectLayoutOptions.hlslToVulkanKindFlags = hlslToVulkanLayoutOptions->getKindShiftEnabledFlags(); + } + if( rulesFamily ) { context.rules = rulesFamily->getConstantBufferRules(targetReq); @@ -1586,21 +1639,22 @@ static TypeLayoutResult createSimpleTypeLayout( return TypeLayoutResult(typeLayout, info); } -static SimpleLayoutInfo getParameterGroupLayoutInfo( - ParameterGroupType* type, - LayoutRulesImpl* rules) +static SimpleLayoutInfo _getParameterGroupLayoutInfo( + TypeLayoutContext const& context, + ParameterGroupType* type, + LayoutRulesImpl* rules) { if( as<ConstantBufferType>(type) ) { - return rules->GetObjectLayout(ShaderParameterKind::ConstantBuffer); + return rules->GetObjectLayout(ShaderParameterKind::ConstantBuffer, context.objectLayoutOptions); } else if( as<TextureBufferType>(type) ) { - return rules->GetObjectLayout(ShaderParameterKind::TextureUniformBuffer); + return rules->GetObjectLayout(ShaderParameterKind::TextureUniformBuffer, context.objectLayoutOptions); } else if( as<GLSLShaderStorageBufferType>(type) ) { - return rules->GetObjectLayout(ShaderParameterKind::ShaderStorageBuffer); + return rules->GetObjectLayout(ShaderParameterKind::ShaderStorageBuffer, context.objectLayoutOptions); } else if (as<ParameterBlockType>(type)) { @@ -2270,7 +2324,7 @@ static RefPtr<TypeLayout> _createParameterGroupTypeLayout( // allocate a constant buffer regiser/binding into // the overall layout, to account for it. // - auto cbUsage = parameterGroupRules->GetObjectLayout(ShaderParameterKind::ConstantBuffer); + auto cbUsage = parameterGroupRules->GetObjectLayout(ShaderParameterKind::ConstantBuffer, context.objectLayoutOptions); containerTypeLayout->addResourceUsage(cbUsage.kind, cbUsage.size); } @@ -2730,7 +2784,7 @@ createStructuredBufferTypeLayout( RefPtr<TypeLayout> elementTypeLayout) { auto rules = context.rules; - auto info = rules->GetObjectLayout(kind); + auto info = rules->GetObjectLayout(kind, context.objectLayoutOptions); auto typeLayout = new StructuredBufferTypeLayout(); @@ -3378,6 +3432,26 @@ RefPtr<TypeLayout> createTypeLayoutForGlobalGenericTypeParam( return _createTypeLayoutForGlobalGenericTypeParam(context, type, globalGenericParamDecl).layout; } +static bool _isDescriptorSlotLike( + TypeLayoutContext const& context, + LayoutResourceKind kind) +{ + if (kind == LayoutResourceKind::DescriptorTableSlot) + { + return true; + } + + if (context.objectLayoutOptions.hlslToVulkanKindFlags) + { + const auto hlslToVulkanKind = HLSLToVulkanLayoutOptions::getKind(kind); + // If it maps to a kind and it is enabled it is 'in effect' a Descriptor slot + return hlslToVulkanKind != HLSLToVulkanLayoutOptions::Kind::Invalid && + (context.objectLayoutOptions.hlslToVulkanKindFlags & HLSLToVulkanLayoutOptions::getKindFlag(hlslToVulkanKind)); + } + + return false; +} + static TypeLayoutResult createArrayLikeTypeLayout( TypeLayoutContext const& context, Type* type, @@ -3472,6 +3546,13 @@ static TypeLayoutResult createArrayLikeTypeLayout( LayoutSize arrayResourceCount = 0; + // We copy because if the element is *actually* DescriptorSlot like, + // we'll change the type. + // NOTE! That as it stands this will change the resource type from an HLSL type + // to Descriptor slot. This scenario happens when we have HLSLToVulkanLayoutOptions + // enabled, we layout with some HLSL types. + auto elementResourceKind = elementResourceInfo.kind; + // In almost all cases, the resources consumed by an array // will be its element count times the resources consumed // by its element type. @@ -3480,14 +3561,15 @@ static TypeLayoutResult createArrayLikeTypeLayout( // compiling to GLSL for Vulkan, where an entire array // only consumes a single descriptor-table slot. // - if (elementResourceInfo.kind == LayoutResourceKind::DescriptorTableSlot) + if (_isDescriptorSlotLike(context, elementResourceKind)) { arrayResourceCount = elementResourceInfo.count; + elementResourceKind = LayoutResourceKind::DescriptorTableSlot; } // The second exception to this is arrays of an existential type // where the entire array should be specialized to a single concrete type. // - else if (elementResourceInfo.kind == LayoutResourceKind::ExistentialTypeParam) + else if (elementResourceKind == LayoutResourceKind::ExistentialTypeParam) { arrayResourceCount = elementResourceInfo.count; } @@ -3505,7 +3587,7 @@ static TypeLayoutResult createArrayLikeTypeLayout( else if( elementCount.isInfinite() && adjustedElementTypeLayout != elementTypeLayout - && doesResourceRequireAdjustmentForArrayOfStructs(elementResourceInfo.kind) ) + && doesResourceRequireAdjustmentForArrayOfStructs(elementResourceKind) ) { // We want to ignore resource types consumed by the element type // that need adjustement if the array size is infinite, since @@ -3611,7 +3693,7 @@ static TypeLayoutResult _createTypeLayout( // different from a `Texture2D<U>` in terms of how it // should be handled as a member of a container. // - auto info = getParameterGroupLayoutInfo(parameterGroupType, rules); + auto info = _getParameterGroupLayoutInfo(context, parameterGroupType, rules); // The more interesting case, though, is when the user // is requesting us to actually create a `TypeLayout`, @@ -3633,7 +3715,7 @@ static TypeLayoutResult _createTypeLayout( else if (const auto samplerStateType = as<SamplerStateType>(type)) { return createSimpleTypeLayout( - rules->GetObjectLayout(ShaderParameterKind::SamplerState), + rules->GetObjectLayout(ShaderParameterKind::SamplerState, context.objectLayoutOptions), type, rules); } @@ -3654,7 +3736,7 @@ static TypeLayoutResult _createTypeLayout( } return createSimpleTypeLayout( - rules->GetObjectLayout(kind), + rules->GetObjectLayout(kind, context.objectLayoutOptions), type, rules); } @@ -3675,7 +3757,7 @@ static TypeLayoutResult _createTypeLayout( } return createSimpleTypeLayout( - rules->GetObjectLayout(kind), + rules->GetObjectLayout(kind, context.objectLayoutOptions), type, rules); } @@ -3696,7 +3778,7 @@ static TypeLayoutResult _createTypeLayout( } return createSimpleTypeLayout( - rules->GetObjectLayout(kind), + rules->GetObjectLayout(kind, context.objectLayoutOptions), type, rules); } @@ -3704,7 +3786,7 @@ static TypeLayoutResult _createTypeLayout( // TODO: need a better way to handle this stuff... #define CASE(TYPE, KIND) \ else if(auto type_##TYPE = as<TYPE>(type)) do { \ - auto info = rules->GetObjectLayout(ShaderParameterKind::KIND); \ + auto info = rules->GetObjectLayout(ShaderParameterKind::KIND, context.objectLayoutOptions); \ auto typeLayout = createStructuredBufferTypeLayout( \ context, \ ShaderParameterKind::KIND, \ @@ -3726,7 +3808,7 @@ static TypeLayoutResult _createTypeLayout( #define CASE(TYPE, KIND) \ else if(as<TYPE>(type)) do { \ return createSimpleTypeLayout( \ - rules->GetObjectLayout(ShaderParameterKind::KIND), \ + rules->GetObjectLayout(ShaderParameterKind::KIND, context.objectLayoutOptions), \ type, rules); \ } while(0) diff --git a/source/slang/slang-type-layout.h b/source/slang/slang-type-layout.h index a80f6afdd..dc769d1c7 100644 --- a/source/slang/slang-type-layout.h +++ b/source/slang/slang-type-layout.h @@ -947,8 +947,13 @@ struct SimpleLayoutRulesImpl struct ObjectLayoutRulesImpl { + struct Options + { + HLSLToVulkanLayoutOptions::KindFlags hlslToVulkanKindFlags = 0; + }; + // Compute layout info for an object type - virtual SimpleLayoutInfo GetObjectLayout(ShaderParameterKind kind) = 0; + virtual SimpleLayoutInfo GetObjectLayout(ShaderParameterKind kind, const Options& options) = 0; }; struct LayoutRulesImpl @@ -999,9 +1004,9 @@ struct LayoutRulesImpl // Forward `ObjectLayoutRulesImpl` interface - SimpleLayoutInfo GetObjectLayout(ShaderParameterKind kind) + SimpleLayoutInfo GetObjectLayout(ShaderParameterKind kind, const ObjectLayoutRulesImpl::Options& options) { - return objectRules->GetObjectLayout(kind); + return objectRules->GetObjectLayout(kind, options); } // @@ -1084,6 +1089,9 @@ struct TypeLayoutContext // Map types to their type layout Dictionary<Type*, TypeLayoutResult> layoutMap; + // Options passed to object layout + ObjectLayoutRulesImpl::Options objectLayoutOptions; + LayoutRulesImpl* getRules() { return rules; } LayoutRulesFamilyImpl* getRulesFamily() const { return rules->getLayoutRulesFamily(); } |
