summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--slang.h14
-rw-r--r--source/slang/slang-ast-builder.cpp5
-rw-r--r--source/slang/slang-ast-builder.h2
-rw-r--r--source/slang/slang-emit-spirv-ops.h52
-rw-r--r--source/slang/slang-emit-spirv.cpp14
-rw-r--r--source/slang/slang-ir-lower-append-consume-structured-buffer.cpp17
-rw-r--r--source/slang/slang-lower-to-ir.cpp31
-rw-r--r--source/slang/slang-reflection-api.cpp105
-rw-r--r--source/slang/slang-type-layout.cpp83
-rw-r--r--source/slang/slang-type-layout.h22
-rw-r--r--tests/hlsl/append-structured-buffer.slang71
-rw-r--r--tests/hlsl/consume-structured-buffer.slang50
-rw-r--r--tools/gfx-util/shader-cursor.cpp52
-rw-r--r--tools/gfx-util/shader-cursor.h7
-rw-r--r--tools/gfx/d3d12/d3d12-device.cpp3
-rw-r--r--tools/gfx/d3d12/d3d12-resource-views.h2
-rw-r--r--tools/gfx/d3d12/d3d12-shader-object.cpp9
-rw-r--r--tools/gfx/d3d12/d3d12-shader-object.h1
-rw-r--r--tools/render-test/render-test-main.cpp42
-rw-r--r--tools/render-test/shader-input-layout.cpp5
-rw-r--r--tools/render-test/shader-input-layout.h4
21 files changed, 483 insertions, 108 deletions
diff --git a/slang.h b/slang.h
index 78b56c546..fab62cf81 100644
--- a/slang.h
+++ b/slang.h
@@ -2227,6 +2227,8 @@ extern "C"
SLANG_API SlangInt spReflectionTypeLayout_findFieldIndexByName(SlangReflectionTypeLayout* typeLayout, const char* nameBegin, const char* nameEnd);
+ SLANG_API SlangReflectionVariableLayout* spReflectionTypeLayout_GetExplicitCounter(SlangReflectionTypeLayout* typeLayout);
+
SLANG_API size_t spReflectionTypeLayout_GetElementStride(SlangReflectionTypeLayout* type, SlangParameterCategory category);
SLANG_API SlangReflectionTypeLayout* spReflectionTypeLayout_GetElementTypeLayout(SlangReflectionTypeLayout* type);
SLANG_API SlangReflectionVariableLayout* spReflectionTypeLayout_GetElementVarLayout(SlangReflectionTypeLayout* type);
@@ -2253,6 +2255,7 @@ extern "C"
SLANG_API SlangReflectionTypeLayout* spReflectionTypeLayout_getBindingRangeLeafTypeLayout(SlangReflectionTypeLayout* typeLayout, SlangInt index);
SLANG_API SlangReflectionVariable* spReflectionTypeLayout_getBindingRangeLeafVariable(SlangReflectionTypeLayout* typeLayout, SlangInt index);
SLANG_API SlangInt spReflectionTypeLayout_getFieldBindingRangeOffset(SlangReflectionTypeLayout* typeLayout, SlangInt fieldIndex);
+ SLANG_API SlangInt spReflectionTypeLayout_getExplicitCounterBindingRangeOffset(SlangReflectionTypeLayout* inTypeLayout);
SLANG_API SlangInt spReflectionTypeLayout_getBindingRangeDescriptorSetIndex(SlangReflectionTypeLayout* typeLayout, SlangInt index);
SLANG_API SlangInt spReflectionTypeLayout_getBindingRangeFirstDescriptorRangeIndex(SlangReflectionTypeLayout* typeLayout, SlangInt index);
@@ -2711,6 +2714,11 @@ namespace slang
return spReflectionTypeLayout_findFieldIndexByName((SlangReflectionTypeLayout*) this, nameBegin, nameEnd);
}
+ VariableLayoutReflection* getExplicitCounter()
+ {
+ return (VariableLayoutReflection*) spReflectionTypeLayout_GetExplicitCounter((SlangReflectionTypeLayout*) this);
+ }
+
bool isArray() { return getType()->isArray(); }
TypeLayoutReflection* unwrapArray()
@@ -2871,6 +2879,12 @@ namespace slang
fieldIndex);
}
+ SlangInt getExplicitCounterBindingRangeOffset()
+ {
+ return spReflectionTypeLayout_getExplicitCounterBindingRangeOffset(
+ (SlangReflectionTypeLayout*) this);
+ }
+
TypeLayoutReflection* getBindingRangeLeafTypeLayout(SlangInt index)
{
return (TypeLayoutReflection*) spReflectionTypeLayout_getBindingRangeLeafTypeLayout(
diff --git a/source/slang/slang-ast-builder.cpp b/source/slang/slang-ast-builder.cpp
index 6a9aad257..ce13ab650 100644
--- a/source/slang/slang-ast-builder.cpp
+++ b/source/slang/slang-ast-builder.cpp
@@ -358,6 +358,11 @@ HLSLStructuredBufferType* ASTBuilder::getStructuredBufferType(Type* elementType)
return as<HLSLStructuredBufferType>(getSpecializedBuiltinType(elementType, "HLSLStructuredBufferType"));
}
+HLSLRWStructuredBufferType* ASTBuilder::getRWStructuredBufferType(Type* elementType)
+{
+ return as<HLSLRWStructuredBufferType>(getSpecializedBuiltinType(elementType, "HLSLRWStructuredBufferType"));
+}
+
SamplerStateType* ASTBuilder::getSamplerStateType()
{
return as<SamplerStateType>(getSpecializedBuiltinType(nullptr, "HLSLStructuredBufferType"));
diff --git a/source/slang/slang-ast-builder.h b/source/slang/slang-ast-builder.h
index a5e1cd40c..aff3088ab 100644
--- a/source/slang/slang-ast-builder.h
+++ b/source/slang/slang-ast-builder.h
@@ -473,6 +473,8 @@ public:
HLSLStructuredBufferType* getStructuredBufferType(Type* elementType);
+ HLSLRWStructuredBufferType* getRWStructuredBufferType(Type* elementType);
+
SamplerStateType* getSamplerStateType();
DifferentialPairType* getDifferentialPairType(
diff --git a/source/slang/slang-emit-spirv-ops.h b/source/slang/slang-emit-spirv-ops.h
index f64fc7f9a..f1a9ff3e4 100644
--- a/source/slang/slang-emit-spirv-ops.h
+++ b/source/slang/slang-emit-spirv-ops.h
@@ -2313,4 +2313,56 @@ SpvInst* emitOpExecutionModeIdLocalSizeId(
zSize
);
}
+
+template<typename T1, typename T2, typename T3, typename T4>
+SpvInst* emitOpAtomicIIncrement(
+ SpvInstParent* parent,
+ IRInst* inst,
+ const T1& idResultType,
+ const T2& pointer,
+ const T3& memory,
+ const T4& semantics
+)
+{
+ static_assert(isSingular<T1>);
+ static_assert(isSingular<T2>);
+ static_assert(isSingular<T3>);
+ static_assert(isSingular<T4>);
+ return emitInst(
+ parent,
+ inst,
+ SpvOpAtomicIIncrement,
+ idResultType,
+ kResultID,
+ pointer,
+ memory,
+ semantics
+ );
+}
+
+template<typename T1, typename T2, typename T3, typename T4>
+SpvInst* emitOpAtomicIDecrement(
+ SpvInstParent* parent,
+ IRInst* inst,
+ const T1& idResultType,
+ const T2& pointer,
+ const T3& memory,
+ const T4& semantics
+)
+{
+ static_assert(isSingular<T1>);
+ static_assert(isSingular<T2>);
+ static_assert(isSingular<T3>);
+ static_assert(isSingular<T4>);
+ return emitInst(
+ parent,
+ inst,
+ SpvOpAtomicIDecrement,
+ idResultType,
+ kResultID,
+ pointer,
+ memory,
+ semantics
+ );
+}
#endif // SLANG_IN_SPIRV_EMIT_CONTEXT
diff --git a/source/slang/slang-emit-spirv.cpp b/source/slang/slang-emit-spirv.cpp
index 16a88b8af..aa21127e9 100644
--- a/source/slang/slang-emit-spirv.cpp
+++ b/source/slang/slang-emit-spirv.cpp
@@ -2390,6 +2390,20 @@ struct SPIRVEmitContext
return emitImageStore(parent, as<IRImageStore>(inst));
case kIROp_ImageSubscript:
return emitImageSubscript(parent, as<IRImageSubscript>(inst));
+ case kIROp_AtomicCounterIncrement:
+ {
+ IRBuilder builder{inst};
+ const auto memoryScope = emitIntConstant(IRIntegerValue{SpvScopeDevice}, builder.getUIntType());
+ const auto memorySemantics = emitIntConstant(IRIntegerValue{SpvMemorySemanticsMaskNone}, builder.getUIntType());
+ return emitOpAtomicIIncrement(parent, inst, inst->getFullType(), inst->getOperand(0), memoryScope, memorySemantics);
+ }
+ case kIROp_AtomicCounterDecrement:
+ {
+ IRBuilder builder{inst};
+ const auto memoryScope = emitIntConstant(IRIntegerValue{SpvScopeDevice}, builder.getUIntType());
+ const auto memorySemantics = emitIntConstant(IRIntegerValue{SpvMemorySemanticsMaskNone}, builder.getUIntType());
+ return emitOpAtomicIDecrement(parent, inst, inst->getFullType(), inst->getOperand(0), memoryScope, memorySemantics);
+ }
}
}
diff --git a/source/slang/slang-ir-lower-append-consume-structured-buffer.cpp b/source/slang/slang-ir-lower-append-consume-structured-buffer.cpp
index 9df5f4e7e..ed04541ef 100644
--- a/source/slang/slang-ir-lower-append-consume-structured-buffer.cpp
+++ b/source/slang/slang-ir-lower-append-consume-structured-buffer.cpp
@@ -154,14 +154,15 @@ namespace Slang
builder.setInsertInto(getDimensionsFunc);
builder.emitBlock();
auto bufferParam = builder.emitParam(structType);
- auto counterBuffer = builder.emitFieldExtract(counterBufferType, bufferParam, counterBufferKey);
- IRInst* getCounterPtrArgs[] = { counterBuffer, builder.getIntValue(builder.getIntType(), 0) };
- auto counterBufferPtr = builder.emitIntrinsicInst(builder.getPtrType(builder.getIntType()), kIROp_RWStructuredBufferGetElementPtr, 2, getCounterPtrArgs);
- auto counter = builder.emitLoad(counterBufferPtr);
- counter = builder.emitCast(builder.getUIntType(), counter);
- auto stride = builder.getIntValue(builder.getUIntType(), elementSize.getStride());
- IRInst* vecArgs[] = { counter, stride };
- builder.emitReturn(builder.emitMakeVector(uint2Type, 2, vecArgs));
+ auto elementBuffer = builder.emitFieldExtract(elementBufferType, bufferParam, elementBufferKey);
+
+ const auto dim = builder.emitIntrinsicInst(
+ uint2Type,
+ kIROp_StructuredBufferGetDimensions,
+ 1,
+ &elementBuffer
+ );
+ builder.emitReturn(dim);
}
// Replace all insts with synthesized functions.
diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp
index c90230c1f..2813918b6 100644
--- a/source/slang/slang-lower-to-ir.cpp
+++ b/source/slang/slang-lower-to-ir.cpp
@@ -8633,6 +8633,23 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
return v;
}
+ void addSpecializedForTargetDecorations(IRInst* inst, Decl* decl)
+ {
+ // If this declaration was marked as being an intrinsic for a particular
+ // target, then we should reflect that here.
+ for (auto targetMod : decl->getModifiersOfType<SpecializedForTargetModifier>())
+ {
+ // `targetMod` indicates that this particular declaration represents
+ // a specialized definition of the particular function for the given
+ // target, and we need to reflect that at the IR level.
+
+ auto targetName = targetMod->targetToken.getContent();
+ auto targetCap = findCapabilityAtom(targetName);
+
+ getBuilder()->addTargetDecoration(inst, CapabilitySet(targetCap));
+ }
+ }
+
// Attach target-intrinsic decorations to an instruction,
// based on modifiers on an AST declaration.
void addTargetIntrinsicDecorations(
@@ -9170,19 +9187,7 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
getBuilder()->addHighLevelDeclDecoration(irFunc, decl);
- // If this declaration was marked as being an intrinsic for a particular
- // target, then we should reflect that here.
- for (auto targetMod : decl->getModifiersOfType<SpecializedForTargetModifier>())
- {
- // `targetMod` indicates that this particular declaration represents
- // a specialized definition of the particular function for the given
- // target, and we need to reflect that at the IR level.
-
- auto targetName = targetMod->targetToken.getContent();
- auto targetCap = findCapabilityAtom(targetName);
-
- getBuilder()->addTargetDecoration(irFunc, CapabilitySet(targetCap));
- }
+ addSpecializedForTargetDecorations(irFunc, decl);
// If this declaration was marked as having a target-specific lowering
// for a particular target, then handle that here.
diff --git a/source/slang/slang-reflection-api.cpp b/source/slang/slang-reflection-api.cpp
index a52b7b886..9d687245f 100644
--- a/source/slang/slang-reflection-api.cpp
+++ b/source/slang/slang-reflection-api.cpp
@@ -935,6 +935,14 @@ SLANG_API SlangInt spReflectionTypeLayout_findFieldIndexByName(SlangReflectionTy
return -1;
}
+SLANG_API SlangReflectionVariableLayout* spReflectionTypeLayout_GetExplicitCounter(SlangReflectionTypeLayout* inTypeLayout)
+{
+ const auto typeLayout = convert(inTypeLayout);
+ if(const auto structuredBufferTypeLayout = as<StructuredBufferTypeLayout>(typeLayout))
+ return (SlangReflectionVariableLayout*) structuredBufferTypeLayout->counterVarLayout.Ptr();
+ return nullptr;
+}
+
SLANG_API size_t spReflectionTypeLayout_GetElementStride(SlangReflectionTypeLayout* inTypeLayout, SlangParameterCategory category)
{
auto typeLayout = convert(inTypeLayout);
@@ -1784,6 +1792,75 @@ namespace Slang
m_extendedInfo->m_bindingRanges.add(bindingRange);
m_extendedInfo->m_subObjectRanges.add(subObjectRange);
}
+ else if(const auto structuredBufferTypeLayout = as<StructuredBufferTypeLayout>(typeLayout))
+ {
+ // For structured buffers we expect them to consume a single
+ // resource descriptor slot (not counting the possible counter
+ // buffer)
+ SLANG_ASSERT(typeLayout->resourceInfos.getCount() == 1);
+ const auto& resInfo = typeLayout->resourceInfos[0];
+
+ const auto bindingType = as<HLSLStructuredBufferType>(typeLayout->getType())
+ ? SLANG_BINDING_TYPE_RAW_BUFFER
+ : SLANG_BINDING_TYPE_MUTABLE_RAW_BUFFER;
+
+ // We now allocate a descriptor range for this buffer
+ TypeLayout::ExtendedInfo::DescriptorRangeInfo descriptorRange;
+ descriptorRange.kind = resInfo.kind;
+ descriptorRange.bindingType = bindingType;
+ // Note that we don't use resInfo.count here, as each
+ // structuredBufferType is essentially a struct of 2 fields
+ // (elements, counter) and not an array of length 2.
+ SLANG_ASSERT(resInfo.count != 2 || structuredBufferTypeLayout->counterVarLayout);
+ SLANG_ASSERT(resInfo.count != 1 || !structuredBufferTypeLayout->counterVarLayout);
+ descriptorRange.count = multiplier;
+ descriptorRange.indexOffset = _calcIndexOffset(path.primary, resInfo.kind);
+
+ Int descriptorSetIndex = _findOrAddDescriptorSet(_calcSpaceOffset(path.primary, resInfo.kind));
+ const RefPtr<TypeLayout::ExtendedInfo::DescriptorSetInfo> descriptorSet
+ = m_extendedInfo->m_descriptorSets[descriptorSetIndex];
+ auto descriptorRangeIndex = descriptorSet->descriptorRanges.getCount();
+ descriptorSet->descriptorRanges.add(descriptorRange);
+
+ // We will map the elements buffer to a single binding range
+ TypeLayout::ExtendedInfo::BindingRangeInfo bindingRange;
+ bindingRange.leafTypeLayout = typeLayout;
+ bindingRange.leafVariable = path.primary ? path.primary->var->getVariable() : nullptr;
+ bindingRange.bindingType = bindingType;
+ bindingRange.count = multiplier;
+ bindingRange.descriptorSetIndex = descriptorSetIndex;
+ bindingRange.firstDescriptorRangeIndex = descriptorRangeIndex;
+ bindingRange.descriptorRangeCount = 1;
+
+ auto bindingRangeIndex = m_extendedInfo->m_bindingRanges.getCount();
+ m_extendedInfo->m_bindingRanges.add(bindingRange);
+
+ // We also make sure to report it as a sub-object range.
+ TypeLayout::ExtendedInfo::SubObjectRangeInfo subObjectRange;
+ subObjectRange.bindingRangeIndex = bindingRangeIndex;
+ subObjectRange.offsetVarLayout = createOffsetVarLayout(typeLayout, path);
+ subObjectRange.spaceOffset = 0;
+ m_extendedInfo->m_subObjectRanges.add(subObjectRange);
+
+ // If we have an associated counter for this structured buffer,
+ // add its ranges
+ if(structuredBufferTypeLayout->counterVarLayout)
+ {
+ ExtendedBindingRangePath counterPath(
+ path,
+ structuredBufferTypeLayout->counterVarLayout
+ );
+ // This should always be 1, because it comes after the
+ // single binding range we just added
+ structuredBufferTypeLayout->counterVarLayout->bindingRangeOffset =
+ m_extendedInfo->m_bindingRanges.getCount() - bindingRangeIndex;
+ addRangesRec(
+ structuredBufferTypeLayout->counterVarLayout->typeLayout,
+ counterPath,
+ multiplier
+ );
+ }
+ }
else
{
// Here we have the catch-all case that handles "leaf" fields
@@ -1940,19 +2017,7 @@ namespace Slang
bindingRange.descriptorRangeCount++;
}
- auto bindingRangeIndex = m_extendedInfo->m_bindingRanges.getCount();
-
m_extendedInfo->m_bindingRanges.add(bindingRange);
-
- // For `StructuredBuffer` fields, we also make sure to report it as a sub-object range.
- if (const auto structuredBufferTypeLayout = as<StructuredBufferTypeLayout>(typeLayout))
- {
- TypeLayout::ExtendedInfo::SubObjectRangeInfo subObjectRange;
- subObjectRange.bindingRangeIndex = bindingRangeIndex;
- subObjectRange.offsetVarLayout = createOffsetVarLayout(typeLayout, path);
- subObjectRange.spaceOffset = 0;
- m_extendedInfo->m_subObjectRanges.add(subObjectRange);
- }
}
}
};
@@ -2298,6 +2363,22 @@ SLANG_API SlangInt spReflectionTypeLayout_getFieldBindingRangeOffset(SlangReflec
return 0;
}
+SLANG_API SlangInt spReflectionTypeLayout_getExplicitCounterBindingRangeOffset(SlangReflectionTypeLayout* inTypeLayout)
+{
+ auto typeLayout = convert(inTypeLayout);
+ if(!typeLayout) return 0;
+
+ if(const auto structuredBufferTypeLayout = as<StructuredBufferTypeLayout>(typeLayout))
+ {
+ getExtendedTypeLayout(structuredBufferTypeLayout);
+ return structuredBufferTypeLayout->counterVarLayout
+ ? structuredBufferTypeLayout->counterVarLayout->bindingRangeOffset
+ : 0;
+ }
+
+ return 0;
+}
+
#if 0
SLANG_API SlangInt spReflectionTypeLayout_getSubObjectRangeCount(SlangReflectionTypeLayout* inTypeLayout)
{
diff --git a/source/slang/slang-type-layout.cpp b/source/slang/slang-type-layout.cpp
index 978fa6fbb..1534b6cc5 100644
--- a/source/slang/slang-type-layout.cpp
+++ b/source/slang/slang-type-layout.cpp
@@ -228,6 +228,11 @@ struct DefaultLayoutRulesImpl : SimpleLayoutRulesImpl
// and instead it will be injected in the concrete implementations
// that require it.
}
+
+ bool DoStructuredBuffersNeedSeparateCounterBuffer() override
+ {
+ return true;
+ }
};
/// Common behavior for GLSL-family layout.
@@ -600,6 +605,14 @@ struct HLSLStructuredBufferLayoutRulesImpl : DefaultLayoutRulesImpl
// but retain the rules around not adjusting the size of an array or
// structure to its alignment. In this way they should match our
// default layout rules.
+ //
+ // DirectX does however allow transparently managing the counter buffer
+ // resource for StructuredBuffers.
+
+ bool DoStructuredBuffersNeedSeparateCounterBuffer() override
+ {
+ return false;
+ }
};
struct DefaultVaryingLayoutRulesImpl : DefaultLayoutRulesImpl
@@ -733,8 +746,7 @@ struct GLSLObjectLayoutRulesImpl : ObjectLayoutRulesImpl
// In Vulkan GLSL, pretty much every object is just a descriptor-table slot.
// Except for AppendConsumeStructuredBuffer, which takes two slots.
- if (kind == ShaderParameterKind::AppendConsumeStructuredBuffer)
- slotCount = 2;
+ // This, however, is added in 'createStructuredBufferWithCounterTypeLayout'
if (options.hlslToVulkanKindFlags)
{
@@ -2785,6 +2797,49 @@ RefPtr<TypeLayout> createParameterGroupTypeLayout(
elementTypeRules);
}
+// Create a type layout for a structured buffer type with an associated counter
+RefPtr<StructuredBufferTypeLayout>
+createStructuredBufferWithCounterTypeLayout(
+ TypeLayoutContext const& context,
+ ShaderParameterKind kind,
+ Type* structuredBufferType,
+ RefPtr<TypeLayout> elementTypeLayout)
+{
+ auto typeLayout = createStructuredBufferTypeLayout(context, kind, structuredBufferType, elementTypeLayout);
+
+ const auto structuredBufferLayoutRules = context.getRulesFamily()->getStructuredBufferRules(context.targetReq);
+
+ const auto counterType = context.astBuilder->getIntType();
+ const auto counterBufferType = context.astBuilder->getRWStructuredBufferType(counterType);
+ const auto counterTypeLayout = createTypeLayoutWith(context, structuredBufferLayoutRules, counterBufferType);
+
+ const auto counterVarDecl = context.astBuilder->create<VarDecl>();
+ counterVarDecl->type.type = counterBufferType;
+ counterVarDecl->nameAndLoc.name =
+ context.astBuilder->getSharedASTBuilder()->getNamePool()->getName("counter");
+
+ RefPtr<VarLayout> counterVarLayout = new VarLayout();
+ counterVarLayout->varDecl = makeDeclRef(counterVarDecl);
+ counterVarLayout->typeLayout = counterTypeLayout;
+
+ for(auto& typeResourceInfo : typeLayout->resourceInfos)
+ {
+ const auto counterResourceInfo
+ = counterVarLayout->findOrAddResourceInfo(typeResourceInfo.kind);
+ const auto counterTypeResourceInfo
+ = counterVarLayout->getTypeLayout()->FindResourceInfo(typeResourceInfo.kind);
+ // We expect this index to be 1
+ counterResourceInfo->index = typeResourceInfo.count.getFiniteValue();
+ // likewise
+ typeResourceInfo.count += counterTypeResourceInfo->count;
+ }
+
+ typeLayout->counterVarLayout = counterVarLayout;
+ typeLayout->addResourceUsageFrom(counterTypeLayout);
+
+ return typeLayout;
+}
+
// Create a type layout for a structured buffer type.
RefPtr<StructuredBufferTypeLayout>
createStructuredBufferTypeLayout(
@@ -2845,12 +2900,24 @@ createStructuredBufferTypeLayout(
structuredBufferLayoutRules,
elementType);
- return createStructuredBufferTypeLayout(
- context,
- kind,
- structuredBufferType,
- elementTypeLayout);
-
+ if(kind == ShaderParameterKind::AppendConsumeStructuredBuffer
+ && structuredBufferLayoutRules->DoStructuredBuffersNeedSeparateCounterBuffer())
+ {
+ return createStructuredBufferWithCounterTypeLayout(
+ context,
+ kind,
+ structuredBufferType,
+ elementTypeLayout
+ );
+ }
+ else
+ {
+ return createStructuredBufferTypeLayout(
+ context,
+ kind,
+ structuredBufferType,
+ elementTypeLayout);
+ }
}
/// Create layout information for the given `type`.
diff --git a/source/slang/slang-type-layout.h b/source/slang/slang-type-layout.h
index e3dd719d6..1ebd26098 100644
--- a/source/slang/slang-type-layout.h
+++ b/source/slang/slang-type-layout.h
@@ -628,6 +628,10 @@ class StructuredBufferTypeLayout : public TypeLayout
{
public:
RefPtr<TypeLayout> elementTypeLayout;
+ // If required, this is the VarLayout for a buffer which contains the
+ // counter associated with the buffer, most often used for
+ // AppendStructuredBuffer or ConsumeStructuredBuffer
+ RefPtr<VarLayout> counterVarLayout;
};
/// Type layout for a logical sequence type
@@ -962,6 +966,10 @@ struct SimpleLayoutRulesImpl
// End layout for a struct, and finalize its size/alignment.
virtual void EndStructLayout(UniformLayoutInfo* ioStructInfo) = 0;
+
+ // Do structured buffers need a separate binding for the counter buffer?
+ // (DirectX is the exception in managing these together)
+ virtual bool DoStructuredBuffersNeedSeparateCounterBuffer() = 0;
};
struct ObjectLayoutRulesImpl
@@ -1021,6 +1029,13 @@ struct LayoutRulesImpl
return simpleRules->EndStructLayout(ioStructInfo);
}
+ // Do structured buffers need a separate binding for the counter buffer?
+ // (DirectX is the exception in managing these together)
+ bool DoStructuredBuffersNeedSeparateCounterBuffer()
+ {
+ return simpleRules->DoStructuredBuffersNeedSeparateCounterBuffer();
+ }
+
// Forward `ObjectLayoutRulesImpl` interface
SimpleLayoutInfo GetObjectLayout(ShaderParameterKind kind, const ObjectLayoutRulesImpl::Options& options)
@@ -1306,6 +1321,13 @@ createStructuredBufferTypeLayout(
Type* structuredBufferType,
Type* elementType);
+RefPtr<StructuredBufferTypeLayout>
+createStructuredBufferTypeLayout(
+ TypeLayoutContext const& context,
+ ShaderParameterKind kind,
+ Type* structuredBufferType,
+ RefPtr<TypeLayout> elementTypeLayout);
+
/// Create a type layout for an unspecialized `globalGenericParamDecl`.
RefPtr<TypeLayout> createTypeLayoutForGlobalGenericTypeParam(
TypeLayoutContext const& context,
diff --git a/tests/hlsl/append-structured-buffer.slang b/tests/hlsl/append-structured-buffer.slang
index 52c657b86..5ec7f844a 100644
--- a/tests/hlsl/append-structured-buffer.slang
+++ b/tests/hlsl/append-structured-buffer.slang
@@ -1,43 +1,42 @@
+//TEST(compute):COMPARE_COMPUTE_EX(filecheck-buffer=BUF):-dx12 -use-dxil -compute -output-using-type
+//TEST(compute, vulkan):COMPARE_COMPUTE_EX(filecheck-buffer=BUF):-vk -compute -output-using-type
+//TEST(compute, vulkan):COMPARE_COMPUTE_EX(filecheck-buffer=BUF):-vk -compute -output-using-type -emit-spirv-directly
-//TEST:SIMPLE(filecheck=GLSL):-target glsl -profile glsl_450 -stage compute -entry computeMain -fvk-u-shift 10 0
-//TEST:SIMPLE(filecheck=SPIRV):-target spirv -profile glsl_450 -stage compute -entry computeMain
+// To check that our counter-initialization works correctly, set the initial
+// counter to 1 instead of 0
+//TEST_INPUT:ubuffer(data=[0 0 0 0 0 0 0 0], stride=4, counter=1):out,name=outputBuffer
+AppendStructuredBuffer<int> outputBuffer;
-//DISABLED_TEST(compute, vulkan):COMPARE_COMPUTE_EX:-vk -compute -shaderobj -output-using-type -xslang -fvk-use-gl-layout
+//TEST_INPUT:set inBuffer = ubuffer(data=[1 2 3 4], stride=4)
+RWStructuredBuffer<int> inBuffer;
-//TEST_INPUT:ubuffer(data=[0], stride=4):out,name=outputBuffer
-RWStructuredBuffer<float> outputBuffer;
-
-AppendStructuredBuffer<float2> appendBuffer;
-
-// GLSL: layout(std430, binding = 11) buffer StructuredBuffer_float2_t
-// GLSL: vec2 _data[];
-// GLSL: } appendBuffer_elements_0
-
-// GLSL: layout(std430, binding = 12) buffer StructuredBuffer_int_t
-// GLSL: int _data[];
-// GLSL: } appendBuffer_counter
-
-// GLSL: void AppendStructuredBuffer_Append_0(vec2 [[PARAM:[A-Za-z0-9_]+]])
-// GLSL: int [[COUNTER:[A-Za-z0-9_]+]] = atomicAdd(appendBuffer_counter_0._data[0], 1);
-// GLSL: appendBuffer_elements_0._data{{\[}}[[COUNTER]]{{\]}} = [[PARAM]];
-
-// GLSL: uvec2 StructuredBuffer_GetDimensions_0()
-// GLSL: {
-// GLSL: return uvec2(uint(appendBuffer_counter_0._data[0]), 8U);
-// GLSL: }
-
-// SPIRV: OpEntryPoint
-
-//TEST_INPUT:set inBuffer = ubuffer(data=[1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0], stride=4)
-RWByteAddressBuffer inBuffer;
-
-[numthreads(1, 1, 1)]
-void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID)
+[numthreads(4, 1, 1)]
+void computeMain(uint i : SV_GroupIndex)
{
- var g = inBuffer.Load<float4>(4);
- appendBuffer.Append(g.xy);
+ int g = inBuffer[i];
+ outputBuffer.Append(g);
+
+ GroupMemoryBarrier();
uint numStructs, stride;
- appendBuffer.GetDimensions(numStructs, stride);
- outputBuffer[dispatchThreadID.x] = numStructs; // expect 1.0
+ outputBuffer.GetDimensions(numStructs, stride);
+ if(i == 0)
+ outputBuffer.Append(int(numStructs));
+
+ // BUF: type: int32_t
+ // Never assigned, as we set the initial counter to 1
+ // BUF: 0
+
+ // The values from inBuffer in any order
+ // BUF-DAG: 1
+ // BUF-DAG: 3
+ // BUF-DAG: 2
+ // BUF-DAG: 4
+
+ // The total size of the AppendStructuredBuffer (from GetDimensions)
+ // BUF: 8
+
+ // Never assigned
+ // BUF: 0
+ // BUF: 0
}
diff --git a/tests/hlsl/consume-structured-buffer.slang b/tests/hlsl/consume-structured-buffer.slang
index 3027b4184..352fd6dac 100644
--- a/tests/hlsl/consume-structured-buffer.slang
+++ b/tests/hlsl/consume-structured-buffer.slang
@@ -1,35 +1,33 @@
+//TEST(compute):COMPARE_COMPUTE_EX(filecheck-buffer=BUF):-dx12 -use-dxil -compute -output-using-type
+//TEST(compute, vulkan):COMPARE_COMPUTE_EX(filecheck-buffer=BUF):-vk -compute -output-using-type
+//TEST(compute, vulkan):COMPARE_COMPUTE_EX(filecheck-buffer=BUF):-vk -compute -output-using-type -emit-spirv-directly
-//TEST:SIMPLE(filecheck=GLSL):-target glsl -profile glsl_450 -stage compute -entry computeMain
-//TEST:SIMPLE(filecheck=SPIRV):-target spirv -profile glsl_450 -stage compute -entry computeMain
-
-//DISABLED_TEST(compute, vulkan):COMPARE_COMPUTE_EX:-vk -compute -shaderobj -output-using-type -xslang -fvk-use-gl-layout
-
-//TEST_INPUT:ubuffer(data=[0], stride=4):out,name=outputBuffer
+//TEST_INPUT:ubuffer(data=[0 0 0 0 0], stride=4):out,name=outputBuffer
RWStructuredBuffer<float> outputBuffer;
-ConsumeStructuredBuffer<float2> consumeBuffer;
+// To check that our counter-initialization works correctly, set the initial
+// counter to 6 instead of 8, so we'll start reading from 6.0 downwards
+//TEST_INPUT:set consumeBuffer = ubuffer(data=[1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0], stride=4, counter=6)
+ConsumeStructuredBuffer<float> consumeBuffer;
-// GLSL: layout(std430, binding = 1) buffer StructuredBuffer_float2_t
-// GLSL: vec2 _data[];
-// GLSL: } consumeBuffer_elements_0
+[numthreads(4, 1, 1)]
+void computeMain(uint i : SV_GroupIndex)
+{
+ uint size, stride;
+ consumeBuffer.GetDimensions(size, stride);
-// GLSL: layout(std430, binding = 2) buffer StructuredBuffer_int_t
-// GLSL: int _data[];
-// GLSL: } consumeBuffer_counter
+ if(i == 0)
+ outputBuffer[0] = size;
-// GLSL: vec2 ConsumeStructuredBuffer_Consume_0()
-// GLSL: int [[COUNTER:[A-Za-z0-9_]+]] = atomicAdd(consumeBuffer_counter_0._data[0], -1);
-// GLSL: int [[COUNTER1:[A-Za-z0-9_]+]] = [[COUNTER]] - 1;
-// GLSL: if{{\s?}}([[COUNTER1]] >= 0)
-// GLSL: return consumeBuffer_elements_0._data{{\[}}[[COUNTER1]]{{\]}};
-// GLSL: else
-// GLSL: return vec2(0.0);
+ outputBuffer[i+1] = consumeBuffer.Consume();
-// SPIRV: OpEntryPoint
+ // BUF: type: float
+ // The total size of the ConsumeStructuredBuffer (from GetDimensions)
+ // BUF: 8.0
-[numthreads(1, 1, 1)]
-void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID)
-{
- var v = consumeBuffer.Consume();
- outputBuffer[dispatchThreadID.x] = v.x; // expect 1.0
+ // The values from consumeBuffer in any order
+ // BUF-DAG: 6.0
+ // BUF-DAG: 4.0
+ // BUF-DAG: 5.0
+ // BUF-DAG: 3.0
}
diff --git a/tools/gfx-util/shader-cursor.cpp b/tools/gfx-util/shader-cursor.cpp
index 5a2cdfd33..c849673bf 100644
--- a/tools/gfx-util/shader-cursor.cpp
+++ b/tools/gfx-util/shader-cursor.cpp
@@ -20,6 +20,58 @@ Result gfx::ShaderCursor::getDereferenced(ShaderCursor& outCursor) const
}
}
+ShaderCursor ShaderCursor::getExplicitCounter() const
+{
+ // Similar to getField below
+
+ // The alternative to handling this here would be to augment IResourceView
+ // with a `getCounterResourceView()`, and set that also in `setResource`
+ if(const auto counterVarLayout = m_typeLayout->getExplicitCounter())
+ {
+ ShaderCursor counterCursor;
+
+ // The counter cursor will point into the same parent object.
+ counterCursor.m_baseObject = m_baseObject;
+
+ // The type being pointed to is the type of the field.
+ counterCursor.m_typeLayout = counterVarLayout->getTypeLayout();
+
+ // The byte offset is the current offset plus the relative offset of the counter.
+ // The offset in binding ranges is computed similarly.
+ counterCursor.m_offset.uniformOffset
+ = m_offset.uniformOffset + SlangInt(counterVarLayout->getOffset());
+ counterCursor.m_offset.bindingRangeIndex
+ = m_offset.bindingRangeIndex + GfxIndex(m_typeLayout->getExplicitCounterBindingRangeOffset());
+
+ // The index of the counter within any binding ranges will be the same
+ // as the index computed for the parent structure.
+ //
+ // Note: this case would arise for an array of structured buffers
+ //
+ // AppendStructuredBuffer g[4];
+ //
+ // In this scenario, `g` holds two binding ranges:
+ //
+ // * Range #0 comprises 4 element buffers, representing `g[...].elements`
+ // * Range #1 comprises 4 counter buffers, representing `g[...].counter`
+ //
+ // A cursor for `g[2]` would have a `bindingRangeIndex` of zero but
+ // a `bindingArrayIndex` of 2, indicating that we could end up
+ // referencing either range, but no matter what we know the index
+ // is 2. Thus when we form a cursor for `g[2].counter` we want to
+ // apply the binding range offset to get a `bindingRangeIndex` of
+ // 1, while the `bindingArrayIndex` is unmodified.
+ //
+ // The result is that `g[2].counter` is stored in range #1 at array index 2.
+ //
+ counterCursor.m_offset.bindingArrayIndex = m_offset.bindingArrayIndex;
+
+ return counterCursor;
+ }
+ // Otherwise, return an invalid cursor
+ return ShaderCursor{};
+}
+
Result ShaderCursor::getField(const char* name, const char* nameEnd, ShaderCursor& outCursor) const
{
// If this cursor is invalid, then can't possible fetch a field.
diff --git a/tools/gfx-util/shader-cursor.h b/tools/gfx-util/shader-cursor.h
index 5dfc57f6d..3d35bb2ed 100644
--- a/tools/gfx-util/shader-cursor.h
+++ b/tools/gfx-util/shader-cursor.h
@@ -63,6 +63,13 @@ struct ShaderCursor
return cursor;
}
+ /// Some resources such as RWStructuredBuffer, AppendStructuredBuffer and
+ /// ConsumeStructuredBuffer need to have their counter explicitly bound on
+ /// APIs other than DirectX, this will return a valid ShaderCursor pointing
+ /// to that resource if that is the case.
+ /// Otherwise, this returns an invalid cursor.
+ ShaderCursor getExplicitCounter() const;
+
ShaderCursor getElement(GfxIndex index) const;
static Result followPath(const char* path, ShaderCursor& ioCursor);
diff --git a/tools/gfx/d3d12/d3d12-device.cpp b/tools/gfx/d3d12/d3d12-device.cpp
index 983f93bce..eb9e597bf 100644
--- a/tools/gfx/d3d12/d3d12-device.cpp
+++ b/tools/gfx/d3d12/d3d12-device.cpp
@@ -1665,9 +1665,11 @@ Result DeviceImpl::createBufferView(
{
auto resourceImpl = (BufferResourceImpl*)buffer;
auto resourceDesc = *resourceImpl->getDesc();
+ const auto counterResourceImpl = static_cast<BufferResourceImpl*>(counterBuffer);
RefPtr<ResourceViewImpl> viewImpl = new ResourceViewImpl();
viewImpl->m_resource = resourceImpl;
+ viewImpl->m_counterResource = counterResourceImpl;
viewImpl->m_desc = desc;
switch (desc.type)
@@ -1722,7 +1724,6 @@ Result DeviceImpl::createBufferView(
}
else
{
- auto counterResourceImpl = static_cast<BufferResourceImpl*>(counterBuffer);
SLANG_RETURN_ON_FAIL(m_cpuViewHeap->allocate(&viewImpl->m_descriptor));
viewImpl->m_allocator = m_cpuViewHeap;
m_device->CreateUnorderedAccessView(
diff --git a/tools/gfx/d3d12/d3d12-resource-views.h b/tools/gfx/d3d12/d3d12-resource-views.h
index 12e3d0714..fd3f44116 100644
--- a/tools/gfx/d3d12/d3d12-resource-views.h
+++ b/tools/gfx/d3d12/d3d12-resource-views.h
@@ -26,6 +26,8 @@ class ResourceViewImpl
{
public:
Slang::RefPtr<Resource> m_resource;
+ // null, unless this is a structuredbuffer with a separate counter buffer
+ Slang::RefPtr<Resource> m_counterResource;
virtual SLANG_NO_THROW Result SLANG_MCALL getNativeHandle(InteropHandle* outHandle) override;
};
diff --git a/tools/gfx/d3d12/d3d12-shader-object.cpp b/tools/gfx/d3d12/d3d12-shader-object.cpp
index c5389f6ea..74760eabd 100644
--- a/tools/gfx/d3d12/d3d12-shader-object.cpp
+++ b/tools/gfx/d3d12/d3d12-shader-object.cpp
@@ -181,7 +181,11 @@ Result ShaderObjectImpl::init(
// referenced by descriptors in this object does not get
// freed while the object is still live.
//
+ // The doubling here is because any buffer resource could
+ // have a counter buffer associated with it, which we
+ // also need to ensure isn't destroyed prematurely.
m_boundResources.setCount(resourceCount);
+ m_boundCounterResources.setCount(resourceCount);
}
if (auto samplerCount = layout->getSamplerSlotCount())
{
@@ -950,8 +954,9 @@ Result ShaderObjectImpl::setResource(ShaderOffset const& offset, IResourceView*
{
auto resourceViewImpl = static_cast<ResourceViewImpl*>(resourceView);
// Hold a reference to the resource to prevent its destruction.
- m_boundResources[bindingRange.baseIndex + offset.bindingArrayIndex] =
- resourceViewImpl->m_resource;
+ const auto resourceOffset = bindingRange.baseIndex + offset.bindingArrayIndex;
+ m_boundResources[resourceOffset] = resourceViewImpl->m_resource;
+ m_boundCounterResources[resourceOffset] = resourceViewImpl->m_counterResource;
internalResourceView = resourceViewImpl;
}
break;
diff --git a/tools/gfx/d3d12/d3d12-shader-object.h b/tools/gfx/d3d12/d3d12-shader-object.h
index d6560628c..84d305ae7 100644
--- a/tools/gfx/d3d12/d3d12-shader-object.h
+++ b/tools/gfx/d3d12/d3d12-shader-object.h
@@ -197,6 +197,7 @@ public:
DescriptorSet m_cachedGPUDescriptorSet;
ShortList<RefPtr<Resource>, 8> m_boundResources;
+ ShortList<RefPtr<Resource>, 8> m_boundCounterResources;
List<D3D12_GPU_VIRTUAL_ADDRESS> m_rootArguments;
/// A constant buffer used to stored ordinary data for this object
/// and existential-type sub-objects.
diff --git a/tools/render-test/render-test-main.cpp b/tools/render-test/render-test-main.cpp
index 3bdcdba07..1f2b3a5ae 100644
--- a/tools/render-test/render-test-main.cpp
+++ b/tools/render-test/render-test-main.cpp
@@ -209,11 +209,49 @@ struct AssignValsFromLayoutContext
ComPtr<IBufferResource> bufferResource;
SLANG_RETURN_ON_FAIL(ShaderRendererUtil::createBufferResource(srcBuffer, /*entry.isOutput,*/ bufferSize, bufferData.getBuffer(), device, bufferResource));
+ ComPtr<IBufferResource> counterResource;
+ const auto explicitCounterCursor = dstCursor.getExplicitCounter();
+ if(srcBuffer.counter != ~0u)
+ {
+ if(explicitCounterCursor.isValid())
+ {
+ // If this cursor has a full buffer object associated with the
+ // resource, then assign to that.
+ ShaderInputLayout::BufferVal counterVal;
+ counterVal.bufferData.add(srcBuffer.counter);
+ assignBuffer(explicitCounterCursor, &counterVal);
+ }
+ else
+ {
+ // Otherwise, this API (D3D) must be handling the buffer object
+ // specially, in which case create the buffer resource to pass
+ // into `createBufferView`
+ const InputBufferDesc& counterBufferDesc{
+ InputBufferType::StorageBuffer,
+ sizeof(uint32_t),
+ Format::Unknown,
+ };
+ SLANG_RETURN_ON_FAIL(ShaderRendererUtil::createBufferResource(
+ counterBufferDesc,
+ sizeof(srcBuffer.counter),
+ &srcBuffer.counter,
+ device,
+ counterResource
+ ));
+ }
+ }
+ else if(explicitCounterCursor.isValid())
+ {
+ // If we know we require a counter for this resource but haven't
+ // been given one, error
+ return SLANG_E_INVALID_ARG;
+ }
+
IResourceView::Desc viewDesc = {};
viewDesc.type = IResourceView::Type::UnorderedAccess;
viewDesc.format = srcBuffer.format;
viewDesc.bufferElementSize = srcVal->bufferDesc.stride;
- auto bufferView = device->createBufferView(bufferResource, nullptr, viewDesc);
+ auto bufferView = device->createBufferView(bufferResource, counterResource, viewDesc);
dstCursor.setResource(bufferView);
maybeAddOutput(dstCursor, srcVal, bufferResource);
@@ -977,7 +1015,7 @@ Result RenderTestApp::writeBindingOutput(const String& fileName)
m_transientHeap->finish();
m_transientHeap->synchronizeAndReset();
- m_device->readBufferResource(stagingBuffer, 0, bufferSize, blob.writeRef());
+ SLANG_RETURN_ON_FAIL(m_device->readBufferResource(stagingBuffer, 0, bufferSize, blob.writeRef()));
}
if (!blob)
diff --git a/tools/render-test/shader-input-layout.cpp b/tools/render-test/shader-input-layout.cpp
index b8c505607..80ce4e316 100644
--- a/tools/render-test/shader-input-layout.cpp
+++ b/tools/render-test/shader-input-layout.cpp
@@ -203,6 +203,11 @@ namespace renderer_test
parser.Read("=");
val->bufferDesc.stride = parser.ReadInt();
}
+ else if (word == "counter")
+ {
+ parser.Read("=");
+ val->bufferDesc.counter = parser.ReadInt();
+ }
else if (word == "random")
{
parser.Read("(");
diff --git a/tools/render-test/shader-input-layout.h b/tools/render-test/shader-input-layout.h
index 78d545114..2803d1915 100644
--- a/tools/render-test/shader-input-layout.h
+++ b/tools/render-test/shader-input-layout.h
@@ -58,6 +58,10 @@ struct InputBufferDesc
InputBufferType type = InputBufferType::StorageBuffer;
int stride = 0; // stride == 0 indicates an unstructured buffer.
Format format = Format::Unknown;
+ // For RWStructuredBuffer, AppendStructuredBuffer, ConsumeStructuredBuffer
+ // the default value of 0xffffffff indicates that a counter buffer should
+ // not be assigned
+ uint32_t counter = ~0u;
};
struct InputSamplerDesc