summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--examples/model-viewer/main.cpp35
-rw-r--r--examples/model-viewer/shaders.slang8
-rw-r--r--slang.h10
-rw-r--r--source/core/slang-string.h2
-rw-r--r--source/slang/slang-ir-legalize-types.cpp4
-rw-r--r--source/slang/slang-ir-lower-tuple-types.cpp15
-rw-r--r--source/slang/slang-reflection-api.cpp214
-rw-r--r--source/slang/slang-type-layout.cpp13
-rw-r--r--source/slang/slang-type-layout.h6
-rw-r--r--tests/compute/interface-shader-param-in-struct.slang6
-rw-r--r--tests/language-feature/shader-params/interface-shader-param-ordinary.slang53
-rw-r--r--tests/language-feature/shader-params/interface-shader-param-ordinary.slang.expected.txt4
-rw-r--r--tools/gfx/d3d11/render-d3d11.cpp965
-rw-r--r--tools/gfx/d3d12/render-d3d12.cpp1070
-rw-r--r--tools/gfx/open-gl/render-gl.cpp16
-rw-r--r--tools/gfx/renderer-shared.cpp27
-rw-r--r--tools/gfx/vulkan/render-vk.cpp1901
17 files changed, 3345 insertions, 1004 deletions
diff --git a/examples/model-viewer/main.cpp b/examples/model-viewer/main.cpp
index f6aab4c4a..6e21f9050 100644
--- a/examples/model-viewer/main.cpp
+++ b/examples/model-viewer/main.cpp
@@ -151,7 +151,7 @@ struct SimpleMaterial : Material
{
glm::vec3 diffuseColor;
glm::vec3 specularColor;
- float specularity;
+ float specularity = 1.0f;
// Create a shader object that contains the type info and parameter values
// that represent an instance of `SimpleMaterial`.
@@ -282,16 +282,17 @@ RefPtr<Model> loadModel(
struct Light : RefObject
{
- // A light must be able to create a shader object defining its
- // corresponding shader type and parameter values.
- virtual IShaderObject* createShaderObject(RendererContext* context) = 0;
+ // A light must be able to write its state into a shader parameters
+ // of the matching Slang type.
+ //
+ virtual void writeTo(ShaderCursor const& cursor) = 0;
// Retrieves the shader type for this light object.
virtual slang::TypeReflection* getShaderType(RendererContext* context) = 0;
// The shader object for a light will be stashed here
// after it is created.
- ComPtr<IShaderObject> shaderObject;
+// ComPtr<IShaderObject> shaderObject;
};
// Helper function to retrieve the underlying shader type of `T`.
@@ -314,14 +315,10 @@ struct DirectionalLight : Light
static const char* getTypeName() { return "DirectionalLight"; }
- virtual IShaderObject* createShaderObject(RendererContext* context) override
+ virtual void writeTo(ShaderCursor const& cursor)
{
- auto shaderType = ::getShaderType<DirectionalLight>(context);
- shaderObject = context->device->createShaderObject(shaderType);
- gfx::ShaderCursor cursor(shaderObject);
cursor["direction"].setData(&direction, sizeof(direction));
cursor["intensity"].setData(&intensity, sizeof(intensity));
- return shaderObject.get();
}
virtual slang::TypeReflection* getShaderType(RendererContext* context) override
@@ -337,14 +334,10 @@ struct PointLight : Light
static const char* getTypeName() { return "PointLight"; }
- virtual IShaderObject* createShaderObject(RendererContext* context) override
+ virtual void writeTo(ShaderCursor const& cursor)
{
- auto shaderType = ::getShaderType<PointLight>(context);
- shaderObject = context->device->createShaderObject(shaderType);
- gfx::ShaderCursor cursor(shaderObject);
cursor["position"].setData(&position, sizeof(position));
cursor["intensity"].setData(&intensity, sizeof(intensity));
- return shaderObject.get();
}
virtual slang::TypeReflection* getShaderType(RendererContext* context) override
@@ -588,8 +581,7 @@ struct LightEnv : public RefObject
if (lightCount > 0)
{
- lightTypeCursor.setObject(
- lightTypeArray->lights[0]->createShaderObject(context));
+ lightTypeArray->lights[0]->writeTo(lightTypeCursor);
}
else
{
@@ -614,8 +606,7 @@ struct LightEnv : public RefObject
auto arrayCursor = lightTypeCursor["lights"];
for (size_t ii = 0; ii < lightCount; ++ii)
{
- arrayCursor[ii].setObject(
- lightTypeArray->lights[ii]->createShaderObject(context));
+ lightTypeArray->lights[ii]->writeTo(arrayCursor[ii]);
}
}
}
@@ -781,7 +772,11 @@ Result initialize()
lightEnvLayout->addLightType<DirectionalLight>(&context, 2);
lightEnv = new LightEnv(lightEnvLayout, &context);
- lightEnv->add(new PointLight());
+
+ RefPtr<PointLight> pointLight = new PointLight();
+ pointLight->position = glm::vec3(5, 3, 1);
+ pointLight->intensity = glm::vec3(10);
+ lightEnv->add(pointLight);
// Once we have created all our graphcis API and application resources,
// we can start to load models. For now we are keeping things extremely
diff --git a/examples/model-viewer/shaders.slang b/examples/model-viewer/shaders.slang
index 0005b427b..81ec89d03 100644
--- a/examples/model-viewer/shaders.slang
+++ b/examples/model-viewer/shaders.slang
@@ -85,7 +85,13 @@ struct BlinnPhong : IBRDF
float3 H = normalize(L + V);
float nDotH = saturate(dot(N, H));
- return kd*nDotL + ks*pow(nDotH, specularity);
+ // TODO: The current model loading has a bug that is leading
+ // to the `ks` and `specularity` fields being invalid garbage
+ // for our example cube, and the result is a non-finite value
+ // coming out of `evaluate()` if we include the specular term.
+
+// return kd*nDotL + ks*pow(nDotH, specularity);
+ return kd*nDotL;
}
};
//
diff --git a/slang.h b/slang.h
index 673d93d1e..f8f5c3073 100644
--- a/slang.h
+++ b/slang.h
@@ -1991,6 +1991,8 @@ extern "C"
SLANG_API SlangReflectionTypeLayout* spReflectionTypeLayout_getPendingDataTypeLayout(SlangReflectionTypeLayout* type);
SLANG_API SlangReflectionVariableLayout* spReflectionTypeLayout_getSpecializedTypePendingDataVarLayout(SlangReflectionTypeLayout* type);
+ SLANG_API SlangInt spReflectionType_getSpecializedTypeArgCount(SlangReflectionType* type);
+ SLANG_API SlangReflectionType* spReflectionType_getSpecializedTypeArgType(SlangReflectionType* type, SlangInt index);
SLANG_API SlangInt spReflectionTypeLayout_getBindingRangeCount(SlangReflectionTypeLayout* typeLayout);
SLANG_API SlangBindingType spReflectionTypeLayout_getBindingRangeType(SlangReflectionTypeLayout* typeLayout, SlangInt index);
@@ -2013,6 +2015,7 @@ extern "C"
SLANG_API SlangInt spReflectionTypeLayout_getSubObjectRangeCount(SlangReflectionTypeLayout* typeLayout);
SLANG_API SlangInt spReflectionTypeLayout_getSubObjectRangeBindingRangeIndex(SlangReflectionTypeLayout* typeLayout, SlangInt subObjectRangeIndex);
SLANG_API SlangInt spReflectionTypeLayout_getSubObjectRangeSpaceOffset(SlangReflectionTypeLayout* typeLayout, SlangInt subObjectRangeIndex);
+ SLANG_API SlangReflectionVariableLayout* spReflectionTypeLayout_getSubObjectRangeOffset(SlangReflectionTypeLayout* typeLayout, SlangInt subObjectRangeIndex);
#if 0
SLANG_API SlangInt spReflectionTypeLayout_getSubObjectRangeCount(SlangReflectionTypeLayout* typeLayout);
@@ -2695,6 +2698,13 @@ namespace slang
(SlangReflectionTypeLayout*) this,
subObjectRangeIndex);
}
+
+ VariableLayoutReflection* getSubObjectRangeOffset(SlangInt subObjectRangeIndex)
+ {
+ return (VariableLayoutReflection*) spReflectionTypeLayout_getSubObjectRangeOffset(
+ (SlangReflectionTypeLayout*) this,
+ subObjectRangeIndex);
+ }
};
struct Modifier
diff --git a/source/core/slang-string.h b/source/core/slang-string.h
index f42e78ac4..6b9a73bcb 100644
--- a/source/core/slang-string.h
+++ b/source/core/slang-string.h
@@ -279,7 +279,7 @@ namespace Slang
{
public:
UnownedTerminatedStringSlice(char const* b)
- : UnownedStringSlice(b, b + strlen(b))
+ : UnownedStringSlice(b, b + (b?strlen(b):0))
{}
};
diff --git a/source/slang/slang-ir-legalize-types.cpp b/source/slang/slang-ir-legalize-types.cpp
index 9b95e069d..1cd94decb 100644
--- a/source/slang/slang-ir-legalize-types.cpp
+++ b/source/slang/slang-ir-legalize-types.cpp
@@ -2435,7 +2435,7 @@ static LegalVal legalizeGlobalVar(
default:
{
- context->insertBeforeGlobal = irGlobalVar->getNextInst();
+ context->insertBeforeGlobal = irGlobalVar;
IRGlobalNameInfo globalNameInfo;
globalNameInfo.globalVar = irGlobalVar;
@@ -2480,7 +2480,7 @@ static LegalVal legalizeGlobalParam(
default:
{
- context->insertBeforeGlobal = irGlobalParam->getNextInst();
+ context->insertBeforeGlobal = irGlobalParam;
LegalVarChainLink varChain(LegalVarChain(), varLayout);
diff --git a/source/slang/slang-ir-lower-tuple-types.cpp b/source/slang/slang-ir-lower-tuple-types.cpp
index a155ae8d3..429058edf 100644
--- a/source/slang/slang-ir-lower-tuple-types.cpp
+++ b/source/slang/slang-ir-lower-tuple-types.cpp
@@ -121,6 +121,18 @@ namespace Slang
inst->removeAndDeallocate();
}
+ void processTupleType(IRTupleType* inst)
+ {
+ IRBuilder builderStorage;
+ auto builder = &builderStorage;
+ builder->sharedBuilder = &sharedBuilderStorage;
+ builder->setInsertBefore(inst);
+
+ auto loweredTupleInfo = getLoweredTupleType(builder, inst);
+ SLANG_ASSERT(loweredTupleInfo);
+ SLANG_UNUSED(loweredTupleInfo);
+ }
+
void processInst(IRInst* inst)
{
switch (inst->getOp())
@@ -131,6 +143,9 @@ namespace Slang
case kIROp_GetTupleElement:
processGetTupleElement((IRGetTupleElement*)inst);
break;
+ case kIROp_TupleType:
+ processTupleType((IRTupleType*)inst);
+ break;
default:
break;
}
diff --git a/source/slang/slang-reflection-api.cpp b/source/slang/slang-reflection-api.cpp
index e68efde06..d75920fdb 100644
--- a/source/slang/slang-reflection-api.cpp
+++ b/source/slang/slang-reflection-api.cpp
@@ -1056,22 +1056,104 @@ SLANG_API SlangReflectionVariableLayout* spReflectionTypeLayout_getSpecializedTy
}
}
+SLANG_API SlangInt spReflectionType_getSpecializedTypeArgCount(SlangReflectionType* inType)
+{
+ auto type = convert(inType);
+ if(!type) return 0;
+
+ auto specializedType = as<ExistentialSpecializedType>(type);
+ if(!specializedType) return 0;
+
+ return specializedType->args.getCount();
+}
+
+SLANG_API SlangReflectionType* spReflectionType_getSpecializedTypeArgType(SlangReflectionType* inType, SlangInt index)
+{
+ auto type = convert(inType);
+ if(!type) return nullptr;
+
+ auto specializedType = as<ExistentialSpecializedType>(type);
+ if(!specializedType) return nullptr;
+
+ if(index < 0) return nullptr;
+ if(index >= specializedType->args.getCount()) return nullptr;
+
+ auto argType = as<Type>(specializedType->args[index].val);
+ return convert(argType);
+}
+
namespace Slang
{
+ /// A link in a chain of `VarLayout`s that can be used to compute offset information for a nested field
struct BindingRangePathLink
{
+ BindingRangePathLink()
+ {}
+
BindingRangePathLink(
BindingRangePathLink* parent,
VarLayout* var)
- : parent(parent)
- , var(var)
+ : var(var)
+ , parent(parent)
{}
- BindingRangePathLink* parent;
- VarLayout* var;
+ /// The inner-most variable that contributes to the offset along this path
+ VarLayout* var = nullptr;
+
+ /// The next outer link along the path
+ BindingRangePathLink* parent = nullptr;
+ };
+
+ /// A path leading to some nested field, with both parimary and "pending" data offsets
+ struct BindingRangePath
+ {
+
+ /// The chain of variables that defines the "primary" offset of a nested field
+ BindingRangePathLink* primary = nullptr;
+
+ /// The chain of variables that defines the offset for "pending" data of a nested field
+ BindingRangePathLink* pending = nullptr;
};
+ /// A helper type to construct a `BindingRangePath` that extends an existing path
+ struct ExtendedBindingRangePath : BindingRangePath
+ {
+ /// Construct a path that extends `parent` with offset information from `varLayout`
+ ExtendedBindingRangePath(
+ BindingRangePath const& parent,
+ VarLayout* varLayout)
+ {
+ SLANG_ASSERT(varLayout);
+
+ // We always add another link to the primary chain.
+ //
+ primaryLink = BindingRangePathLink(parent.primary, varLayout);
+ primary = &primaryLink;
+
+ // If the `varLayout` provided has any offset information
+ // for pending data, then we also add a link to the pending
+ // chain, but otherwise we re-use the pending chain from
+ // the parent path.
+ //
+ if(auto pendingLayout = varLayout->pendingVarLayout)
+ {
+ pendingLink = BindingRangePathLink(parent.pending, pendingLayout);
+ pending = &pendingLink;
+ }
+ else
+ {
+ pending = parent.pending;
+ }
+ }
+
+ /// Storage for a link in the primary chain, if needed
+ BindingRangePathLink primaryLink;
+
+ /// Storage for a link in the pending chain, if needed
+ BindingRangePathLink pendingLink;
+ };
+ /// Calculate the offset for resources of the given `kind` in the `path`.
Int _calcIndexOffset(BindingRangePathLink* path, LayoutResourceKind kind)
{
Int result = 0;
@@ -1085,6 +1167,7 @@ namespace Slang
return result;
}
+ /// Calculate the regsiter space / set for resources of the given `kind` in the `path`.
Int _calcSpaceOffset(BindingRangePathLink* path, LayoutResourceKind kind)
{
Int result = 0;
@@ -1208,12 +1291,24 @@ namespace Slang
}
}
-
-
SlangBindingType _calcBindingType(
Slang::TypeLayout* typeLayout,
LayoutResourceKind kind)
{
+ // At the type level, a push-constant buffer and a regular constant
+ // buffer are currently not distinct, so we need to detect push
+ // constant buffers/ranges before we inspect the `typeLayout` to
+ // avoid reflecting them all as ordinary constant buffers.
+ //
+ switch(kind)
+ {
+ default:
+ break;
+
+ case LayoutResourceKind::PushConstantBuffer:
+ return SLANG_BINDING_TYPE_PUSH_CONSTANT;
+ }
+
// If the type or type layout implies a specific binding type
// (e.g., a `Texture2D` implies a texture binding), then we
// will always favor the binding type implied.
@@ -1267,7 +1362,42 @@ namespace Slang
return index;
}
- void addRangesRec(TypeLayout* typeLayout, BindingRangePathLink* path, LayoutSize multiplier)
+ /// Create a single `VarLayout` for `typeLayout` that summarizes all of the offset information in `path`.
+ ///
+ /// Note: This function does not handle "pending" layout information.
+ RefPtr<VarLayout> _createSimpleOffsetVarLayout(TypeLayout* typeLayout, BindingRangePathLink* path)
+ {
+ SLANG_ASSERT(typeLayout);
+
+ RefPtr<VarLayout> varLayout = new VarLayout();
+ varLayout->typeLayout = typeLayout;
+
+ for(auto typeResInfo : typeLayout->resourceInfos)
+ {
+ auto kind = typeResInfo.kind;
+ auto varResInfo = varLayout->findOrAddResourceInfo(kind);
+ varResInfo->index = _calcIndexOffset(path, kind);
+ varResInfo->space = _calcSpaceOffset(path, kind);
+ }
+
+ return varLayout;
+ }
+
+ /// Create a single `VarLayout` for `typeLayout` that summarizes all of the offset information in `path`.
+ RefPtr<VarLayout> createOffsetVarLayout(TypeLayout* typeLayout, BindingRangePath const& path)
+ {
+ auto primaryVarLayout = _createSimpleOffsetVarLayout(typeLayout, path.primary);
+ SLANG_ASSERT(primaryVarLayout);
+
+ if(auto pendingDataTypeLayout = typeLayout->pendingDataTypeLayout)
+ {
+ primaryVarLayout->pendingVarLayout = _createSimpleOffsetVarLayout(pendingDataTypeLayout, path.pending);
+ }
+
+ return primaryVarLayout;
+ }
+
+ void addRangesRec(TypeLayout* typeLayout, BindingRangePath const& path, LayoutSize multiplier)
{
if( auto structTypeLayout = as<StructTypeLayout>(typeLayout) )
{
@@ -1287,9 +1417,8 @@ namespace Slang
auto fieldTypeLayout = fieldVarLayout->getTypeLayout();
-
- BindingRangePathLink fieldLink(path, fieldVarLayout);
- addRangesRec(fieldTypeLayout, &fieldLink, multiplier);
+ ExtendedBindingRangePath fieldPath(path, fieldVarLayout);
+ addRangesRec(fieldTypeLayout, fieldPath, multiplier);
}
return;
}
@@ -1336,7 +1465,7 @@ namespace Slang
{
if( spaceOffset == -1 )
{
- spaceOffset = _calcSpaceOffset(path, kind);
+ spaceOffset = _calcSpaceOffset(path.primary, kind);
}
kind = resInfo.kind;
@@ -1382,24 +1511,17 @@ namespace Slang
bindingRange.firstDescriptorRangeIndex = 0;
bindingRange.descriptorRangeCount = 0;
- if( kind == LayoutResourceKind::PushConstantBuffer )
- {
- if(auto resInfo = parameterGroupTypeLayout->elementVarLayout->typeLayout->FindResourceInfo(LayoutResourceKind::Uniform))
- {
- bindingRange.count *= resInfo->count;
- }
- }
-
// Every parameter group will introduce a sub-object range,
// which will include bindings based on the type of data
// inside the sub-object.
//
TypeLayout::ExtendedInfo::SubObjectRangeInfo subObjectRange;
subObjectRange.bindingRangeIndex = bindingRangeIndex;
+ subObjectRange.offsetVarLayout = createOffsetVarLayout(typeLayout, path);
subObjectRange.spaceOffset = 0;
- if (kind == LayoutResourceKind::RegisterSpace && path)
+ if (kind == LayoutResourceKind::RegisterSpace && path.primary)
{
- auto resInfo = path->var->FindResourceInfo(LayoutResourceKind::RegisterSpace);
+ auto resInfo = path.primary->var->FindResourceInfo(LayoutResourceKind::RegisterSpace);
subObjectRange.spaceOffset = resInfo->index;
}
// It is possible that the sub-object has descriptor ranges
@@ -1488,16 +1610,7 @@ namespace Slang
descriptorRange.kind = resInfo.kind;
descriptorRange.bindingType = _calcBindingType(typeLayout, resInfo.kind);
descriptorRange.count = multiplier;
- descriptorRange.indexOffset = _calcIndexOffset(path, resInfo.kind);
-
- if( resInfo.kind == LayoutResourceKind::PushConstantBuffer )
- {
- if(auto uniformResInfo = parameterGroupTypeLayout->elementVarLayout->typeLayout->FindResourceInfo(LayoutResourceKind::Uniform))
- {
- descriptorRange.count *= uniformResInfo->count;
- }
- }
-
+ descriptorRange.indexOffset = _calcIndexOffset(path.primary, resInfo.kind);
descriptorSet->descriptorRanges.add(descriptorRange);
}
}
@@ -1548,12 +1661,12 @@ namespace Slang
// up binding information, to ensure that the descriptor ranges
// that get enumerated here have correct register/binding offsets.
//
- BindingRangePathLink elementPath(path, parameterGroupTypeLayout->elementVarLayout);
+ ExtendedBindingRangePath elementPath(path, parameterGroupTypeLayout->elementVarLayout);
Index bindingRangeCountBefore = m_extendedInfo->m_bindingRanges.getCount();
Index subObjectRangeCountBefore = m_extendedInfo->m_subObjectRanges.getCount();
- addRangesRec(parameterGroupTypeLayout->elementVarLayout->typeLayout, &elementPath, multiplier);
+ addRangesRec(parameterGroupTypeLayout->elementVarLayout->typeLayout, elementPath, multiplier);
m_extendedInfo->m_bindingRanges.setCount(bindingRangeCountBefore);
m_extendedInfo->m_subObjectRanges.setCount(subObjectRangeCountBefore);
@@ -1576,10 +1689,6 @@ namespace Slang
// An `interface` type should introduce a binding range and a matching
// sub-object range.
//
- // We currently do *not* allocate any descriptor ranges to represent
- // an interface-type field, since the only direct storage required
- // is all uniform/ordinary data.
- //
TypeLayout::ExtendedInfo::BindingRangeInfo bindingRange;
bindingRange.leafTypeLayout = typeLayout;
bindingRange.bindingType = SLANG_BINDING_TYPE_EXISTENTIAL_VALUE;
@@ -1590,10 +1699,7 @@ namespace Slang
TypeLayout::ExtendedInfo::SubObjectRangeInfo subObjectRange;
subObjectRange.bindingRangeIndex = m_extendedInfo->m_bindingRanges.getCount();
-
- // TODO: if we have "pending" layout information that tells us where
- // data for the sub-object range has been allocated, then we need
- // a way to reference that data here.
+ subObjectRange.offsetVarLayout = createOffsetVarLayout(typeLayout, path);
m_extendedInfo->m_bindingRanges.add(bindingRange);
m_extendedInfo->m_subObjectRanges.add(subObjectRange);
@@ -1730,8 +1836,8 @@ namespace Slang
// `resInfo` representing resouce usage.
//
auto count = resInfo.count * multiplier;
- auto indexOffset = _calcIndexOffset(path, kind);
- auto spaceOffset = _calcSpaceOffset(path, kind);
+ auto indexOffset = _calcIndexOffset(path.primary, kind);
+ auto spaceOffset = _calcSpaceOffset(path.primary, kind);
TypeLayout::ExtendedInfo::DescriptorRangeInfo descriptorRange;
descriptorRange.kind = kind;
@@ -1767,7 +1873,8 @@ namespace Slang
context.m_typeLayout = typeLayout;
context.m_extendedInfo = extendedInfo;
- context.addRangesRec(typeLayout, nullptr, 1);
+ BindingRangePath rootPath;
+ context.addRangesRec(typeLayout, rootPath, 1);
typeLayout->m_extendedInfo = extendedInfo;
}
@@ -2033,6 +2140,25 @@ SLANG_API SlangInt spReflectionTypeLayout_getSubObjectRangeSpaceOffset(
return extTypeLayout->m_subObjectRanges[subObjectRangeIndex].spaceOffset;
}
+SLANG_API SlangReflectionVariableLayout* spReflectionTypeLayout_getSubObjectRangeOffset(
+ SlangReflectionTypeLayout* inTypeLayout,
+ SlangInt subObjectRangeIndex)
+{
+ auto typeLayout = convert(inTypeLayout);
+ if (!typeLayout)
+ return 0;
+
+ auto extTypeLayout = Slang::getExtendedTypeLayout(typeLayout);
+
+ if (subObjectRangeIndex < 0)
+ return 0;
+ if (subObjectRangeIndex >= extTypeLayout->m_subObjectRanges.getCount())
+ return 0;
+
+ return convert(extTypeLayout->m_subObjectRanges[subObjectRangeIndex].offsetVarLayout);
+}
+
+
#if 0
SLANG_API SlangInt spReflectionTypeLayout_getBindingRangeSubObjectRangeIndex(SlangReflectionTypeLayout* inTypeLayout, SlangInt index)
diff --git a/source/slang/slang-type-layout.cpp b/source/slang/slang-type-layout.cpp
index a015bfd78..f21722bd7 100644
--- a/source/slang/slang-type-layout.cpp
+++ b/source/slang/slang-type-layout.cpp
@@ -4013,6 +4013,12 @@ static TypeLayoutResult _createTypeLayout(
typeLayout->type = type;
typeLayout->rules = rules;
+ for( auto resInfo : baseTypeLayoutResult.layout->resourceInfos )
+ {
+ if(resInfo.kind != LayoutResourceKind::Uniform)
+ typeLayout->addResourceUsage(resInfo);
+ }
+
RefPtr<VarLayout> pendingDataVarLayout = new VarLayout();
if(auto pendingDataTypeLayout = baseTypeLayoutResult.layout->pendingDataTypeLayout)
{
@@ -4033,6 +4039,7 @@ static TypeLayoutResult _createTypeLayout(
{
if(auto primaryResInfo = baseTypeLayoutResult.layout->FindResourceInfo(kind))
index = primaryResInfo->count.getFiniteValue();
+ typeLayout->addResourceUsage(pendingResInfo);
}
pendingDataVarLayout->AddResourceInfo(kind)->index = index;
}
@@ -4041,6 +4048,12 @@ static TypeLayoutResult _createTypeLayout(
typeLayout->baseTypeLayout = baseTypeLayoutResult.layout;
typeLayout->pendingDataVarLayout = pendingDataVarLayout;
+ typeLayout->uniformAlignment = info.alignment;
+ if( info.size != 0 )
+ {
+ typeLayout->addResourceUsage(LayoutResourceKind::Uniform, info.size);
+ }
+
return makeTypeLayoutResult(typeLayout);
}
diff --git a/source/slang/slang-type-layout.h b/source/slang/slang-type-layout.h
index 88ba72003..52b575787 100644
--- a/source/slang/slang-type-layout.h
+++ b/source/slang/slang-type-layout.h
@@ -310,6 +310,7 @@ struct SimpleArrayLayoutInfo : SimpleLayoutInfo
};
struct LayoutRulesImpl;
+class VarLayout;
// Base class for things that store layout info
class Layout : public RefObject
@@ -436,8 +437,9 @@ public:
struct SubObjectRangeInfo
{
- Int bindingRangeIndex;
- Int spaceOffset;
+ Int bindingRangeIndex = 0;
+ Int spaceOffset = 0;
+ RefPtr<VarLayout> offsetVarLayout;
};
List<RefPtr<DescriptorSetInfo>> m_descriptorSets;
diff --git a/tests/compute/interface-shader-param-in-struct.slang b/tests/compute/interface-shader-param-in-struct.slang
index 62aa093ed..5080e4d57 100644
--- a/tests/compute/interface-shader-param-in-struct.slang
+++ b/tests/compute/interface-shader-param-in-struct.slang
@@ -3,9 +3,9 @@
// This test puts interface-type shader parameters
// inside of structure types to make sure that works
-//DISABLED_TEST(compute):COMPARE_COMPUTE_EX:-slang -compute
-//DISABLED_TEST(compute):COMPARE_COMPUTE_EX:-slang -compute -dx12 -profile sm_6_0 -use-dxil
-//DISABLED_TEST(compute, vulkan):COMPARE_COMPUTE_EX:-vk -compute
+//TEST(compute):COMPARE_COMPUTE_EX:-slang -compute
+//TEST(compute):COMPARE_COMPUTE_EX:-slang -compute -dx12 -profile sm_6_0 -use-dxil
+//TEST(compute, vulkan):COMPARE_COMPUTE_EX:-vk -compute
// A lot of the setup is the same as for `interface-shader-param`,
// so look there if you want the comments.
diff --git a/tests/language-feature/shader-params/interface-shader-param-ordinary.slang b/tests/language-feature/shader-params/interface-shader-param-ordinary.slang
new file mode 100644
index 000000000..8d475254f
--- /dev/null
+++ b/tests/language-feature/shader-params/interface-shader-param-ordinary.slang
@@ -0,0 +1,53 @@
+// interface-shader-param-ordinary.slang
+
+// This test is for interface-type shader parameters that
+// get specialized to types that include "ordinary" data
+// but also don't fit into the allocation provided for
+// them in the existential-type field itself.
+
+//TEST(compute):COMPARE_COMPUTE_EX:-slang -compute
+//TEST(compute):COMPARE_COMPUTE_EX:-slang -compute -dx12 -profile sm_6_0 -use-dxil
+//TEST(compute, vulkan):COMPARE_COMPUTE_EX:-vk -compute
+
+interface IModifier
+{
+ int modify(int value);
+}
+
+//TEST_INPUT:set gOutputBuffer = out ubuffer(data=[0 0 0 0], stride=4)
+RWStructuredBuffer<int> gOutputBuffer;
+
+//TEST_INPUT:set delta = 65536
+uniform int delta;
+
+//TEST_INPUT:set gModifier = new MyModifier{ ubuffer(data=[4 3 2 1], stride=4), 3 } }
+uniform IModifier gModifier;
+
+int test(int val)
+{
+ return gModifier.modify(val) + delta;
+}
+
+
+[numthreads(4, 1, 1)]
+void computeMain(
+ uint3 dispatchThreadID : SV_DispatchThreadID)
+{
+ let tid = dispatchThreadID.x;
+
+ let inputVal : int = tid;
+ let outputVal = test(inputVal);
+
+ gOutputBuffer[tid] = outputVal;
+}
+
+struct MyModifier : IModifier
+{
+ RWStructuredBuffer<int> data;
+ int extra;
+
+ int modify(int val)
+ {
+ return val*65536 + data[val]*256 + val*extra;
+ }
+}
diff --git a/tests/language-feature/shader-params/interface-shader-param-ordinary.slang.expected.txt b/tests/language-feature/shader-params/interface-shader-param-ordinary.slang.expected.txt
new file mode 100644
index 000000000..434576d50
--- /dev/null
+++ b/tests/language-feature/shader-params/interface-shader-param-ordinary.slang.expected.txt
@@ -0,0 +1,4 @@
+10400
+20303
+30206
+40109
diff --git a/tools/gfx/d3d11/render-d3d11.cpp b/tools/gfx/d3d11/render-d3d11.cpp
index 5108b4d8b..415e0b833 100644
--- a/tools/gfx/d3d11/render-d3d11.cpp
+++ b/tools/gfx/d3d11/render-d3d11.cpp
@@ -383,36 +383,327 @@ protected:
}
};
- struct RootBindingState
+ /// Contextual data and operations required when binding shader objects to the pipeline state
+ struct BindingContext
+ {
+ // One key service that the `BindingContext` provides is abstracting over
+ // the difference between the D3D11 compute and graphics/rasteriation pipelines.
+ // D3D11 has distinct operations for, e.g., `CSSetShaderResources`
+ // for compute vs. `VSSetShaderResources` and `PSSetShaderResources`
+ // for rasterization.
+ //
+ // The context type provides simple operations for setting each class
+ // of resource/sampler, which will be overridden in derived types.
+ //
+ // TODO: These operations should really support binding multiple resources/samplers
+ // in one call, so that we can eventually make more efficient use of the API.
+ //
+ // TODO: We could reasonably also just store the bound resources into
+ // lcoal arrays like we are doing for UAVs, and remove the pipeline-specific
+ // virtual functions. However, doing so would seemingly eliminate any
+ // chance of avoiding redundant binding work when binding changes are
+ // made for a root shader object.
+ //
+ virtual void setCBV(UINT index, ID3D11Buffer* buffer) = 0;
+ virtual void setSRV(UINT index, ID3D11ShaderResourceView* srv) = 0;
+ virtual void setSampler(UINT index, ID3D11SamplerState* sampler) = 0;
+
+ // Unordered Access Views (UAVs) are a somewhat special case in that
+ // the D3D11 API requires them to all be set at once, rather than one
+ // at a time. To support this, we will keep a local array of the UAVs
+ // that have been bound (up to the maximum supported by D3D 11.0)
+ //
+ void setUAV(UINT index, ID3D11UnorderedAccessView* uav)
+ {
+ uavs[index] = uav;
+
+ // We will also track the total number of UAV slots that will
+ // need to be bound (including any gaps that might occur due
+ // to either explicit bindings or RTV bindings that conflict
+ // with the `u` registers for fragment shaders).
+ //
+ if(uavCount <= index)
+ {
+ uavCount = index+1;
+ }
+ }
+
+ /// The values bound for any UAVs
+ ID3D11UnorderedAccessView* uavs[D3D11_PS_CS_UAV_REGISTER_COUNT];
+
+ /// The number of entries in `uavs` that need to be considered when binding to the pipeline
+ UINT uavCount = 0;
+
+ /// The D3D11 device that we are using for binding
+ D3D11Device* device = nullptr;
+
+ /// The D3D11 device context that we are using for binding
+ ID3D11DeviceContext* context = nullptr;
+
+ /// Initialize a binding context for binding to the given `device` and `context`
+ BindingContext(
+ D3D11Device* device,
+ ID3D11DeviceContext* context)
+ : device(device)
+ , context(context)
+ {
+ memset(uavs, 0, sizeof(uavs));
+ }
+ };
+
+ /// A `BindingContext` for binding to the compute pipeline
+ struct ComputeBindingContext : BindingContext
+ {
+ /// Initialize a binding context for binding to the given `device` and `context`
+ ComputeBindingContext(
+ D3D11Device* device,
+ ID3D11DeviceContext* context)
+ : BindingContext(device, context)
+ {}
+
+ void setCBV(UINT index, ID3D11Buffer* buffer) SLANG_OVERRIDE
+ {
+ context->CSSetConstantBuffers(index, 1, &buffer);
+ }
+
+ void setSRV(UINT index, ID3D11ShaderResourceView* srv) SLANG_OVERRIDE
+ {
+ context->CSSetShaderResources(index, 1, &srv);
+ }
+
+ void setSampler(UINT index, ID3D11SamplerState* sampler) SLANG_OVERRIDE
+ {
+ context->CSSetSamplers(index, 1, &sampler);
+ }
+ };
+
+ /// A `BindingContext` for binding to the graphics/rasterization pipeline
+ struct GraphicsBindingContext : BindingContext
{
- List<ID3D11ShaderResourceView*> srvBindings;
- List<ID3D11UnorderedAccessView*> uavBindings;
- List<ID3D11SamplerState*> samplerBindings;
- List<ID3D11Buffer*> constantBuffers;
+ /// Initialize a binding context for binding to the given `device` and `context`
+ GraphicsBindingContext(
+ D3D11Device* device,
+ ID3D11DeviceContext* context)
+ : BindingContext(device, context)
+ {}
+
+ // TODO: The operations here are only dealing with vertex and fragment
+ // shaders for now. We should eventually extend them to handle HS/DS/GS
+ // bindings. (We might want to skip those stages depending on whether
+ // the associated program uses them at all).
+ //
+ // TODO: If we support cases where different stages might use distinct
+ // entry-point parameters, we might need to support some modes where
+ // a "stage mask" is passed in that applies to the bindings.
+ //
+ void setCBV(UINT index, ID3D11Buffer* buffer) SLANG_OVERRIDE
+ {
+ context->VSSetConstantBuffers(index, 1, &buffer);
+ context->PSSetConstantBuffers(index, 1, &buffer);
+ }
+
+ void setSRV(UINT index, ID3D11ShaderResourceView* srv) SLANG_OVERRIDE
+ {
+ context->VSSetShaderResources(index, 1, &srv);
+ context->PSSetShaderResources(index, 1, &srv);
+ }
+
+ void setSampler(UINT index, ID3D11SamplerState* sampler) SLANG_OVERRIDE
+ {
+ context->VSSetSamplers(index, 1, &sampler);
+ context->PSSetSamplers(index, 1, &sampler);
+ }
+ };
+
+ // In order to bind shader parameters to the correct locations, we need to
+ // be able to describe those locations. Most shader parameters will
+ // only consume a single type of D3D11-visible regsiter (e.g., a `t`
+ // register for a txture, or an `s` register for a sampler), and scalar
+ // integers suffice for these cases.
+ //
+ // In more complex cases we might be binding an entire "sub-object" like
+ // a parameter block, an entry point, etc. For the general case, we need
+ // to be able to represent a composite offset that includes offsets for
+ // each of the register classes known to D3D11.
+
+ /// A "simple" binding offset that records an offset in CBV/SRV/UAV/Sampler slots
+ struct SimpleBindingOffset
+ {
+ uint32_t cbv = 0;
+ uint32_t srv = 0;
+ uint32_t uav = 0;
+ uint32_t sampler = 0;
+
+ /// Create a default (zero) offset
+ SimpleBindingOffset()
+ {}
+
+ /// Create an offset based on offset information in the given Slang `varLayout`
+ SimpleBindingOffset(slang::VariableLayoutReflection* varLayout)
+ {
+ if(varLayout)
+ {
+ cbv = (uint32_t) varLayout->getOffset(SLANG_PARAMETER_CATEGORY_CONSTANT_BUFFER);
+ srv = (uint32_t) varLayout->getOffset(SLANG_PARAMETER_CATEGORY_SHADER_RESOURCE);
+ uav = (uint32_t) varLayout->getOffset(SLANG_PARAMETER_CATEGORY_UNORDERED_ACCESS);
+ sampler = (uint32_t) varLayout->getOffset(SLANG_PARAMETER_CATEGORY_SAMPLER_STATE);
+ }
+ }
+
+ /// Add any values in the given `offset`
+ void operator+=(SimpleBindingOffset const& offset)
+ {
+ cbv += offset.cbv;
+ srv += offset.srv;
+ uav += offset.uav;
+ sampler += offset.sampler;
+ }
+ };
+
+ // While a "simple" binding offset representation will work in many cases,
+ // once we need to deal with layout for programs with interface-type parameters
+ // that have been statically specialized, we also need to track the offset
+ // for where to bind any "pending" data that arises from the process of static
+ // specialization.
+ //
+ // In order to conveniently track both the "primary" and "pending" offset information,
+ // we will define a more complete `BindingOffset` type that combines simple
+ // binding offsets for the primary and pending parts.
+
+ /// A representation of the offset at which to bind a shader parameter or sub-object
+ struct BindingOffset : SimpleBindingOffset
+ {
+ // Offsets for "primary" data are stored directly in the `BindingOffset`
+ // via the inheritance from `SimpleBindingOffset`.
+
+ /// Offset for any "pending" data
+ SimpleBindingOffset pending;
+
+ /// Create a default (zero) offset
+ BindingOffset()
+ {}
+
+ /// Create an offset from a simple offset
+ explicit BindingOffset(SimpleBindingOffset const& offset)
+ : SimpleBindingOffset(offset)
+ {}
+
+ /// Create an offset based on offset information in the given Slang `varLayout`
+ BindingOffset(slang::VariableLayoutReflection* varLayout)
+ : SimpleBindingOffset(varLayout)
+ , pending(varLayout->getPendingDataLayout())
+ {}
+
+ /// Add any values in the given `offset`
+ void operator+=(SimpleBindingOffset const& offset)
+ {
+ SimpleBindingOffset::operator+=(offset);
+ }
+
+ /// Add any values in the given `offset`
+ void operator+=(BindingOffset const& offset)
+ {
+ SimpleBindingOffset::operator+=(offset);
+ pending += offset.pending;
+ }
};
class ShaderObjectLayoutImpl : public ShaderObjectLayoutBase
{
public:
+ // A shader object comprises three main kinds of state:
+ //
+ // * Zero or more bytes of ordinary ("uniform") data
+ // * Zero or more *bindings* for textures, buffers, and samplers
+ // * Zero or more *sub-objects* representing nested parameter blocks, etc.
+ //
+ // A shader object *layout* stores information that can be used to
+ // organize these different kinds of state and optimize access to them.
+ //
+ // For example, both texture/buffer/sampler bindings and sub-objects
+ // are organized into logical *binding ranges* by the Slang reflection
+ // API, and a shader object layout will store information about those
+ // ranges in a form that is usable for the D3D11 API:
+
+ /// Information about a logical binding range as reported by Slang reflection
struct BindingRangeInfo
{
+ /// The type of bindings in this range
slang::BindingType bindingType;
+
+ /// The number of bindings in this range
Index count;
+
+ /// The starting index for this range in the appropriate "flat" array in a shader object.
+ /// E.g., for a shader resourve view range, this would be an index into the `m_srvs` array.
Index baseIndex;
- // baseIndex2 is used to specify samplers in a CombinedTextureSampler binding.
- Index baseIndex2;
- // Returns true if this binding range consumes a specialization argument slot.
- bool isSpecializationArg() const
+ /// The offset of this binding range from the start of the sub-object
+ /// in terms of whatever D3D11 register class it consumes. E.g., for
+ /// a `Texture2D` binding range this will represent an offset in
+ /// `t` registers.
+ ///
+ uint32_t registerOffset;
+ };
+
+ // Sometimes we just want to iterate over the ranges that represnet
+ // sub-objects while skipping over the others, because sub-object
+ // ranges often require extra handling or more state.
+ //
+ // For that reason we also store pre-computed information about each
+ // sub-object range.
+
+ /// Offset information for a sub-object range
+ struct SubObjectRangeOffset : BindingOffset
+ {
+ SubObjectRangeOffset()
+ {}
+
+ SubObjectRangeOffset(slang::VariableLayoutReflection* varLayout)
+ : BindingOffset(varLayout)
+ {
+ if(auto pendingLayout = varLayout->getPendingDataLayout())
+ {
+ pendingOrdinaryData = (uint32_t) pendingLayout->getOffset(SLANG_PARAMETER_CATEGORY_UNIFORM);
+ }
+ }
+
+ /// The offset for "pending" ordinary data related to this range
+ uint32_t pendingOrdinaryData = 0;
+ };
+
+ /// Stride information for a sub-object range
+ struct SubObjectRangeStride
+ {
+ SubObjectRangeStride()
+ {}
+
+ SubObjectRangeStride(slang::TypeLayoutReflection* typeLayout)
{
- return bindingType == slang::BindingType::ExistentialValue;
+ if(auto pendingLayout = typeLayout->getPendingDataTypeLayout())
+ {
+ pendingOrdinaryData = (uint32_t) pendingLayout->getSize(SLANG_PARAMETER_CATEGORY_UNIFORM);
+ }
}
+
+ /// The strid for "pending" ordinary data related to this range
+ uint32_t pendingOrdinaryData = 0;
};
+ /// Information about a logical binding range as reported by Slang reflection
struct SubObjectRangeInfo
{
- RefPtr<ShaderObjectLayoutImpl> layout;
+ /// The index of the binding range that corresponds to this sub-object range
Index bindingRangeIndex;
+
+ /// The layout expected for objects bound to this range (if known)
+ RefPtr<ShaderObjectLayoutImpl> layout;
+
+ /// The offset to use when binding the first object in this range
+ SubObjectRangeOffset offset;
+
+ /// Stride between consecutive objects in this range
+ SubObjectRangeStride stride;
};
struct Builder
@@ -428,12 +719,21 @@ protected:
List<BindingRangeInfo> m_bindingRanges;
List<SubObjectRangeInfo> m_subObjectRanges;
+ /// The indices of the binding ranges that represent SRVs
+ List<Index> m_srvRanges;
+
+ /// The indices of the binding ranges that represent UAVs
+ List<Index> m_uavRanges;
+
+ /// The indices of the binding ranges that represent samplers
+ List<Index> m_samplerRanges;
+
Index m_srvCount = 0;
Index m_samplerCount = 0;
Index m_uavCount = 0;
Index m_subObjectCount = 0;
- Index m_varyingInputCount = 0;
- Index m_varyingOutputCount = 0;
+
+ uint32_t m_totalOrdinaryDataSize = 0;
Result setElementTypeLayout(slang::TypeLayoutReflection* typeLayout)
{
@@ -441,6 +741,8 @@ protected:
m_elementTypeLayout = typeLayout;
+ m_totalOrdinaryDataSize = (uint32_t) typeLayout->getSize();
+
// Compute the binding ranges that are used to store
// the logical contents of the object in memory.
@@ -468,13 +770,10 @@ protected:
case slang::BindingType::Sampler:
bindingRangeInfo.baseIndex = m_samplerCount;
m_samplerCount += count;
+ m_samplerRanges.add(r);
break;
case slang::BindingType::CombinedTextureSampler:
- bindingRangeInfo.baseIndex = m_srvCount;
- bindingRangeInfo.baseIndex2 = m_samplerCount;
- m_srvCount += count;
- m_samplerCount += count;
break;
case slang::BindingType::MutableRawBuffer:
@@ -482,24 +781,53 @@ protected:
case slang::BindingType::MutableTypedBuffer:
bindingRangeInfo.baseIndex = m_uavCount;
m_uavCount += count;
+ m_uavRanges.add(r);
break;
case slang::BindingType::VaryingInput:
- bindingRangeInfo.baseIndex = m_varyingInputCount;
- m_varyingInputCount += count;
break;
case slang::BindingType::VaryingOutput:
- bindingRangeInfo.baseIndex = m_varyingOutputCount;
- m_varyingOutputCount += count;
break;
default:
bindingRangeInfo.baseIndex = m_srvCount;
m_srvCount += count;
+ m_srvRanges.add(r);
break;
}
+ // We'd like to extract the information on the D3D11 shader
+ // register that this range should bind into.
+ //
+ // A binding range represents a logical member of the shader
+ // object type, and it may encompass zero or more *descriptor
+ // ranges* that describe how it is physically bound to pipeline
+ // state.
+ //
+ // If the current bindign range is backed by at least one descriptor
+ // range then we can query the register offset of that descriptor
+ // range. We expect that in the common case there will be exactly
+ // one descriptor range, and we can extract the information easily.
+ //
+ // TODO: we might eventually need to special-case our handling
+ // of combined texture-sampler ranges since they will need to
+ // store two different offsets.
+ //
+ if(typeLayout->getBindingRangeDescriptorRangeCount(r) != 0)
+ {
+ // The Slang reflection information organizes the descriptor ranges
+ // into "descriptor sets" but D3D11 has no notion like that so we
+ // expect all ranges belong to a single set.
+ //
+ SlangInt descriptorSetIndex = typeLayout->getBindingRangeDescriptorSetIndex(r);
+ SLANG_ASSERT(descriptorSetIndex == 0);
+
+ SlangInt descriptorRangeIndex = typeLayout->getBindingRangeFirstDescriptorRangeIndex(r);
+ auto registerOffset = typeLayout->getDescriptorSetDescriptorRangeIndexOffset(descriptorSetIndex, descriptorRangeIndex);
+
+ bindingRangeInfo.registerOffset = (uint32_t) registerOffset;
+ }
m_bindingRanges.add(bindingRangeInfo);
}
@@ -508,28 +836,70 @@ protected:
for (SlangInt r = 0; r < subObjectRangeCount; ++r)
{
SlangInt bindingRangeIndex = typeLayout->getSubObjectRangeBindingRangeIndex(r);
+ auto& bindingRange = m_bindingRanges[bindingRangeIndex];
+
auto slangBindingType = typeLayout->getBindingRangeType(bindingRangeIndex);
slang::TypeLayoutReflection* slangLeafTypeLayout =
typeLayout->getBindingRangeLeafTypeLayout(bindingRangeIndex);
+ SubObjectRangeInfo subObjectRange;
+ subObjectRange.bindingRangeIndex = bindingRangeIndex;
+
+ // We will use Slang reflection information to extract the offset and stride
+ // information for each sub-object range.
+ //
+ subObjectRange.offset = SubObjectRangeOffset(typeLayout->getSubObjectRangeOffset(r));
+ subObjectRange.stride = SubObjectRangeStride(slangLeafTypeLayout);
+
// A sub-object range can either represent a sub-object of a known
// type, like a `ConstantBuffer<Foo>` or `ParameterBlock<Foo>`
- // (in which case we can pre-compute a layout to use, based on
- // the type `Foo`) *or* it can represent a sub-object of some
- // existential type (e.g., `IBar`) in which case we cannot
- // know the appropraite type/layout of sub-object to allocate.
+ // *or* it can represent a sub-object of some existential type (e.g., `IBar`).
//
RefPtr<ShaderObjectLayoutImpl> subObjectLayout;
- if (slangBindingType != slang::BindingType::ExistentialValue)
+ switch(slangBindingType)
{
- createForElementType(
- m_renderer,
- slangLeafTypeLayout->getElementTypeLayout(),
- subObjectLayout.writeRef());
- }
+ default:
+ {
+ // In the case of `ConstantBuffer<X>` or `ParameterBlock<X>`
+ // we can construct a layout from the element type directly.
+ //
+ auto elementTypeLayout = slangLeafTypeLayout->getElementTypeLayout();
+ createForElementType(
+ m_renderer,
+ elementTypeLayout,
+ subObjectLayout.writeRef());
+ }
+ break;
- SubObjectRangeInfo subObjectRange;
- subObjectRange.bindingRangeIndex = bindingRangeIndex;
+ case slang::BindingType::ExistentialValue:
+ // In the case of an interface-type sub-object range, we can only
+ // construct a layout if we have static specialization information
+ // that tells us what type we expect to find in that range.
+ //
+ // The static specialization information is expected to take the
+ // form of a "pending" type layotu attached to the interface type
+ // of the leaf type layout.
+ //
+ if(auto pendingTypeLayout = slangLeafTypeLayout->getPendingDataTypeLayout())
+ {
+ createForElementType(
+ m_renderer,
+ pendingTypeLayout,
+ subObjectLayout.writeRef());
+
+ // An interface-type range that includes ordinary data can
+ // increase the size of the ordinary data buffer we need to
+ // allocate for the parent object.
+ //
+ uint32_t ordinaryDataEnd = subObjectRange.offset.pendingOrdinaryData
+ + (uint32_t) bindingRange.count * subObjectRange.stride.pendingOrdinaryData;
+
+ if(ordinaryDataEnd > m_totalOrdinaryDataSize)
+ {
+ m_totalOrdinaryDataSize = ordinaryDataEnd;
+ }
+ }
+ }
subObjectRange.layout = subObjectLayout;
m_subObjectRanges.add(subObjectRange);
@@ -581,6 +951,18 @@ protected:
{
return m_elementTypeLayout->getType();
}
+
+ /// Get the indices that represent all the SRV ranges in this type
+ List<Index> const& getSRVRanges() const { return m_srvRanges; }
+
+ /// Get the indices that reprsent all the UAV ranges in this type
+ List<Index> const& getUAVRanges() const { return m_uavRanges; }
+
+ /// Get the indices that represnet all the sampler ranges in this type
+ List<Index> const& getSamplerRanges() const { return m_samplerRanges; }
+
+ uint32_t getTotalOrdinaryDataSize() const { return m_totalOrdinaryDataSize; }
+
protected:
Result _init(Builder const* builder)
{
@@ -589,24 +971,32 @@ protected:
initBase(renderer, builder->m_elementTypeLayout);
m_bindingRanges = builder->m_bindingRanges;
+ m_srvRanges = builder->m_srvRanges;
+ m_uavRanges = builder->m_uavRanges;
+ m_samplerRanges = builder->m_samplerRanges;
m_srvCount = builder->m_srvCount;
m_samplerCount = builder->m_samplerCount;
m_uavCount = builder->m_uavCount;
m_subObjectCount = builder->m_subObjectCount;
m_subObjectRanges = builder->m_subObjectRanges;
- m_varyingInputCount = builder->m_varyingInputCount;
- m_varyingOutputCount = builder->m_varyingOutputCount;
+
+ m_totalOrdinaryDataSize = builder->m_totalOrdinaryDataSize;
+
return SLANG_OK;
}
List<BindingRangeInfo> m_bindingRanges;
+ List<Index> m_srvRanges;
+ List<Index> m_uavRanges;
+ List<Index> m_samplerRanges;
Index m_srvCount = 0;
Index m_samplerCount = 0;
Index m_uavCount = 0;
Index m_subObjectCount = 0;
Index m_varyingInputCount = 0;
Index m_varyingOutputCount = 0;
+ uint32_t m_totalOrdinaryDataSize = 0;
List<SubObjectRangeInfo> m_subObjectRanges;
};
@@ -617,7 +1007,10 @@ protected:
public:
struct EntryPointInfo
{
- RefPtr<ShaderObjectLayoutImpl> layout;
+ RefPtr<ShaderObjectLayoutImpl> layout;
+
+ /// The offset for this entry point's parameters, relative to the starting offset for the program
+ BindingOffset offset;
};
struct Builder : Super::Builder
@@ -643,18 +1036,21 @@ protected:
void addGlobalParams(slang::VariableLayoutReflection* globalsLayout)
{
setElementTypeLayout(globalsLayout->getTypeLayout());
+ m_pendingDataOffset = BindingOffset(globalsLayout).pending;
}
- void addEntryPoint(SlangStage stage, ShaderObjectLayoutImpl* entryPointLayout)
+ void addEntryPoint(SlangStage stage, ShaderObjectLayoutImpl* entryPointLayout, slang::EntryPointLayout* slangEntryPoint)
{
EntryPointInfo info;
info.layout = entryPointLayout;
+ info.offset = BindingOffset(slangEntryPoint->getVarLayout());
m_entryPoints.add(info);
}
- slang::IComponentType* m_program;
- slang::ProgramLayout* m_programLayout;
- List<EntryPointInfo> m_entryPoints;
+ slang::IComponentType* m_program;
+ slang::ProgramLayout* m_programLayout;
+ List<EntryPointInfo> m_entryPoints;
+ SimpleBindingOffset m_pendingDataOffset;
};
EntryPointInfo& getEntryPoint(Index index) { return m_entryPoints[index]; }
@@ -677,7 +1073,7 @@ protected:
RefPtr<ShaderObjectLayoutImpl> entryPointLayout;
SLANG_RETURN_ON_FAIL(ShaderObjectLayoutImpl::createForElementType(
renderer, slangEntryPoint->getTypeLayout(), entryPointLayout.writeRef()));
- builder.addEntryPoint(slangEntryPoint->getStage(), entryPointLayout);
+ builder.addEntryPoint(slangEntryPoint->getStage(), entryPointLayout, slangEntryPoint);
}
SLANG_RETURN_ON_FAIL(builder.build(outLayout));
@@ -688,6 +1084,9 @@ protected:
slang::IComponentType* getSlangProgram() const { return m_program; }
slang::ProgramLayout* getSlangProgramLayout() const { return m_programLayout; }
+ /// Get the offset at which "pending" shader parameters for this program start
+ SimpleBindingOffset const& getPendingDataOffset() const { return m_pendingDataOffset; }
+
protected:
Result _init(Builder const* builder)
{
@@ -698,6 +1097,7 @@ protected:
m_program = builder->m_program;
m_programLayout = builder->m_programLayout;
m_entryPoints = builder->m_entryPoints;
+ m_pendingDataOffset = builder->m_pendingDataOffset;
return SLANG_OK;
}
@@ -705,6 +1105,7 @@ protected:
slang::ProgramLayout* m_programLayout = nullptr;
List<EntryPointInfo> m_entryPoints;
+ SimpleBindingOffset m_pendingDataOffset;
};
class ShaderObjectImpl : public ShaderObjectBase
@@ -935,15 +1336,7 @@ protected:
SLANG_NO_THROW Result SLANG_MCALL setCombinedTextureSampler(
ShaderOffset const& offset, IResourceView* textureView, ISamplerState* sampler) SLANG_OVERRIDE
{
- if (offset.bindingRangeIndex < 0)
- return SLANG_E_INVALID_ARG;
- auto layout = getLayout();
- if (offset.bindingRangeIndex >= layout->getBindingRangeCount())
- return SLANG_E_INVALID_ARG;
- auto& bindingRange = layout->getBindingRange(offset.bindingRangeIndex);
- m_srvs[bindingRange.baseIndex + offset.bindingArrayIndex] = static_cast<ShaderResourceViewImpl*>(textureView);
- m_samplers[bindingRange.baseIndex2 + offset.bindingArrayIndex] = static_cast<SamplerStateImpl*>(sampler);
- return SLANG_OK;
+ return SLANG_E_NOT_IMPLEMENTED;
}
public:
@@ -1064,18 +1457,16 @@ protected:
/// Write the uniform/ordinary data of this object into the given `dest` buffer at the given `offset`
Result _writeOrdinaryData(
- D3D11Device* device,
- BufferResourceImpl* buffer,
- size_t offset,
- size_t destSize,
+ void* dest,
+ size_t destSize,
ShaderObjectLayoutImpl* specializedLayout)
{
+ // We start by simply writing in the ordinary data contained directly in this object.
+ //
auto src = m_ordinaryData.getBuffer();
auto srcSize = size_t(m_ordinaryData.getCount());
-
SLANG_ASSERT(srcSize <= destSize);
-
- device->uploadBufferData(buffer, offset, srcSize, src);
+ memcpy(dest, src, srcSize);
// In the case where this object has any sub-objects of
// existential/interface type, we need to recurse on those objects
@@ -1129,8 +1520,8 @@ protected:
// contiguous array with a single stride; we need to carefully consider what the layout
// logic does for complex cases with multiple layers of nested arrays and structures.
//
- size_t subObjectRangePendingDataOffset = _getSubObjectRangePendingDataOffset(specializedLayout, subObjectRangeIndex);
- size_t subObjectRangePendingDataStride = _getSubObjectRangePendingDataStride(specializedLayout, subObjectRangeIndex);
+ size_t subObjectRangePendingDataOffset = subObjectRangeInfo.offset.pendingOrdinaryData;
+ size_t subObjectRangePendingDataStride = subObjectRangeInfo.stride.pendingOrdinaryData;
// If the range doesn't actually need/use the "pending" allocation at all, then
// we need to detect that case and skip such ranges.
@@ -1151,21 +1542,23 @@ protected:
auto subObjectOffset = subObjectRangePendingDataOffset + i * subObjectRangePendingDataStride;
- subObject->_writeOrdinaryData(device, buffer, offset + subObjectOffset, destSize - subObjectOffset, subObjectLayout);
+ auto subObjectDest = (char*)dest + subObjectOffset;
+
+ subObject->_writeOrdinaryData(subObjectDest, destSize - subObjectOffset, subObjectLayout);
}
}
return SLANG_OK;
}
- // As discussed in `_writeOrdinaryData()`, these methods are just stubs waiting for
- // the "flat" Slang refelction information to provide access to the relevant data.
- //
- size_t _getSubObjectRangePendingDataOffset(ShaderObjectLayoutImpl* specializedLayout, Index subObjectRangeIndex) { return 0; }
- size_t _getSubObjectRangePendingDataStride(ShaderObjectLayoutImpl* specializedLayout, Index subObjectRangeIndex) { return 0; }
-
- /// Ensure that the `m_ordinaryDataBuffer` has been created, if it is needed
- Result _ensureOrdinaryDataBufferCreatedIfNeeded(D3D11Device* device)
+ /// Ensure that the `m_ordinaryDataBuffer` has been created, if it is needed
+ ///
+ /// The `specializedLayout` type must represent a specialized layout for this
+ /// type that includes any "pending" data.
+ ///
+ Result _ensureOrdinaryDataBufferCreatedIfNeeded(
+ D3D11Device* device,
+ ShaderObjectLayoutImpl* specializedLayout)
{
// If we have already created a buffer to hold ordinary data, then we should
// simply re-use that buffer rather than re-create it.
@@ -1178,22 +1571,7 @@ protected:
if (m_ordinaryDataBuffer)
return SLANG_OK;
- // Computing the size of the ordinary data buffer is *not* just as simple
- // as using the size of the `m_ordinayData` array that we store. The reason
- // for the added complexity is that interface-type fields may lead to the
- // storage being specialized such that it needs extra appended data to
- // store the concrete values that logically belong in those interface-type
- // fields but wouldn't fit in the fixed-size allocation we gave them.
- //
- // TODO: We need to actually implement that logic by using reflection
- // data computed for the specialized type of this shader object.
- // For now we just make the simple assumption described above despite
- // knowing that it is false.
- //
- RefPtr<ShaderObjectLayoutImpl> specializedLayout;
- SLANG_RETURN_ON_FAIL(_getSpecializedLayout(specializedLayout.writeRef()));
-
- auto specializedOrdinaryDataSize = specializedLayout->getElementTypeLayout()->getSize();
+ auto specializedOrdinaryDataSize = specializedLayout->getTotalOrdinaryDataSize();
if (specializedOrdinaryDataSize == 0)
return SLANG_OK;
@@ -1219,75 +1597,243 @@ protected:
// where this object contains interface/existential-type fields, so we
// don't need or want to inline it into this call site.
//
- SLANG_RETURN_ON_FAIL(_writeOrdinaryData(device, m_ordinaryDataBuffer, 0, specializedOrdinaryDataSize, specializedLayout));
- return SLANG_OK;
+ auto ordinaryData = device->map(m_ordinaryDataBuffer, gfx::MapFlavor::WriteDiscard);
+ auto result = _writeOrdinaryData(ordinaryData, specializedOrdinaryDataSize, specializedLayout);
+ device->unmap(m_ordinaryDataBuffer);
+
+ return result;
}
- /// Bind the buffer for ordinary/uniform data, if needed
+ /// Bind the buffer for ordinary/uniform data, if needed
+ ///
+ /// The `ioOffset` parameter will be updated to reflect the constant buffer
+ /// register consumed by the ordinary data buffer, if one was bound.
+ ///
Result _bindOrdinaryDataBufferIfNeeded(
- D3D11Device* device,
- RootBindingState* bindingState)
+ BindingContext* context,
+ BindingOffset& ioOffset,
+ ShaderObjectLayoutImpl* specializedLayout)
{
// We start by ensuring that the buffer is created, if it is needed.
//
- SLANG_RETURN_ON_FAIL(_ensureOrdinaryDataBufferCreatedIfNeeded(device));
+ SLANG_RETURN_ON_FAIL(_ensureOrdinaryDataBufferCreatedIfNeeded(context->device, specializedLayout));
// If we did indeed need/create a buffer, then we must bind it
// into root binding state.
//
if (m_ordinaryDataBuffer)
{
- bindingState->constantBuffers.add(m_ordinaryDataBuffer->m_buffer);
+ context->setCBV(ioOffset.cbv, m_ordinaryDataBuffer->m_buffer);
+ ioOffset.cbv++;
}
return SLANG_OK;
}
public:
- virtual Result bindObject(D3D11Device* device, RootBindingState* bindingState)
+ /// Bind this object as if it was declared as a `ConstantBuffer<T>` in Slang
+ Result bindAsConstantBuffer(
+ BindingContext* context,
+ BindingOffset const& inOffset,
+ ShaderObjectLayoutImpl* specializedLayout)
+ {
+ // When binding a `ConstantBuffer<X>` we need to first bind a constant
+ // buffer for any "ordinary" data in `X`, and then bind the remaining
+ // resources and sub-objets.
+ //
+ // The one important detail to keep track of its that *if* we bind
+ // a constant buffer for ordinary data we will need to account for
+ // it in the offset we use for binding the remaining data. That
+ // detail is dealt with here by the way that `_bindOrdinaryDataBufferIfNeeded`
+ // will modify the `offset` parameter if it binds anything.
+ //
+ BindingOffset offset = inOffset;
+ SLANG_RETURN_ON_FAIL(_bindOrdinaryDataBufferIfNeeded(context, /*inout*/ offset, specializedLayout));
+
+ // Once the ordinary data buffer is bound, we can move on to binding
+ // the rest of the state, which can use logic shared with the case
+ // for interface-type sub-object ranges.
+ //
+ // Note that this call will use the `offset` value that might have
+ // been modified during `_bindOrindaryDataBufferIfNeeded`.
+ //
+ SLANG_RETURN_ON_FAIL(bindAsValue(context, offset, specializedLayout));
+
+ return SLANG_OK;
+ }
+
+ /// Bind this object as a value that appears in the body of another object.
+ ///
+ /// This case is directly used when binding an object for an interface-type
+ /// sub-object range when static specialization is used. It is also used
+ /// indirectly when binding sub-objects to constant buffer or parameter
+ /// block ranges.
+ ///
+ Result bindAsValue(
+ BindingContext* context,
+ BindingOffset const& offset,
+ ShaderObjectLayoutImpl* specializedLayout)
{
- ShaderObjectLayoutImpl* layout = getLayout();
+ // We start by iterating over the binding ranges in this type, isolating
+ // just those ranges that represent SRVs, UAVs, and samplers.
+ // In each loop we will bind the values stored for those binding ranges
+ // to the correct D3D11 register (based on the `registerOffset` field
+ // stored in the bindinge range).
+ //
+ // TODO: These loops could be optimized if we stored parallel arrays
+ // for things like `m_srvs` so that we directly store an array of
+ // `ID3D11ShaderResourceView*` where each entry matches the `gfx`-level
+ // object that was bound (or holds null if nothing is bound).
+ // In that case, we could perform a single `setSRVs()` call for each
+ // binding range.
+ //
+ // TODO: More ambitiously, if the Slang layout algorithm could be modified
+ // so that non-sub-object binding ranges are guaranteed to be contiguous
+ // then a *single* `setSRVs()` call could set all of the SRVs for an object
+ // at once.
- Index baseRangeIndex = 0;
- SLANG_RETURN_ON_FAIL(_bindOrdinaryDataBufferIfNeeded(device, bindingState));
+ for(auto bindingRangeIndex : specializedLayout->getSRVRanges())
+ {
+ auto const& bindingRange = specializedLayout->getBindingRange(bindingRangeIndex);
+ auto count = (uint32_t) bindingRange.count;
+ auto baseIndex = (uint32_t) bindingRange.baseIndex;
+ auto registerOffset = bindingRange.registerOffset + offset.srv;
+ for(uint32_t i = 0; i < count; ++i)
+ {
+ auto srv = m_srvs[baseIndex + i];
+ context->setSRV(registerOffset + i, srv ? srv->m_srv : nullptr);
+ }
+ }
- for (auto sampler : m_samplers)
- bindingState->samplerBindings.add(sampler ? sampler->m_sampler.get() : nullptr);
-
- for (auto srv : m_srvs)
- bindingState->srvBindings.add(srv ? srv->m_srv : nullptr);
+ for(auto bindingRangeIndex : specializedLayout->getUAVRanges())
+ {
+ auto const& bindingRange = specializedLayout->getBindingRange(bindingRangeIndex);
+ auto count = (uint32_t) bindingRange.count;
+ auto baseIndex = (uint32_t) bindingRange.baseIndex;
+ auto registerOffset = bindingRange.registerOffset + offset.uav;
+ for(uint32_t i = 0; i < count; ++i)
+ {
+ auto uav = m_uavs[baseIndex + i];
+ context->setUAV(registerOffset + i, uav ? uav->m_uav : nullptr);
+ }
+ }
- for (auto uav : m_uavs)
- bindingState->uavBindings.add(uav ? uav->m_uav : nullptr);
+ for(auto bindingRangeIndex : specializedLayout->getSamplerRanges())
+ {
+ auto const& bindingRange = specializedLayout->getBindingRange(bindingRangeIndex);
+ auto count = (uint32_t) bindingRange.count;
+ auto baseIndex = (uint32_t) bindingRange.baseIndex;
+ auto registerOffset = bindingRange.registerOffset + offset.sampler;
+ for(uint32_t i = 0; i < count; ++i)
+ {
+ auto sampler = m_samplers[baseIndex + i];
+ context->setSampler(registerOffset + i, sampler ? sampler->m_sampler.get() : nullptr);
+ }
+ }
+
+ // Once all the simple binding ranges are dealt with, we will bind
+ // all of the sub-objects in sub-object ranges.
+ //
+ for(auto const& subObjectRange : specializedLayout->getSubObjectRanges())
+ {
+ auto subObjectLayout = subObjectRange.layout;
+ auto const& bindingRange = specializedLayout->getBindingRange(subObjectRange.bindingRangeIndex);
+ Index count = bindingRange.count;
+ Index baseIndex = bindingRange.baseIndex;
+
+ // The starting offset for a sub-object range was computed
+ // from Slang reflection information, so we can apply it here.
+ //
+ BindingOffset rangeOffset = offset;
+ rangeOffset += subObjectRange.offset;
+
+ switch(bindingRange.bindingType)
+ {
+ // For D3D11-compatible compilation targets, the Slang compiler
+ // treats the `ConstantBuffer<T>` and `ParameterBlock<T>` types the same.
+ //
+ case slang::BindingType::ConstantBuffer:
+ case slang::BindingType::ParameterBlock:
+ {
+ BindingOffset objOffset = rangeOffset;
+ for(Index i = 0; i < count; ++i)
+ {
+ auto subObject = m_objects[ baseIndex + i ];
+
+ // Unsurprisingly, we bind each object in the range as
+ // a constant buffer.
+ //
+ subObject->bindAsConstantBuffer(context, objOffset, subObjectLayout);
+
+ // TODO: We need to update `objOffset` by the stride for
+ // this range.
+ }
+ }
+ break;
+
+ case slang::BindingType::ExistentialValue:
+ // We can only bind information for existential-typed sub-object
+ // ranges if we have a static type that we are able to specialize to.
+ //
+ if(subObjectLayout)
+ {
+ // The data for objects in this range will always be bound into
+ // the "pending" allocation for the parent block/buffer/object.
+ // As a result, the offset for the first object in the range
+ // will come from the `pending` part of the range's offset.
+ //
+ BindingOffset objOffset = BindingOffset(rangeOffset.pending);
+
+ for(Index i = 0; i < count; ++i)
+ {
+ auto subObject = m_objects[ baseIndex + i ];
+ subObject->bindAsValue(context, objOffset, subObjectLayout);
+
+ // TODO: We need to update `objOffset` by the stride for
+ // this range.
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
- for (auto subObject : m_objects)
- subObject->bindObject(device, bindingState);
-
return SLANG_OK;
}
- /// Any "ordinary" / uniform data for this object
+ // Because the binding ranges have already been reflected
+ // and organized as part of each shader object layout,
+ // the object itself can store its data in a small number
+ // of simple arrays.
+
+ /// Any "ordinary" / uniform data for this object
List<char> m_ordinaryData;
+ /// The shader resource views (SRVs) that are part of the state of this object
List<RefPtr<ShaderResourceViewImpl>> m_srvs;
- List<RefPtr<SamplerStateImpl>> m_samplers;
-
+ /// The unordered access views (UAVs) that are part of the state of this object
List<RefPtr<UnorderedAccessViewImpl>> m_uavs;
+ /// The samplers that are part of the state of this object
+ List<RefPtr<SamplerStateImpl>> m_samplers;
+
+ /// The sub-objects that are part of the state of this object
List<RefPtr<ShaderObjectImpl>> m_objects;
- /// A constant buffer used to stored ordinary data for this object
- /// and existential-type sub-objects.
- ///
- /// Created on demand with `_createOrdinaryDataBufferIfNeeded()`
+ /// A constant buffer used to stored ordinary data for this object
+ /// and existential-type sub-objects.
+ ///
+ /// Created on demand with `_createOrdinaryDataBufferIfNeeded()`
RefPtr<BufferResourceImpl> m_ordinaryDataBuffer;
- /// Get the layout of this shader object with specialization arguments considered
- ///
- /// This operation should only be called after the shader object has been
- /// fully filled in and finalized.
- ///
+ /// Get the layout of this shader object with specialization arguments considered
+ ///
+ /// This operation should only be called after the shader object has been
+ /// fully filled in and finalized.
+ ///
Result _getSpecializedLayout(ShaderObjectLayoutImpl** outLayout)
{
if (!m_specializedLayout)
@@ -1357,21 +1903,67 @@ protected:
return SLANG_OK;
}
- protected:
- virtual Result bindObject(D3D11Device* device, RootBindingState* bindingState) override
+ /// Bind this object as a root shader object
+ Result bindAsRoot(
+ BindingContext* context,
+ RootShaderObjectLayoutImpl* specializedLayout)
{
- SLANG_RETURN_ON_FAIL(Super::bindObject(device, bindingState));
+ // When binding an entire root shader object, we need to deal with
+ // the way that specialization might have allocated space for "pending"
+ // parameter data after all the primary parameters.
+ //
+ // We start by initializing an offset that will store zeros for the
+ // primary data, an the computed offset from the specialized layout
+ // for pending data.
+ //
+ BindingOffset offset;
+ offset.pending = specializedLayout->getPendingDataOffset();
+
+ // Note: We could *almost* call `bindAsConstantBuffer()` here to bind
+ // the state of the root object itself, but there is an important
+ // detail that means we can't:
+ //
+ // The `_bindOrdinaryDataBufferIfNeeded` operation automatically
+ // increments the offset parameter if it binds a buffer, so that
+ // subsequently bindings will be adjusted. However, the reflection
+ // information computed for root shader parameters is absolute rather
+ // than relative to the default constant buffer (if any).
+ //
+ // TODO: Quite technically, the ordinary data buffer for the global
+ // scope is *not* guaranteed to be at offset zero, so this logic should
+ // really be querying an appropriate absolute offset from `specializedLayout`.
+ //
+ BindingOffset ordinaryDataBufferOffset = offset;
+ SLANG_RETURN_ON_FAIL(_bindOrdinaryDataBufferIfNeeded(context, /*inout*/ ordinaryDataBufferOffset, specializedLayout));
+ SLANG_RETURN_ON_FAIL(bindAsValue(context, offset, specializedLayout));
+ // Once the state stored in the root shader object itself has been bound,
+ // we turn our attention to the entry points and their parameters.
+ //
auto entryPointCount = m_entryPoints.getCount();
for (Index i = 0; i < entryPointCount; ++i)
{
auto entryPoint = m_entryPoints[i];
- SLANG_RETURN_ON_FAIL(entryPoint->bindObject(device, bindingState));
+ auto const& entryPointInfo = specializedLayout->getEntryPoint(i);
+
+ // Each entry point will be bound at some offset relative to where
+ // the root shader parameters start.
+ //
+ BindingOffset entryPointOffset = offset;
+ entryPointOffset += entryPointInfo.offset;
+
+ // An entry point can simply be bound as a constant buffer, because
+ // the absolute offsets as are used for the global scope do not apply
+ // (because entry points don't need to deal with explicit bindings).
+ //
+ SLANG_RETURN_ON_FAIL(entryPoint->bindAsConstantBuffer(context, entryPointOffset, entryPointInfo.layout));
}
return SLANG_OK;
}
+
+ protected:
Result init(IDevice* device, RootShaderObjectLayoutImpl* layout)
{
SLANG_RETURN_ON_FAIL(Super::init(device, layout));
@@ -1487,11 +2079,6 @@ protected:
RefPtr<PipelineStateImpl> m_currentPipelineState;
- RootBindingState m_rootBindingState;
-
- bool m_framebufferBindingDirty = true;
- bool m_shaderBindingDirty = true;
-
uint32_t m_stencilRef = 0;
bool m_depthStencilStateDirty = true;
@@ -1817,7 +2404,13 @@ Result D3D11Device::createFramebuffer(
void D3D11Device::setFramebuffer(IFramebuffer* frameBuffer)
{
- m_framebufferBindingDirty = true;
+ // Note: the framebuffer state will be flushed to the pipeline as part
+ // of binding the root shader object.
+ //
+ // TODO: alternatively we could call `OMSetRenderTargets` here and then
+ // call `OMSetRenderTargetsAndUnorderedAccessViews` later with the option
+ // that preserves the existing RTV/DSV bindings.
+ //
m_currentFramebuffer = static_cast<FramebufferImpl*>(frameBuffer);
}
@@ -2669,6 +3262,8 @@ void D3D11Device::setPipelineState(IPipelineState* state)
m_immediateContext->IASetInputLayout(stateImpl->m_inputLayout->m_layout);
// VS
+
+ // TODO(tfoley): Why the conditional here? If somebody is trying to disable the VS or PS, shouldn't we respect that?
if (programImpl->m_vertexShader)
m_immediateContext->VSSetShader(programImpl->m_vertexShader, nullptr, 0);
@@ -2968,29 +3563,91 @@ void D3D11Device::bindRootShaderObject(IShaderObject* shaderObject)
RootShaderObjectImpl* rootShaderObjectImpl = static_cast<RootShaderObjectImpl*>(shaderObject);
RefPtr<PipelineStateBase> specializedPipeline;
maybeSpecializePipeline(m_currentPipelineState, rootShaderObjectImpl, specializedPipeline);
- setPipelineState(specializedPipeline.Ptr());
-
- m_rootBindingState.samplerBindings.clear();
- m_rootBindingState.srvBindings.clear();
- m_rootBindingState.uavBindings.clear();
- m_rootBindingState.constantBuffers.clear();
- static_cast<ShaderObjectImpl*>(shaderObject)->bindObject(this, &m_rootBindingState);
+ PipelineStateImpl* specializedPipelineImpl = static_cast<PipelineStateImpl*>(specializedPipeline.Ptr());
+ setPipelineState(specializedPipelineImpl);
+
+ // In order to bind the root object we must compute its specialized layout.
+ //
+ // TODO: This is in most ways redundant with `maybeSpecializePipeline` above,
+ // and the two operations should really be one.
+ //
+ RefPtr<ShaderObjectLayoutImpl> specializedRootLayout;
+ rootShaderObjectImpl->_getSpecializedLayout(specializedRootLayout.writeRef());
+ RootShaderObjectLayoutImpl* specializedRootLayoutImpl = static_cast<RootShaderObjectLayoutImpl*>(specializedRootLayout.Ptr());
+
+ // Depending on whether we are binding a compute or a graphics/rasterization
+ // pipeline, we will need to bind any SRVs/UAVs/CBs/samplers using different
+ // D3D11 calls. We deal with that distinction here by instantiating an
+ // appropriate subtype of `BindingContext` based on the pipeline type.
+ //
switch (m_currentPipelineState->desc.type)
{
case PipelineType::Compute:
- m_immediateContext->CSSetShaderResources(0, (UINT)m_rootBindingState.srvBindings.getCount(), m_rootBindingState.srvBindings.getBuffer());
- m_immediateContext->CSSetUnorderedAccessViews(0, (UINT)m_rootBindingState.uavBindings.getCount(), m_rootBindingState.uavBindings.getBuffer(), nullptr);
- m_immediateContext->CSSetSamplers(0, (UINT)m_rootBindingState.samplerBindings.getCount(), m_rootBindingState.samplerBindings.getBuffer());
- m_immediateContext->CSSetConstantBuffers(0, (UINT)m_rootBindingState.constantBuffers.getCount(), m_rootBindingState.constantBuffers.getBuffer());
+ {
+ ComputeBindingContext context(this, m_immediateContext);
+ rootShaderObjectImpl->bindAsRoot(&context, specializedRootLayoutImpl);
+
+ // Because D3D11 requires all UAVs to be set at once, we did *not* issue
+ // actual binding calls during the `bindAsRoot` step, and instead we
+ // batch them up and set them here.
+ //
+ m_immediateContext->CSSetUnorderedAccessViews(0, context.uavCount, context.uavs, nullptr);
+ }
break;
default:
- m_immediateContext->VSSetShaderResources(0, (UINT)m_rootBindingState.srvBindings.getCount(), m_rootBindingState.srvBindings.getBuffer());
- m_immediateContext->PSSetShaderResources(0, (UINT)m_rootBindingState.srvBindings.getCount(), m_rootBindingState.srvBindings.getBuffer());
- m_immediateContext->VSSetSamplers(0, (UINT)m_rootBindingState.samplerBindings.getCount(), m_rootBindingState.samplerBindings.getBuffer());
- m_immediateContext->PSSetSamplers(0, (UINT)m_rootBindingState.samplerBindings.getCount(), m_rootBindingState.samplerBindings.getBuffer());
- m_immediateContext->VSSetConstantBuffers(0, (UINT)m_rootBindingState.constantBuffers.getCount(), m_rootBindingState.constantBuffers.getBuffer());
- m_immediateContext->PSSetConstantBuffers(0, (UINT)m_rootBindingState.constantBuffers.getCount(), m_rootBindingState.constantBuffers.getBuffer());
- m_shaderBindingDirty = true;
+ {
+ GraphicsBindingContext context(this, m_immediateContext);
+ rootShaderObjectImpl->bindAsRoot(&context, specializedRootLayoutImpl);
+
+ // Similar to the compute case above, the rasteirzation case needs to
+ // set the UAVs after the call to `bindAsRoot()` completes, but we
+ // also have a few extra wrinkles here that are specific to the D3D 11.0
+ // rasterization pipeline.
+ //
+ // In D3D 11.0, the RTV and UAV binding slots alias, so that a shader
+ // that binds an RTV for `SV_Target0` cannot also bind a UAV for `u0`.
+ // The Slang layout algorithm already accounts for this rule, and assigns
+ // all UAVs to slots taht won't alias the RTVs it knows about.
+ //
+ // In order to account for the aliasing, we need to consider how many
+ // RTVs are bound as part of the active framebuffer, and then adjust
+ // the UAVs that we bind accordingly.
+ //
+ auto rtvCount = (UINT)m_currentFramebuffer->renderTargetViews.getCount();
+ //
+ // The `context` we are using will have computed the number of UAV registers
+ // that might need to be bound, as a range from 0 to `context.uavCount`.
+ // However we need to skip over the first `rtvCount` of those, so the
+ // actual number of UAVs we wnat to bind is smaller:
+ //
+ // Note: As a result we expect that either there were no UAVs bound,
+ // *or* the number of UAV slots bound is higher than the number of
+ // RTVs so that there is something left to actually bind.
+ //
+ SLANG_ASSERT((context.uavCount == 0) || (context.uavCount >= rtvCount));
+ auto bindableUAVCount = context.uavCount - rtvCount;
+ //
+ // Similarly, the actual UAVs we intend to bind will come after the first
+ // `rtvCount` in the array.
+ //
+ auto bindableUAVs = context.uavs + rtvCount;
+
+ // Once the offsetting is accounted for, we set all of the RTVs, DSV,
+ // and UAVs with one call.
+ //
+ // TODO: We may want to use the capability for `OMSetRenderTargetsAnd...`
+ // to only set the UAVs and leave the RTVs/UAVs alone, so that we don't
+ // needlessly re-bind RTVs during a pass.
+ //
+ m_immediateContext->OMSetRenderTargetsAndUnorderedAccessViews(
+ rtvCount,
+ m_currentFramebuffer->d3dRenderTargetViews.getArrayView().getBuffer(),
+ m_currentFramebuffer->d3dDepthStencilView,
+ rtvCount,
+ bindableUAVCount,
+ bindableUAVs,
+ nullptr);
+ }
break;
}
}
@@ -3158,24 +3815,6 @@ void D3D11Device::dispatchCompute(int x, int y, int z)
void D3D11Device::_flushGraphicsState()
{
- auto pipelineType = int(PipelineType::Graphics);
- if (m_framebufferBindingDirty || m_shaderBindingDirty)
- {
- m_framebufferBindingDirty = false;
- m_shaderBindingDirty = false;
-
- auto pipelineState = static_cast<GraphicsPipelineStateImpl*>(m_currentPipelineState.Ptr());
- auto rtvCount = (UINT)m_currentFramebuffer->renderTargetViews.getCount();
- auto uavCount = (UINT)m_rootBindingState.uavBindings.getCount();
- m_immediateContext->OMSetRenderTargetsAndUnorderedAccessViews(
- rtvCount,
- m_currentFramebuffer->d3dRenderTargetViews.getArrayView().getBuffer(),
- m_currentFramebuffer->d3dDepthStencilView,
- rtvCount,
- uavCount,
- m_rootBindingState.uavBindings.getBuffer(),
- nullptr);
- }
if (m_depthStencilStateDirty)
{
m_depthStencilStateDirty = false;
diff --git a/tools/gfx/d3d12/render-d3d12.cpp b/tools/gfx/d3d12/render-d3d12.cpp
index 8cb9ce197..67223088a 100644
--- a/tools/gfx/d3d12/render-d3d12.cpp
+++ b/tools/gfx/d3d12/render-d3d12.cpp
@@ -606,12 +606,18 @@ public:
{
DescriptorHeapReference heap;
uint32_t table;
- };
- struct BindingOffset
- {
- int32_t resource;
- int32_t sampler;
+ /// Get the GPU handle at the specified index
+ SLANG_FORCE_INLINE D3D12_GPU_DESCRIPTOR_HANDLE getGpuHandle(uint32_t index) const
+ {
+ return heap.getGpuHandle(table + index);
+ }
+
+ /// Get the CPU handle at the specified index
+ SLANG_FORCE_INLINE D3D12_CPU_DESCRIPTOR_HANDLE getCpuHandle(uint32_t index) const
+ {
+ return heap.getCpuHandle(table + index);
+ }
};
struct RootBindingState
@@ -619,21 +625,35 @@ public:
TransientResourceHeapImpl* transientHeap;
D3D12Device* device;
ArrayView<DescriptorTable> descriptorTables;
- BindingOffset offset;
- uint32_t rootParamIndex; // The root parameter index of this object.
- uint32_t futureRootParamOffset; // The starting offset of additional sub-object descriptor tables.
};
- struct DescriptorSetInfo
+ struct BindingCounts
{
- uint32_t resourceDescriptorCount = 0;
- uint32_t samplerDescriptorCount = 0;
+ uint32_t rootParam = 0;
+ uint32_t resource = 0;
+ uint32_t sampler = 0;
+
+ BindingCounts()
+ {}
};
- struct BindingLocation
+ struct HeapBindingIndex
{
- int32_t index;
- BindingOffset offsetInDescriptorTable;
+ uint32_t descriptorTableIndex = 0;
+ uint32_t descriptorIndex = 0;
+ };
+
+ struct RootBindingIndex
+ {
+ uint32_t rootParamIndex = 0;
+ HeapBindingIndex resource;
+ HeapBindingIndex sampler;
+ };
+
+ struct DescriptorSetInfo
+ {
+ uint32_t resourceDescriptorCount = 0;
+ uint32_t samplerDescriptorCount = 0;
};
// Provides information on how binding ranges are stored in descriptor tables for
@@ -645,31 +665,79 @@ public:
class ShaderObjectLayoutImpl : public ShaderObjectLayoutBase
{
public:
+
+ /// Information about a single logical binding range
struct BindingRangeInfo
{
+ // Some of the information we store on binding ranges is redundant with
+ // the information that Slang's reflection information stores, but having
+ // it here can make the code more compact and obvious.
+
+ /// The type of binding in this range.
slang::BindingType bindingType;
+
+ /// The number of distinct bindings in this range.
uint32_t count;
- uint32_t spaceIndex;
- uint32_t flatResourceOffset; // Offset in flattend array of resource binding slots.
- BindingLocation binding;
- // Returns true if this binding range consumes a specialization argument slot.
- bool isSpecializationArg() const
+ /// A "flat" index for this range in whatever array provides backing storage for it
+ uint32_t flatIndex;
+ };
+
+ /// Offset information for a sub-object range
+ struct SubObjectRangeOffset : BindingCounts
+ {
+ SubObjectRangeOffset()
+ {}
+
+ SubObjectRangeOffset(slang::VariableLayoutReflection* varLayout)
{
- return bindingType == slang::BindingType::ExistentialValue;
+ if(auto pendingLayout = varLayout->getPendingDataLayout())
+ {
+ pendingOrdinaryData = (uint32_t) pendingLayout->getOffset(SLANG_PARAMETER_CATEGORY_UNIFORM);
+ }
}
+
+ /// The offset for "pending" ordinary data related to this range
+ uint32_t pendingOrdinaryData = 0;
};
+
+ /// Stride information for a sub-object range
+ struct SubObjectRangeStride
+ {
+ SubObjectRangeStride()
+ {}
+
+ SubObjectRangeStride(slang::TypeLayoutReflection* typeLayout)
+ {
+ if(auto pendingLayout = typeLayout->getPendingDataTypeLayout())
+ {
+ pendingOrdinaryData = (uint32_t) pendingLayout->getSize(SLANG_PARAMETER_CATEGORY_UNIFORM);
+ }
+ }
+
+ /// The strid for "pending" ordinary data related to this range
+ uint32_t pendingOrdinaryData = 0;
+ };
+
+ /// Information about a sub-objecrt range
struct SubObjectRangeInfo
{
+ /// The index of the binding range corresponding to this sub-object range
+ Index bindingRangeIndex = 0;
+
+ /// Layout information for the type of sub-object expected to be bound, if known
RefPtr<ShaderObjectLayoutImpl> layout;
- Index bindingRangeIndex;
- slang::BindingType bindingType;
- // The offset for the constant buffer descriptor if this
- // sub-object is referenced as a `ConstantBuffer<T>`.
- // For a `ParameterBlock` binding range, this is always 0 since
- // parameter blocks start in a fresh descriptor table.
- BindingOffset descriptorOffset;
+ /// The offset to use when binding the first object in this range
+ SubObjectRangeOffset offset;
+
+ /// Stride between consecutive objects in this range
+ SubObjectRangeStride stride;
+ };
+
+ struct RootParameterInfo
+ {
+ D3D12_ROOT_PARAMETER rootParameter;
};
struct Builder
@@ -683,9 +751,29 @@ public:
slang::TypeLayoutReflection* m_elementTypeLayout;
List<BindingRangeInfo> m_bindingRanges;
List<SubObjectRangeInfo> m_subObjectRanges;
- DescriptorSetInfo m_descriptorSetInfo;
- uint32_t m_subObjectCount = 0;
- uint32_t m_flatResourceCount = 0;
+// DescriptorSetInfo m_descriptorSetInfo;
+
+// uint32_t m_resourceSlotCount = 0;
+// uint32_t m_samplerSlotCount = 0;
+ uint32_t m_subObjectSlotCount = 0;
+
+ BindingCounts m_ownCounts;
+// BindingCounts m_childCounts;
+ BindingCounts m_totalCounts;
+
+ uint32_t m_childRootParameterCount = 0;
+
+ uint32_t m_ordinaryDataBufferCount = 0;
+
+ uint32_t m_totalOrdinaryDataSize = 0;
+
+
+// uint32_t m_totalResourceDescriptorCount = 0;
+// uint32_t m_totalSamplerDescriptorCount = 0;
+
+// uint32_t m_ownRootParameterCount = 0;
+// uint32_t m_childRootParameterCount = 0;
+// uint32_t m_totalRootParameterCount = 0;
void addBindingRangesOfType(slang::TypeLayoutReflection* typeLayout)
{
@@ -695,7 +783,9 @@ public:
// ordinary uniform data fields.
if (typeLayout->getSize(slang::ParameterCategory::Uniform) != 0)
{
- m_descriptorSetInfo.resourceDescriptorCount = 1;
+ m_ordinaryDataBufferCount++;
+ m_ownCounts.resource++;
+// m_descriptorSetInfo.resourceDescriptorCount = 1;
}
for (SlangInt r = 0; r < bindingRangeCount; ++r)
@@ -707,42 +797,25 @@ public:
BindingRangeInfo bindingRangeInfo = {};
bindingRangeInfo.bindingType = slangBindingType;
bindingRangeInfo.count = count;
- bindingRangeInfo.flatResourceOffset = m_flatResourceCount;
- bindingRangeInfo.spaceIndex =
- (uint32_t)typeLayout->getBindingRangeDescriptorSetIndex(r);
+
+// bindingRangeInfo.flatIndex = m_flatResourceCount;
switch (slangBindingType)
{
case slang::BindingType::ConstantBuffer:
case slang::BindingType::ParameterBlock:
case slang::BindingType::ExistentialValue:
- bindingRangeInfo.binding.index = m_subObjectCount;
- m_subObjectCount += count;
+ bindingRangeInfo.flatIndex = m_subObjectSlotCount;
+ m_subObjectSlotCount += count;
break;
case slang::BindingType::Sampler:
- bindingRangeInfo.binding.offsetInDescriptorTable.sampler =
- m_descriptorSetInfo.samplerDescriptorCount;
- m_descriptorSetInfo.samplerDescriptorCount += count;
+ bindingRangeInfo.flatIndex = m_ownCounts.sampler;
+ m_ownCounts.sampler += count;
break;
case slang::BindingType::CombinedTextureSampler:
- bindingRangeInfo.binding.offsetInDescriptorTable.sampler =
- m_descriptorSetInfo.samplerDescriptorCount;
- bindingRangeInfo.binding.offsetInDescriptorTable.resource =
- m_descriptorSetInfo.resourceDescriptorCount;
- m_descriptorSetInfo.samplerDescriptorCount += count;
- m_descriptorSetInfo.resourceDescriptorCount += count;
- m_flatResourceCount += count;
- break;
-
- case slang::BindingType::MutableRawBuffer:
- case slang::BindingType::MutableTexture:
- case slang::BindingType::MutableTypedBuffer:
- bindingRangeInfo.binding.offsetInDescriptorTable.resource =
- m_descriptorSetInfo.resourceDescriptorCount;
- m_descriptorSetInfo.resourceDescriptorCount += count;
- m_flatResourceCount += count;
+ // TODO: support this case...
break;
case slang::BindingType::VaryingInput:
@@ -750,10 +823,8 @@ public:
break;
default:
- bindingRangeInfo.binding.offsetInDescriptorTable.resource =
- m_descriptorSetInfo.resourceDescriptorCount;
- m_descriptorSetInfo.resourceDescriptorCount += count;
- m_flatResourceCount += count;
+ bindingRangeInfo.flatIndex = m_ownCounts.resource;
+ m_ownCounts.resource += count;
break;
}
m_bindingRanges.add(bindingRangeInfo);
@@ -766,16 +837,21 @@ public:
m_elementTypeLayout = typeLayout;
+ m_totalOrdinaryDataSize = (uint32_t) typeLayout->getSize();
+
// Compute the binding ranges that are used to store
// the logical contents of the object in memory.
addBindingRangesOfType(typeLayout);
+ m_totalCounts = m_ownCounts;
+
SlangInt subObjectRangeCount = typeLayout->getSubObjectRangeCount();
for (SlangInt r = 0; r < subObjectRangeCount; ++r)
{
SlangInt bindingRangeIndex = typeLayout->getSubObjectRangeBindingRangeIndex(r);
auto slangBindingType = typeLayout->getBindingRangeType(bindingRangeIndex);
+ auto count = (uint32_t) typeLayout->getBindingRangeBindingCount(bindingRangeIndex);
slang::TypeLayoutReflection* slangLeafTypeLayout =
typeLayout->getBindingRangeLeafTypeLayout(bindingRangeIndex);
@@ -787,7 +863,17 @@ public:
// know the appropraite type/layout of sub-object to allocate.
//
RefPtr<ShaderObjectLayoutImpl> subObjectLayout;
- if (slangBindingType != slang::BindingType::ExistentialValue)
+ if (slangBindingType == slang::BindingType::ExistentialValue)
+ {
+ if(auto pendingTypeLayout = slangLeafTypeLayout->getPendingDataTypeLayout())
+ {
+ createForElementType(
+ m_renderer,
+ pendingTypeLayout,
+ subObjectLayout.writeRef());
+ }
+ }
+ else
{
createForElementType(
m_renderer,
@@ -798,14 +884,83 @@ public:
SubObjectRangeInfo subObjectRange;
subObjectRange.bindingRangeIndex = bindingRangeIndex;
subObjectRange.layout = subObjectLayout;
- subObjectRange.bindingType = slangBindingType;
- subObjectRange.descriptorOffset.resource =
- m_descriptorSetInfo.resourceDescriptorCount;
- subObjectRange.descriptorOffset.sampler =
- m_descriptorSetInfo.samplerDescriptorCount;
+// subObjectRange.bindingType = slangBindingType;
+
+ subObjectRange.offset = SubObjectRangeOffset(typeLayout->getSubObjectRangeOffset(r));
+ subObjectRange.stride = SubObjectRangeStride(slangLeafTypeLayout);
+
+ subObjectRange.offset.rootParam = m_childRootParameterCount;
+ subObjectRange.offset.resource = m_totalCounts.resource;
+ subObjectRange.offset.sampler = m_totalCounts.sampler;
+
+ BindingCounts objectCounts;
+ switch(slangBindingType)
+ {
+ default:
+ break;
+
+ case slang::BindingType::ConstantBuffer:
+ {
+ SLANG_ASSERT(subObjectLayout);
+
+ // The resource and sampler descriptors of a nested
+ // constant buffer will "leak" into those of the
+ // parent type, and we need to account for them
+ // whenever we allocate storage.
+ //
+
+ objectCounts.resource = subObjectLayout->getTotalResourceDescriptorCount();
+ objectCounts.sampler = subObjectLayout->getTotalSamplerDescriptorCount();
+ objectCounts.rootParam = subObjectRange.layout->getChildRootParameterCount();
+ }
+ break;
+
+ case slang::BindingType::ExistentialValue:
+ if(subObjectLayout)
+ {
+ objectCounts.resource = subObjectLayout->getTotalResourceDescriptorCountWithoutOrdinaryDataBuffer();
+ objectCounts.sampler = subObjectLayout->getTotalSamplerDescriptorCount();
+ objectCounts.rootParam = subObjectRange.layout->getChildRootParameterCount();
+
+ // An interface-type range that includes ordinary data can
+ // increase the size of the ordinary data buffer we need to
+ // allocate for the parent object.
+ //
+ uint32_t ordinaryDataEnd = subObjectRange.offset.pendingOrdinaryData
+ + (uint32_t) count * subObjectRange.stride.pendingOrdinaryData;
+
+ if(ordinaryDataEnd > m_totalOrdinaryDataSize)
+ {
+ m_totalOrdinaryDataSize = ordinaryDataEnd;
+ }
+ }
+ break;
+
+ case slang::BindingType::ParameterBlock:
+ {
+ SLANG_ASSERT(subObjectLayout);
+
+ objectCounts.rootParam = subObjectRange.layout->getTotalRootParameterCount();
+ }
+ break;
+ }
+
+ auto rangeResourceCount = count * objectCounts.resource;
+ auto rangeSamplerCount = count * objectCounts.sampler;
+ auto rangeRootParamCount = count * objectCounts.rootParam;
+
+ m_totalCounts.resource += rangeResourceCount;
+ m_totalCounts.sampler += rangeSamplerCount;
+ m_childRootParameterCount += rangeRootParamCount;
+
m_subObjectRanges.add(subObjectRange);
}
+ if(m_totalCounts.resource) m_ownCounts.rootParam++;
+ if(m_totalCounts.sampler) m_ownCounts.rootParam++;
+
+ m_totalCounts.rootParam = m_ownCounts.rootParam + m_childRootParameterCount;
+
return SLANG_OK;
}
@@ -835,13 +990,26 @@ public:
BindingRangeInfo const& getBindingRange(Index index) { return m_bindingRanges[index]; }
- DescriptorSetInfo getDescriptorSetInfo() { return m_descriptorSetInfo; }
+// DescriptorSetInfo getDescriptorSetInfo() { return m_descriptorSetInfo; }
slang::TypeLayoutReflection* getElementTypeLayout() { return m_elementTypeLayout; }
- uint32_t getResourceCount() { return m_resourceSlotCount; }
+ uint32_t getResourceSlotCount() { return m_ownCounts.resource; }
+ uint32_t getSamplerSlotCount() { return m_ownCounts.sampler; }
+ Index getSubObjectSlotCount() { return m_subObjectSlotCount; }
+
+ uint32_t getTotalResourceDescriptorCount() { return m_totalCounts.resource; }
+ uint32_t getTotalSamplerDescriptorCount() { return m_totalCounts.sampler; }
+
+ uint32_t getOrdinaryDataBufferCount() { return m_ordinaryDataBufferCount; }
+ bool hasOrdinaryDataBuffer() { return m_ordinaryDataBufferCount != 0; }
- Index getSubObjectCount() { return m_subObjectCount; }
+ uint32_t getTotalResourceDescriptorCountWithoutOrdinaryDataBuffer() { return m_totalCounts.resource - m_ordinaryDataBufferCount; }
+
+ uint32_t getTotalRootParameterCount() { return m_totalCounts.rootParam; }
+ uint32_t getChildRootParameterCount() { return m_childRootParameterCount; }
+
+ uint32_t getTotalOrdinaryDataSize() const { return m_totalOrdinaryDataSize; }
SubObjectRangeInfo const& getSubObjectRange(Index index)
{
@@ -860,19 +1028,54 @@ public:
initBase(renderer, builder->m_elementTypeLayout);
- m_descriptorSetInfo = builder->m_descriptorSetInfo;
+// m_descriptorSetInfo = builder->m_descriptorSetInfo;
m_bindingRanges = _Move(builder->m_bindingRanges);
- m_subObjectCount = builder->m_subObjectCount;
m_subObjectRanges = builder->m_subObjectRanges;
- m_resourceSlotCount = builder->m_flatResourceCount;
+
+ m_ownCounts = builder->m_ownCounts;
+ m_totalCounts = builder->m_totalCounts;
+ m_subObjectSlotCount = builder->m_subObjectSlotCount;
+ m_childRootParameterCount = builder->m_childRootParameterCount;
+ m_ordinaryDataBufferCount = builder->m_ordinaryDataBufferCount;
+ m_totalOrdinaryDataSize = builder->m_totalOrdinaryDataSize;
+
+#if 0
+ m_resourceSlotCount = builder->m_resourceSlotCount;
+ m_samplerSlotCount = builder->m_samplerSlotCount;
+ m_subObjectSlotCount = builder->m_subObjectSlotCount;
+
+ m_totalResourceDescriptorCount = builder->m_totalResourceDescriptorCount;
+ m_totalSamplerDescriptorCount = builder->m_totalSamplerDescriptorCount;
+
+ m_childRootParameterCount = builder->m_childRootParameterCount;
+ m_totalRootParameterCount = builder->m_totalRootParameterCount;
+#endif
+
return SLANG_OK;
}
List<BindingRangeInfo> m_bindingRanges;
- DescriptorSetInfo m_descriptorSetInfo;
- Index m_subObjectCount = 0;
List<SubObjectRangeInfo> m_subObjectRanges;
- uint32_t m_resourceSlotCount;
+
+
+// DescriptorSetInfo m_descriptorSetInfo;
+// Index m_subObjectCount = 0;
+
+ BindingCounts m_ownCounts;
+ BindingCounts m_totalCounts;
+
+// uint32_t m_resourceSlotCount;
+// uint32_t m_samplerSlotCount;
+ uint32_t m_subObjectSlotCount;
+
+// uint32_t m_totalResourceDescriptorCount;
+// uint32_t m_totalSamplerDescriptorCount;
+
+ uint32_t m_childRootParameterCount = 0;
+// uint32_t m_totalRootParameterCount = 0;
+
+ uint32_t m_ordinaryDataBufferCount = 0;
+ uint32_t m_totalOrdinaryDataSize = 0;
};
class RootShaderObjectLayoutImpl : public ShaderObjectLayoutImpl
@@ -883,6 +1086,7 @@ public:
struct EntryPointInfo
{
RefPtr<ShaderObjectLayoutImpl> layout;
+ BindingCounts offset;
};
struct Builder : Super::Builder
@@ -914,6 +1118,17 @@ public:
{
EntryPointInfo info;
info.layout = entryPointLayout;
+
+ info.offset.resource = m_totalCounts.resource;
+ info.offset.sampler = m_totalCounts.sampler;
+ info.offset.rootParam = m_childRootParameterCount;
+
+ m_totalCounts.resource += entryPointLayout->getTotalResourceDescriptorCount();
+ m_totalCounts.sampler += entryPointLayout->getTotalSamplerDescriptorCount();
+
+ // TODO(tfoley): Check this to make sure it is reasonable...
+ m_childRootParameterCount += entryPointLayout->getChildRootParameterCount();
+
m_entryPoints.add(info);
}
@@ -976,13 +1191,15 @@ public:
{
uint32_t spaceOffset = 0; // The `space` index as specified in shader.
+ enum { kRangeTypeCount = 4 };
+
/// An offset to apply for each D3D12 register class, as given
/// by a `D3D12_DESCRIPTOR_RANGE_TYPE`.
///
/// Note that the `D3D12_DESCRIPTOR_RANGE_TYPE` enumeration has
/// values between 0 and 3, inclusive.
///
- uint32_t offsetForRangeType[4] = {0, 0, 0, 0};
+ uint32_t offsetForRangeType[kRangeTypeCount] = {0, 0, 0, 0};
uint32_t& operator[](D3D12_DESCRIPTOR_RANGE_TYPE type)
{
@@ -993,8 +1210,60 @@ public:
{
return offsetForRangeType[int(type)];
}
+
+ BindingRegisterOffset()
+ {}
+
+ BindingRegisterOffset(slang::VariableLayoutReflection* varLayout)
+ {
+ if(varLayout)
+ {
+ spaceOffset = (UINT) varLayout->getOffset(SLANG_PARAMETER_CATEGORY_REGISTER_SPACE);
+ offsetForRangeType[D3D12_DESCRIPTOR_RANGE_TYPE_CBV] = (UINT) varLayout->getOffset(SLANG_PARAMETER_CATEGORY_CONSTANT_BUFFER);
+ offsetForRangeType[D3D12_DESCRIPTOR_RANGE_TYPE_SRV] = (UINT) varLayout->getOffset(SLANG_PARAMETER_CATEGORY_SHADER_RESOURCE);
+ offsetForRangeType[D3D12_DESCRIPTOR_RANGE_TYPE_UAV] = (UINT) varLayout->getOffset(SLANG_PARAMETER_CATEGORY_UNORDERED_ACCESS);
+ offsetForRangeType[D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER] = (UINT) varLayout->getOffset(SLANG_PARAMETER_CATEGORY_SAMPLER_STATE);
+ }
+ }
+
+ void operator+=(BindingRegisterOffset const& other)
+ {
+ spaceOffset += other.spaceOffset;
+ for(int i = 0; i < kRangeTypeCount; ++i)
+ {
+ offsetForRangeType[i] += other.offsetForRangeType[i];
+ }
+ }
+
};
+ struct BindingRegisterOffsetPair
+ {
+ BindingRegisterOffset primary;
+ BindingRegisterOffset pending;
+
+ BindingRegisterOffsetPair()
+ {}
+
+ BindingRegisterOffsetPair(slang::VariableLayoutReflection* varLayout)
+ : primary(varLayout)
+ , pending(varLayout->getPendingDataLayout())
+ {}
+
+ void operator+=(BindingRegisterOffsetPair const& other)
+ {
+ primary += other.primary;
+ pending += other.pending;
+ }
+ };
+
+ Index reserveRootParameters(Index count)
+ {
+ Index result = m_rootParameters.getCount();
+ m_rootParameters.setCount(result + count);
+ return result;
+ }
+
/// Add a new descriptor set to the layout being computed.
///
/// Note that a "descriptor set" in the layout may amount to
@@ -1114,33 +1383,15 @@ public:
}
}
- /// Add binding ranges and parameter blocks to the root signature.
- ///
- /// The layout information is taken from `varLayout` which should
- /// be a layout for either a program or an entry point.
- ///
- /// The `physicalDescriptorSetIndex` is the index in the `m_descriptorSets` array of
- /// the descriptor set that binding ranges not belonging to nested
- /// parameter blocks should be added to.
- ///
- /// This routine will use absolute offset information computed from `varLayout`
- /// to apply appropriate space/register offsets to the bindings and parameter
- /// blocks inside the layout.
- ///
- void addBindingRangesAndParameterBlocks(
+ void addAsValue(
slang::VariableLayoutReflection* varLayout,
Index physicalDescriptorSetIndex)
{
- BindingRegisterOffset offset;
- offset.spaceOffset = (UINT) varLayout->getOffset(SLANG_PARAMETER_CATEGORY_REGISTER_SPACE);
- offset[D3D12_DESCRIPTOR_RANGE_TYPE_CBV] = (UINT) varLayout->getOffset(SLANG_PARAMETER_CATEGORY_CONSTANT_BUFFER);
- offset[D3D12_DESCRIPTOR_RANGE_TYPE_SRV] = (UINT) varLayout->getOffset(SLANG_PARAMETER_CATEGORY_SHADER_RESOURCE);
- offset[D3D12_DESCRIPTOR_RANGE_TYPE_UAV] = (UINT) varLayout->getOffset(SLANG_PARAMETER_CATEGORY_UNORDERED_ACCESS);
- offset[D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER] = (UINT) varLayout->getOffset(SLANG_PARAMETER_CATEGORY_SAMPLER_STATE);
-
- addBindingRangesAndParameterBlocks(varLayout->getTypeLayout(), physicalDescriptorSetIndex, offset);
+ BindingRegisterOffsetPair offset(varLayout);
+ addAsValue(varLayout->getTypeLayout(), physicalDescriptorSetIndex, offset);
}
+
/// Add binding ranges and parameter blocks to the root signature.
///
/// The layout information is taken from `typeLayout` which should
@@ -1153,10 +1404,31 @@ public:
/// The `offset` encodes information about space and/or register offsets that
/// should be applied to descrptor ranges.
///
- void addBindingRangesAndParameterBlocks(
+ void addAsConstantBuffer(
slang::TypeLayoutReflection* typeLayout,
Index physicalDescriptorSetIndex,
- BindingRegisterOffset const& offset)
+ BindingRegisterOffsetPair const& containerOffset,
+ BindingRegisterOffsetPair const& elementOffset)
+ {
+ if(typeLayout->getSize(SLANG_PARAMETER_CATEGORY_UNIFORM) != 0)
+ {
+ auto descriptorRangeType = D3D12_DESCRIPTOR_RANGE_TYPE_CBV;
+ auto& offsetForRangeType = containerOffset.primary.offsetForRangeType[descriptorRangeType];
+ addDescriptorRange(
+ physicalDescriptorSetIndex,
+ descriptorRangeType,
+ offsetForRangeType,
+ containerOffset.primary.spaceOffset,
+ 1);
+ }
+
+ addAsValue(typeLayout, physicalDescriptorSetIndex, elementOffset);
+ }
+
+ void addAsValue(
+ slang::TypeLayoutReflection* typeLayout,
+ Index physicalDescriptorSetIndex,
+ BindingRegisterOffsetPair const& offset)
{
// Our first task is to add the binding ranges for stuff that is
// directly contained in `typeLayout` rather than via sub-objects.
@@ -1186,68 +1458,100 @@ public:
// For binding ranges that don't represent sub-objects, we will add
// all of the descriptor ranges they encompass to the root signature.
//
- addBindingRange(typeLayout, physicalDescriptorSetIndex, offset, bindingRangeIndex);
+ addBindingRange(typeLayout, physicalDescriptorSetIndex, offset.primary, bindingRangeIndex);
}
- // Next we need to add any sub binding ranges in `ConstantBuffer` bindings.
- //
- Index subObjectCount = typeLayout->getSubObjectRangeCount();
- for (Index subObjectRangeIndex = 0; subObjectRangeIndex < subObjectCount; subObjectRangeIndex++)
+ // Next we need to recursively include everything bound via sub-objects
+ Index subObjectRangeCount = typeLayout->getSubObjectRangeCount();
+ for (Index subObjectRangeIndex = 0; subObjectRangeIndex < subObjectRangeCount; subObjectRangeIndex++)
{
auto bindingRangeIndex = typeLayout->getSubObjectRangeBindingRangeIndex(subObjectRangeIndex);
auto bindingType = typeLayout->getBindingRangeType(bindingRangeIndex);
- switch (bindingType)
+
+ auto subObjectTypeLayout = typeLayout->getBindingRangeLeafTypeLayout(bindingRangeIndex);
+
+ BindingRegisterOffsetPair subObjectRangeOffset = offset;
+ subObjectRangeOffset += BindingRegisterOffsetPair(typeLayout->getSubObjectRangeOffset(subObjectRangeIndex));
+
+ switch(bindingType)
{
case slang::BindingType::ConstantBuffer:
{
- // Constant buffer ranges (for `ConstantBuffer<ConcreteType>`) will "leak" their
- // binding ranges into the surrounding type, so we can add them here like any other
- // binding range.
- //
- // Note: It would be valid to allow `slang::BindingType::ConstantBuffer` to be handled
- // in the earlier loop, but that would mean that descriptor ranges coming directly
- // from the fields of `typeLayout` could be broken up with ranges coming from constant-buffer
- // sub-objects. By moving the handling of constant buffers to this later loop, we
- // guarantee that the descritpors used by non-sub-object binding ranges are all
- // contiguous.
- //
- // This call will add all descriptor ranges reported in `typeLayout` that is associated
- // with `bindingRangeIndex`.
- //
- addBindingRange(
- typeLayout,
- physicalDescriptorSetIndex,
- offset,
- bindingRangeIndex);
+ auto containerVarLayout = subObjectTypeLayout->getContainerVarLayout();
+ SLANG_ASSERT(containerVarLayout);
- // We also need to recurse into the element type of the constant buffer to add
- // any binding ranges defined in the element type.
- auto subObjectType =
- typeLayout->getBindingRangeLeafTypeLayout(bindingRangeIndex);
- BindingRegisterOffset subOffset;
- subOffset.spaceOffset =
- offset.spaceOffset +
- (uint32_t)typeLayout->getSubObjectRangeSpaceOffset(
- subObjectRangeIndex);
- addParameterBlocks(
- _unwrapParameterGroups(subObjectType),
- physicalDescriptorSetIndex,
- subOffset);
+ auto elementVarLayout = subObjectTypeLayout->getElementVarLayout();
+ SLANG_ASSERT(elementVarLayout);
+
+ auto elementTypeLayout = elementVarLayout->getTypeLayout();
+ SLANG_ASSERT(elementTypeLayout);
+
+ BindingRegisterOffsetPair containerOffset = subObjectRangeOffset;
+ containerOffset += BindingRegisterOffsetPair(containerVarLayout);
+
+ BindingRegisterOffsetPair elementOffset = subObjectRangeOffset;
+ elementOffset += BindingRegisterOffsetPair(elementVarLayout);
+
+ addAsConstantBuffer(elementTypeLayout, physicalDescriptorSetIndex, containerOffset, elementOffset);
}
break;
- default:
+
+ case slang::BindingType::ParameterBlock:
+ {
+ auto containerVarLayout = subObjectTypeLayout->getContainerVarLayout();
+ SLANG_ASSERT(containerVarLayout);
+
+ auto elementVarLayout = subObjectTypeLayout->getElementVarLayout();
+ SLANG_ASSERT(elementVarLayout);
+
+ auto elementTypeLayout = elementVarLayout->getTypeLayout();
+ SLANG_ASSERT(elementTypeLayout);
+
+ BindingRegisterOffsetPair subDescriptorSetOffset;
+ subDescriptorSetOffset.primary.spaceOffset = subObjectRangeOffset.primary.spaceOffset;
+ subDescriptorSetOffset.pending.spaceOffset = subObjectRangeOffset.pending.spaceOffset;
+
+ auto subPhysicalDescriptorSetIndex = addDescriptorSet();
+
+ BindingRegisterOffsetPair containerOffset = subDescriptorSetOffset;
+ containerOffset += BindingRegisterOffsetPair(containerVarLayout);
+
+ BindingRegisterOffsetPair elementOffset = subDescriptorSetOffset;
+ elementOffset += BindingRegisterOffsetPair(elementVarLayout);
+
+ addAsConstantBuffer(elementTypeLayout, subPhysicalDescriptorSetIndex, containerOffset, elementOffset);
+ }
+ break;
+
+ case slang::BindingType::ExistentialValue:
+ {
+ // Any nested binding ranges in the sub-object will "leak" into the
+ // binding ranges for the surrounding context.
+ //
+ auto specializedTypeLayout = subObjectTypeLayout->getPendingDataTypeLayout();
+ if(specializedTypeLayout)
+ {
+ BindingRegisterOffsetPair pendingOffset;
+ pendingOffset.primary = subObjectRangeOffset.pending;
+
+ addAsValue(specializedTypeLayout, physicalDescriptorSetIndex, pendingOffset);
+ }
+ }
break;
}
}
- addParameterBlocks(typeLayout, physicalDescriptorSetIndex, offset);
+// BindingRegisterOffsetPair pendingOffset;
+// pendingOffset.primary = offset.pending;
+// addPendingResourceBindingRanges(typeLayout, physicalDescriptorSetIndex, pendingOffset);
}
+#if 0
/// Add child parameter blocks defined in `typeLayout` to the root signature.
void addParameterBlocks(
- slang::TypeLayoutReflection* typeLayout,
- Index physicalDescriptorSetIndex,
- BindingRegisterOffset const& offset)
+ slang::TypeLayoutReflection* typeLayout,
+ Index physicalDescriptorSetIndex,
+ BindingRegisterOffsetPair const& offset)
{
Index subObjectCount = typeLayout->getSubObjectRangeCount();
for (Index subObjectRangeIndex = 0; subObjectRangeIndex < subObjectCount;
@@ -1261,6 +1565,24 @@ public:
auto bindingType = typeLayout->getBindingRangeType(bindingRangeIndex);
switch (bindingType)
{
+ case slang::BindingType::ConstantBuffer:
+ {
+ // We also need to recurse into the element type of the constant buffer to add
+ // any binding ranges defined in the element type.
+ auto subObjectType =
+ typeLayout->getBindingRangeLeafTypeLayout(bindingRangeIndex);
+ auto spaceOffset = (uint32_t)typeLayout->getSubObjectRangeSpaceOffset(subObjectRangeIndex);
+
+ BindingRegisterOffsetPair subOffset;
+ subOffset.primary.spaceOffset = offset.primary.spaceOffset + spaceOffset;
+ subOffset.pending.spaceOffset = offset.pending.spaceOffset + spaceOffset;
+ addParameterBlocks(
+ _unwrapParameterGroups(subObjectType),
+ physicalDescriptorSetIndex,
+ subOffset);
+ }
+ break;
+
case slang::BindingType::ParameterBlock:
{
// A parameter block (`ParameterBlock<ConcreteType>`) will always map to
@@ -1293,8 +1615,9 @@ public:
// this point, because `register` offsets from outside of the block
// don't affect layout within the block.
//
- BindingRegisterOffset blockOffset;
- blockOffset.spaceOffset = offset.spaceOffset + (uint32_t)spaceOffset;
+ BindingRegisterOffsetPair blockOffset;
+ blockOffset.primary.spaceOffset = offset.primary.spaceOffset + (uint32_t)spaceOffset;
+ blockOffset.pending.spaceOffset = offset.pending.spaceOffset + (uint32_t)spaceOffset;
// Note: there is an important subtlety going on here. We are passing in
// the type `blockTypeLayout` which corresponds to
@@ -1311,15 +1634,15 @@ public:
blockPhysicalDescriptorSetIndex,
D3D12_DESCRIPTOR_RANGE_TYPE_CBV,
0,
- blockOffset.spaceOffset,
+ blockOffset.primary.spaceOffset,
1);
- blockOffset.offsetForRangeType[D3D12_DESCRIPTOR_RANGE_TYPE_CBV] = 1;
+ blockOffset.primary.offsetForRangeType[D3D12_DESCRIPTOR_RANGE_TYPE_CBV] = 1;
}
// Once we have all the details worked out, we can write the binding
// ranges for the block's type into the newly-allocated descriptor set.
//
- addBindingRangesAndParameterBlocks(
+ addAsConstantBuffer(
elementLayout, blockPhysicalDescriptorSetIndex, blockOffset);
}
break;
@@ -1332,7 +1655,109 @@ public:
break;
}
}
+
+// addPendingParameterBlocks(typeLayout, physicalDescriptorSetIndex, offset);
}
+#endif
+
+#if 0
+ void addPendingResourceBindingRanges(
+ slang::TypeLayoutReflection* typeLayout,
+ Index physicalDescriptorSetIndex,
+ BindingRegisterOffsetPair const& offset)
+ {
+ Index subObjectRangeCount = typeLayout->getSubObjectRangeCount();
+ for (Index subObjectRangeIndex = 0; subObjectRangeIndex < subObjectRangeCount; subObjectRangeIndex++)
+ {
+ auto bindingRangeIndex = typeLayout->getSubObjectRangeBindingRangeIndex(subObjectRangeIndex);
+ auto bindingType = typeLayout->getBindingRangeType(bindingRangeIndex);
+ switch (bindingType)
+ {
+ case slang::BindingType::ExistentialValue:
+ {
+ // Any nested binding ranges in the sub-object will "leak" into the
+ // binding ranges for the surrounding context.
+ //
+ auto subObjectTypeLayout = typeLayout->getBindingRangeLeafTypeLayout(bindingRangeIndex);
+ auto specializedTypeLayout = subObjectTypeLayout->getPendingDataTypeLayout();
+ if(specializedTypeLayout)
+ {
+ // TODO: We need to compute the offsets that should be applied to
+ // any resources bound via the sub-object.
+ BindingRegisterOffsetPair subOffset = offset;
+ subOffset.primary += BindingRegisterOffset(typeLayout->getSubObjectRangePendingDataOffset(subObjectRangeIndex));
+
+ addResourceBindingRanges(specializedTypeLayout, physicalDescriptorSetIndex, subOffset);
+ }
+ }
+ break;
+
+ case slang::BindingType::ConstantBuffer:
+ {
+ auto subObjectTypeLayout = typeLayout->getBindingRangeLeafTypeLayout(bindingRangeIndex);
+ auto elementTypeLayout = subObjectTypeLayout->getElementTypeLayout();
+ SLANG_ASSERT(elementTypeLayout);
+
+ BindingRegisterOffsetPair subOffset = offset;
+ subOffset.primary += BindingRegisterOffset(typeLayout->getSubObjectRangePendingDataOffset(subObjectRangeIndex));
+
+ addPendingResourceBindingRanges(elementTypeLayout, physicalDescriptorSetIndex, subOffset);
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+#endif
+
+#if 0
+ void addPendingParameterBlocks(
+ slang::TypeLayoutReflection* typeLayout,
+ Index physicalDescriptorSetIndex,
+ BindingRegisterOffset const& offset)
+ {
+ Index subObjectRangeCount = typeLayout->getSubObjectRangeCount();
+ for (Index subObjectRangeIndex = 0; subObjectRangeIndex < subObjectRangeCount; subObjectRangeIndex++)
+ {
+ auto bindingRangeIndex = typeLayout->getSubObjectRangeBindingRangeIndex(subObjectRangeIndex);
+ auto bindingType = typeLayout->getBindingRangeType(bindingRangeIndex);
+ switch (bindingType)
+ {
+ case slang::BindingType::ExistentialValue:
+ {
+ // Any nested binding ranges in the sub-object will "leak" into the
+ // binding ranges for the surrounding context.
+ //
+ auto subObjectTypeLayout = typeLayout->getBindingRangeLeafTypeLayout(bindingRangeIndex);
+ auto pendingTypeLayout = subObjectTypeLayout->getPendingDataTypeLayout();
+ if(pendingTypeLayout)
+ {
+ // TODO: We need to compute the offsets that should be applied to
+ // any resources bound via the sub-object.
+ BindingRegisterOffset subOffset = offset;
+
+ addParameterBlocks(pendingTypeLayout, physicalDescriptorSetIndex, subOffset);
+ }
+ }
+ break;
+
+ case slang::BindingType::ConstantBuffer:
+ case slang::BindingType::ParameterBlock:
+ {
+ auto subObjectTypeLayout = typeLayout->getBindingRangeLeafTypeLayout(bindingRangeIndex);
+ BindingRegisterOffset subOffset = offset;
+ addPendingParameterBlocks(subObjectTypeLayout, physicalDescriptorSetIndex, subOffset);
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+#endif
D3D12_ROOT_SIGNATURE_DESC& build(
List<D3D12Device::DescriptorSetInfo>& outRootDescriptorSetInfos)
@@ -1385,6 +1810,7 @@ public:
static Result createRootSignatureFromSlang(
D3D12Device* device,
+ RootShaderObjectLayoutImpl* rootLayout,
slang::IComponentType* program,
ID3D12RootSignature** outRootSignature,
List<DescriptorSetInfo>& outRootDescriptorSetInfos)
@@ -1411,7 +1837,7 @@ public:
// parameters.
//
auto rootDescriptorSetIndex = builder.addDescriptorSet();
- builder.addBindingRangesAndParameterBlocks(layout->getGlobalParamsVarLayout(), rootDescriptorSetIndex);
+ builder.addAsValue(layout->getGlobalParamsVarLayout(), rootDescriptorSetIndex);
for (SlangUInt i = 0; i < layout->getEntryPointCount(); i++)
{
@@ -1428,7 +1854,7 @@ public:
// being included in the global root signature as is being done here.
//
auto entryPoint = layout->getEntryPointByIndex(i);
- builder.addBindingRangesAndParameterBlocks(entryPoint->getVarLayout(), rootDescriptorSetIndex);
+ builder.addAsValue(entryPoint->getVarLayout(), rootDescriptorSetIndex);
}
auto& rootSignatureDesc = builder.build(outRootDescriptorSetInfos);
@@ -1476,7 +1902,8 @@ public:
builder.addEntryPoint(slangEntryPoint->getStage(), entryPointLayout);
}
- SLANG_RETURN_ON_FAIL(builder.build(outLayout));
+ RefPtr<RootShaderObjectLayoutImpl> layout;
+ SLANG_RETURN_ON_FAIL(builder.build(layout.writeRef()));
if (program->getSpecializationParamCount() == 0)
{
@@ -1488,10 +1915,14 @@ public:
// it in `m_gpuDescriptorSetInfos`.
SLANG_RETURN_ON_FAIL(createRootSignatureFromSlang(
device,
+ layout,
program,
- (*outLayout)->m_rootSignature.writeRef(),
- (*outLayout)->m_gpuDescriptorSetInfos));
+ layout->m_rootSignature.writeRef(),
+ layout->m_gpuDescriptorSetInfos));
}
+
+ *outLayout = layout.detach();
+
return SLANG_OK;
}
@@ -1548,16 +1979,17 @@ public:
~ShaderObjectImpl()
{
- auto layoutImpl = static_cast<ShaderObjectLayoutImpl*>(m_layout.Ptr());
- if (m_descriptorSet.m_resourceCount)
- {
- m_resourceHeap.freeIfSupported(
- m_descriptorSet.m_resourceTable, m_descriptorSet.m_resourceCount);
- }
- if (m_descriptorSet.m_samplerCount)
+ auto layoutImpl = getLayout();
+ if(layoutImpl)
{
- m_samplerHeap.freeIfSupported(
- m_descriptorSet.m_samplerTable, m_descriptorSet.m_samplerCount);
+ if(auto resourceCount = layoutImpl->getResourceSlotCount())
+ {
+ m_resourceHeap.freeIfSupported(m_descriptorSet.m_resourceTable, resourceCount);
+ }
+ if(auto samplerCount = layoutImpl->getSamplerSlotCount())
+ {
+ m_samplerHeap.freeIfSupported(m_descriptorSet.m_samplerTable, samplerCount);
+ }
}
}
@@ -1625,7 +2057,7 @@ public:
auto bindingRangeIndex = offset.bindingRangeIndex;
auto& bindingRange = layout->getBindingRange(bindingRangeIndex);
- m_objects[bindingRange.binding.index + offset.bindingArrayIndex] = subObject;
+ m_objects[bindingRange.flatIndex + offset.bindingArrayIndex] = subObject;
// If the range being assigned into represents an interface/existential-type leaf field,
// then we need to consider how the `object` being assigned here affects specialization.
@@ -1734,7 +2166,7 @@ public:
return SLANG_E_INVALID_ARG;
auto& bindingRange = layout->getBindingRange(offset.bindingRangeIndex);
- returnComPtr(outObject, m_objects[bindingRange.binding.index + offset.bindingArrayIndex]);
+ returnComPtr(outObject, m_objects[bindingRange.flatIndex + offset.bindingArrayIndex]);
return SLANG_OK;
}
@@ -1750,18 +2182,16 @@ public:
auto resourceViewImpl = static_cast<ResourceViewImpl*>(resourceView);
auto& bindingRange = layout->getBindingRange(offset.bindingRangeIndex);
- auto descriptorSlotIndex = bindingRange.binding.offsetInDescriptorTable.resource +
- (int32_t)offset.bindingArrayIndex;
+ auto descriptorSlotIndex = bindingRange.flatIndex + (int32_t)offset.bindingArrayIndex;
// Hold a reference to the resource to prevent its destruction.
- m_boundResources[bindingRange.flatResourceOffset + offset.bindingArrayIndex] =
+ m_boundResources[bindingRange.flatIndex + offset.bindingArrayIndex] =
resourceViewImpl->m_resource;
ID3D12Device* d3dDevice = static_cast<D3D12Device*>(getDevice())->m_device;
d3dDevice->CopyDescriptorsSimple(
1,
m_resourceHeap.getCpuHandle(
m_descriptorSet.m_resourceTable +
- bindingRange.binding.offsetInDescriptorTable.resource +
- (int32_t)offset.bindingArrayIndex),
+ bindingRange.flatIndex + (int32_t)offset.bindingArrayIndex),
resourceViewImpl->m_descriptor.cpuHandle,
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
return SLANG_OK;
@@ -1782,7 +2212,7 @@ public:
1,
m_samplerHeap.getCpuHandle(
m_descriptorSet.m_samplerTable +
- bindingRange.binding.offsetInDescriptorTable.sampler +
+ bindingRange.flatIndex +
(int32_t)offset.bindingArrayIndex),
samplerImpl->m_descriptor.cpuHandle,
D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
@@ -1794,6 +2224,7 @@ public:
IResourceView* textureView,
ISamplerState* sampler) SLANG_OVERRIDE
{
+#if 0
if (offset.bindingRangeIndex < 0)
return SLANG_E_INVALID_ARG;
auto layout = getLayout();
@@ -1819,6 +2250,7 @@ public:
(int32_t)offset.bindingArrayIndex),
samplerImpl->m_descriptor.cpuHandle,
D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
+#endif
return SLANG_OK;
}
@@ -1844,7 +2276,7 @@ public:
SLANG_ASSERT(count == 1);
Index subObjectIndexInRange = 0;
- auto subObject = m_objects[bindingRange.binding.index + subObjectIndexInRange];
+ auto subObject = m_objects[bindingRange.flatIndex + subObjectIndexInRange];
switch (bindingRange.bindingType)
{
@@ -1913,29 +2345,39 @@ public:
memset(m_ordinaryData.getBuffer(), 0, uniformSize);
}
- // Allocate descriptor tables for this shader object.
+ // Each shader object will own CPU descriptor heap memory
+ // for any resource or sampler descriptors it might store
+ // as part of its value.
+ //
+ // This allocate includes a reservation for any constant
+ // buffer descriptor pertaining to the ordinary data,
+ // but does *not* include any descriptors that are managed
+ // as part of sub-objects.
+ //
m_resourceHeap = viewHeap;
m_samplerHeap = samplerHeap;
- auto descSetInfo = layout->getDescriptorSetInfo();
- m_descriptorSet.m_resourceCount = descSetInfo.resourceDescriptorCount;
- if (descSetInfo.resourceDescriptorCount)
+
+ if(auto resourceCount = layout->getResourceSlotCount())
{
- m_descriptorSet.m_resourceTable =
- viewHeap.allocate(descSetInfo.resourceDescriptorCount);
+ m_descriptorSet.m_resourceTable = viewHeap.allocate(resourceCount);
+
+ // We must also ensure that the memory for any resources
+ // referenced by descriptors in this object does not get
+ // freed while the object is still live.
+ //
+ m_boundResources.setCount(resourceCount);
}
- m_descriptorSet.m_samplerCount = descSetInfo.samplerDescriptorCount;
- if (descSetInfo.samplerDescriptorCount)
+
+ if(auto samplerCount = layout->getSamplerSlotCount())
{
- m_descriptorSet.m_samplerTable =
- samplerHeap.allocate(descSetInfo.samplerDescriptorCount);
+ m_descriptorSet.m_samplerTable = samplerHeap.allocate(samplerCount);
}
- m_boundResources.setCount(layout->getResourceCount());
// If the layout specifies that we have any sub-objects, then
// we need to size the array to account for them.
//
- Index subObjectCount = layout->getSubObjectCount();
+ Index subObjectCount = layout->getSubObjectSlotCount();
m_objects.setCount(subObjectCount);
for (auto subObjectRangeInfo : layout->getSubObjectRanges())
@@ -1962,7 +2404,7 @@ public:
RefPtr<ShaderObjectImpl> subObject;
SLANG_RETURN_ON_FAIL(
ShaderObjectImpl::create(device, subObjectLayout, subObject.writeRef()));
- m_objects[bindingRangeInfo.binding.index + i] = subObject;
+ m_objects[bindingRangeInfo.flatIndex + i] = subObject;
}
}
@@ -2039,10 +2481,8 @@ public:
// layout logic does for complex cases with multiple layers of nested arrays and
// structures.
//
- size_t subObjectRangePendingDataOffset =
- _getSubObjectRangePendingDataOffset(specializedLayout, subObjectRangeIndex);
- size_t subObjectRangePendingDataStride =
- _getSubObjectRangePendingDataStride(specializedLayout, subObjectRangeIndex);
+ size_t subObjectRangePendingDataOffset = subObjectRangeInfo.offset.pendingOrdinaryData;
+ size_t subObjectRangePendingDataStride = subObjectRangeInfo.stride.pendingOrdinaryData;
// If the range doesn't actually need/use the "pending" allocation at all, then
// we need to detect that case and skip such ranges.
@@ -2056,7 +2496,7 @@ public:
for (uint32_t i = 0; i < count; ++i)
{
- auto subObject = m_objects[bindingRangeInfo.binding.index + i];
+ auto subObject = m_objects[bindingRangeInfo.flatIndex + i];
RefPtr<ShaderObjectLayoutImpl> subObjectLayout;
SLANG_RETURN_ON_FAIL(
@@ -2077,24 +2517,10 @@ public:
return SLANG_OK;
}
- // As discussed in `_writeOrdinaryData()`, these methods are just stubs waiting for
- // the "flat" Slang refelction information to provide access to the relevant data.
- //
- size_t _getSubObjectRangePendingDataOffset(
- ShaderObjectLayoutImpl* specializedLayout,
- Index subObjectRangeIndex)
- {
- return 0;
- }
- size_t _getSubObjectRangePendingDataStride(
- ShaderObjectLayoutImpl* specializedLayout,
- Index subObjectRangeIndex)
- {
- return 0;
- }
-
/// Ensure that the `m_ordinaryDataBuffer` has been created, if it is needed
- Result _ensureOrdinaryDataBufferCreatedIfNeeded(PipelineCommandEncoder* encoder)
+ Result _ensureOrdinaryDataBufferCreatedIfNeeded(
+ PipelineCommandEncoder* encoder,
+ ShaderObjectLayoutImpl* specializedLayout)
{
// If we have already created a buffer to hold ordinary data, then we should
// simply re-use that buffer rather than re-create it.
@@ -2117,15 +2543,7 @@ public:
// store the concrete values that logically belong in those interface-type
// fields but wouldn't fit in the fixed-size allocation we gave them.
//
- // TODO: We need to actually implement that logic by using reflection
- // data computed for the specialized type of this shader object.
- // For now we just make the simple assumption described above despite
- // knowing that it is false.
- //
- RefPtr<ShaderObjectLayoutImpl> specializedLayout;
- SLANG_RETURN_ON_FAIL(getSpecializedLayout(specializedLayout.writeRef()));
-
- m_constantBufferSize = specializedLayout->getElementTypeLayout()->getSize();
+ m_constantBufferSize = specializedLayout->getTotalOrdinaryDataSize();
if (m_constantBufferSize == 0)
{
m_upToDateConstantBufferHeapVersion =
@@ -2180,78 +2598,197 @@ public:
}
public:
- Result bindObject(PipelineCommandEncoder* encoder, RootBindingState* bindingState)
+ RootBindingIndex prepareToBindAsParameterBlock(uint32_t inRootParameterIndex)
{
ShaderObjectLayoutImpl* layout = getLayout();
- SLANG_RETURN_ON_FAIL(_ensureOrdinaryDataBufferCreatedIfNeeded(encoder));
- uint32_t descTableIndex = bindingState->rootParamIndex;
+
+ auto rootParameterIndex = inRootParameterIndex;
+
+ RootBindingIndex rootBindingIndex;
+
+ if(layout->getTotalResourceDescriptorCount())
+ {
+ rootBindingIndex.resource.descriptorTableIndex = rootParameterIndex++;
+ }
+ if(layout->getTotalSamplerDescriptorCount())
+ {
+ rootBindingIndex.sampler.descriptorTableIndex = rootParameterIndex++;
+ }
+ rootBindingIndex.rootParamIndex = rootParameterIndex;
+
+ return rootBindingIndex;
+ }
+
+ Result bindAsParameterBlock(PipelineCommandEncoder* encoder, RootBindingState& bindingState, uint32_t rootParameterIndex, ShaderObjectLayoutImpl* layout)
+ {
+ auto rootBindingIndex = prepareToBindAsParameterBlock(rootParameterIndex);
+ SLANG_RETURN_ON_FAIL(bindAsConstantBuffer(encoder, bindingState, rootBindingIndex, layout));
+ return SLANG_OK;
+ }
+
+ Result bindAsConstantBuffer(
+ PipelineCommandEncoder* encoder,
+ RootBindingState& bindingState,
+ RootBindingIndex const& rootBindingIndex,
+ ShaderObjectLayoutImpl* specializedLayout)
+ {
+ SLANG_RETURN_ON_FAIL(_ensureOrdinaryDataBufferCreatedIfNeeded(encoder, specializedLayout));
+ SLANG_RETURN_ON_FAIL(_bindImpl(encoder, bindingState, rootBindingIndex, specializedLayout, 0));
+ return SLANG_OK;
+ }
+
+ Result bindAsValue(
+ PipelineCommandEncoder* encoder,
+ RootBindingState& bindingState,
+ RootBindingIndex const& rootBindingIndex,
+ ShaderObjectLayoutImpl* layout)
+ {
+ SLANG_RETURN_ON_FAIL(_bindImpl(encoder, bindingState, rootBindingIndex, layout, layout->getOrdinaryDataBufferCount()));
+ return SLANG_OK;
+ }
+
+ Result _bindImpl(
+ PipelineCommandEncoder* encoder,
+ RootBindingState& bindingState,
+ RootBindingIndex const& rootBindingIndex,
+ ShaderObjectLayoutImpl* layout,
+ uint32_t skipResourceCount)
+ {
auto& descSet = m_descriptorSet;
- if (descSet.m_resourceCount)
+ if(auto resourceCount = (layout->getResourceSlotCount() - skipResourceCount))
{
- auto gpuDescriptorTable = bindingState->descriptorTables[descTableIndex];
- auto& gpuHeap = gpuDescriptorTable.heap;
+ auto dstIndex = rootBindingIndex.resource;
+ auto& dstTable = bindingState.descriptorTables[dstIndex.descriptorTableIndex];
auto& cpuHeap = m_resourceHeap;
auto cpuDescriptorTable = descSet.m_resourceTable;
- bindingState->device->m_device->CopyDescriptorsSimple(
- UINT(descSet.m_resourceCount),
- gpuHeap.getCpuHandle(
- gpuDescriptorTable.table + bindingState->offset.resource),
- cpuHeap.getCpuHandle(cpuDescriptorTable),
+ bindingState.device->m_device->CopyDescriptorsSimple(
+ UINT(resourceCount),
+ dstTable.getCpuHandle(dstIndex.descriptorIndex),
+ cpuHeap.getCpuHandle(cpuDescriptorTable + skipResourceCount),
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
- bindingState->offset.resource += descSet.m_resourceCount;
- descTableIndex++;
}
- if (descSet.m_samplerCount)
+ if (auto samplerCount = layout->getSamplerSlotCount())
{
- auto gpuDescriptorTable = bindingState->descriptorTables[descTableIndex];
- auto& gpuHeap = gpuDescriptorTable.heap;
+ auto dstIndex = rootBindingIndex.sampler;
+ auto& dstTable = bindingState.descriptorTables[dstIndex.descriptorTableIndex];
auto& cpuHeap = m_samplerHeap;
auto cpuDescriptorTable = (int)descSet.m_samplerTable;
- bindingState->device->m_device->CopyDescriptorsSimple(
- UINT(descSet.m_samplerCount),
- gpuHeap.getCpuHandle(
- gpuDescriptorTable.table + bindingState->offset.sampler),
+ bindingState.device->m_device->CopyDescriptorsSimple(
+ UINT(samplerCount),
+ dstTable.getCpuHandle(dstIndex.descriptorIndex),
cpuHeap.getCpuHandle(cpuDescriptorTable),
D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
- bindingState->offset.sampler += descSet.m_samplerCount;
- descTableIndex++;
}
- bindingState->futureRootParamOffset =
- Math::Max(descTableIndex, bindingState->futureRootParamOffset);
+
auto& subObjectRanges = layout->getSubObjectRanges();
- for (Index i = 0; i < subObjectRanges.getCount(); i++)
+ auto subObjectRangeCount = subObjectRanges.getCount();
+ for (Index i = 0; i < subObjectRangeCount; i++)
{
- auto bindingRange =
- layout->getBindingRange(layout->getSubObjectRange(i).bindingRangeIndex);
- switch (layout->getSubObjectRange(i).bindingType)
+ auto subObjectRange = layout->getSubObjectRange(i);
+ auto bindingRange = layout->getBindingRange(subObjectRange.bindingRangeIndex);
+ auto subObjectLayout = subObjectRange.layout.Ptr();
+ switch(bindingRange.bindingType)
{
+ case slang::BindingType::ConstantBuffer:
+ {
+ auto baseIndex = bindingRange.flatIndex;
+ for (uint32_t j = 0; j < bindingRange.count; j++)
+ {
+ auto& object = m_objects[baseIndex + j];
+
+ RootBindingIndex subIndex = rootBindingIndex;
+ subIndex.rootParamIndex += subObjectRange.offset.rootParam;
+ subIndex.resource.descriptorIndex += subObjectRange.offset.resource;
+ subIndex.sampler.descriptorTableIndex += subObjectRange.offset.sampler;
+
+ object->bindAsConstantBuffer(
+ encoder,
+ bindingState,
+ subIndex,
+ subObjectLayout);
+ }
+ }
+ break;
+
case slang::BindingType::ParameterBlock:
{
- auto baseIndex = bindingRange.binding.index;
+ auto baseIndex = bindingRange.flatIndex;
for (uint32_t j = 0; j < bindingRange.count; j++)
{
- auto newBindingState = *bindingState;
- newBindingState.offset.resource = 0;
- newBindingState.offset.sampler = 0;
- newBindingState.rootParamIndex = bindingState->futureRootParamOffset;
- newBindingState.futureRootParamOffset = newBindingState.rootParamIndex;
- m_objects[baseIndex + j]->bindObject(encoder, &newBindingState);
- bindingState->futureRootParamOffset =
- newBindingState.futureRootParamOffset;
+ auto& object = m_objects[baseIndex + j];
+
+ auto subRootParamIndex = rootBindingIndex.rootParamIndex + subObjectRange.offset.rootParam;
+
+ object->bindAsParameterBlock(
+ encoder,
+ bindingState,
+ subRootParamIndex,
+ subObjectLayout);
}
}
break;
- case slang::BindingType::ConstantBuffer:
+
+ case slang::BindingType::ExistentialValue:
+ if(subObjectLayout)
{
- auto baseIndex = bindingRange.binding.index;
+ auto baseIndex = bindingRange.flatIndex;
for (uint32_t j = 0; j < bindingRange.count; j++)
{
- m_objects[baseIndex + j]->bindObject(encoder, bindingState);
+ auto& object = m_objects[baseIndex + j];
+
+ RootBindingIndex subIndex = rootBindingIndex;
+ subIndex.rootParamIndex += subObjectRange.offset.rootParam;
+ subIndex.resource.descriptorIndex += subObjectRange.offset.resource;
+ subIndex.sampler.descriptorTableIndex += subObjectRange.offset.sampler;
+
+ object->bindAsValue(
+ encoder,
+ bindingState,
+ subIndex,
+ subObjectLayout);
}
}
break;
+ }
+ }
+
+// SLANG_RETURN_ON_FAIL(bindChildRootParameters(encoder, bindingState, rootParameterIndex));
+
+ return SLANG_OK;
+ }
+
+#if 0
+ Result bindChildRootParameters(PipelineCommandEncoder* encoder, RootBindingState& bindingState, SlangInt rootParameterIndex)
+ {
+ ShaderObjectLayoutImpl* layout = getLayout();
+ auto& subObjectRanges = layout->getSubObjectRanges();
+ auto subObjectRangeCount = subObjectRanges.getCount();
+ for (Index i = 0; i < subObjectRangeCount; i++)
+ {
+ auto subObjectRange = layout->getSubObjectRange(i);
+ auto bindingRange = layout->getBindingRange(subObjectRange.bindingRangeIndex);
+ switch(bindingRange.bindingType)
+ {
+ case slang::BindingType::ParameterBlock:
+ {
+ auto baseIndex = bindingRange.flatIndex;
+ auto subRootParameterIndex = rootParameterIndex + subObjectRange.childRootParameterOffset;
+ for (uint32_t j = 0; j < bindingRange.count; j++)
+ {
+ auto& object = m_objects[baseIndex + j];
+
+ object->bindAsParameterBlock(encoder, bindingState, subRootParameterIndex);
+ subRootParameterIndex += subObjectRange.layout->getTotalRootParameterCount();
+ }
+ }
+ break;
+ case slang::BindingType::ConstantBuffer:
+ {
+ // TODO:
+ }
+ break;
case slang::BindingType::ExistentialValue:
// If the existential object contains only ordinary data fields,
// the data is already written into m_ordinaryDataBuffer during `setObject`,
@@ -2263,8 +2800,10 @@ public:
break;
}
}
+
return SLANG_OK;
}
+#endif
/// Any "ordinary" / uniform data for this object
List<char> m_ordinaryData;
@@ -2279,8 +2818,8 @@ public:
{
int32_t m_resourceTable = 0;
int32_t m_samplerTable = 0;
- uint32_t m_resourceCount = 0;
- uint32_t m_samplerCount = 0;
+// uint32_t m_resourceCount = 0;
+// uint32_t m_samplerCount = 0;
};
DescriptorSet m_descriptorSet;
@@ -2374,15 +2913,24 @@ public:
}
public:
- Result bindObject(PipelineCommandEncoder* encoder, RootBindingState* bindingState)
+ Result bindRootObject(PipelineCommandEncoder* encoder, RootBindingState& bindingState, RootShaderObjectLayoutImpl* layout)
{
- SLANG_RETURN_ON_FAIL(Super::bindObject(encoder, bindingState));
+ auto rootBindingIndex = prepareToBindAsParameterBlock(0);
+
+ SLANG_RETURN_ON_FAIL(Super::bindAsConstantBuffer(encoder, bindingState, rootBindingIndex, layout));
auto entryPointCount = m_entryPoints.getCount();
for (Index i = 0; i < entryPointCount; ++i)
{
auto entryPoint = m_entryPoints[i];
- SLANG_RETURN_ON_FAIL(entryPoint->bindObject(encoder, bindingState));
+ auto& entryPointInfo = layout->getEntryPoint(i);
+
+ auto entryPointBindingIndex = rootBindingIndex;
+ entryPointBindingIndex.rootParamIndex += entryPointInfo.offset.rootParam;
+ entryPointBindingIndex.resource.descriptorIndex += entryPointInfo.offset.resource;
+ entryPointBindingIndex.sampler.descriptorIndex += entryPointInfo.offset.sampler;
+
+ SLANG_RETURN_ON_FAIL(entryPoint->bindAsConstantBuffer(encoder, bindingState, entryPointBindingIndex, entryPointInfo.layout));
}
return SLANG_OK;
@@ -3392,7 +3940,7 @@ Result D3D12Device::PipelineCommandEncoder::_bindRenderState(Submitter* submitte
static_cast<RootShaderObjectLayoutImpl*>(specializedRootLayout.Ptr());
ShortList<DescriptorTable> descriptorTables;
- auto descSetInfo = rootLayoutImpl->getDescriptorSetInfo();
+// auto descSetInfo = rootLayoutImpl->getDescriptorSetInfo();
auto heap = m_commandBuffer->m_transientHeap;
for (auto& descSet : rootLayoutImpl->m_gpuDescriptorSetInfos)
{
@@ -3416,7 +3964,7 @@ Result D3D12Device::PipelineCommandEncoder::_bindRenderState(Submitter* submitte
bindState.transientHeap = m_transientHeap;
auto descTablesView = descriptorTables.getArrayView();
bindState.descriptorTables = descTablesView.arrayView;
- SLANG_RETURN_ON_FAIL(rootObjectImpl->bindObject(this, &bindState));
+ SLANG_RETURN_ON_FAIL(rootObjectImpl->bindRootObject(this, bindState, rootLayoutImpl));
for (Index i = 0; i < descriptorTables.getCount(); i++)
{
diff --git a/tools/gfx/open-gl/render-gl.cpp b/tools/gfx/open-gl/render-gl.cpp
index 3ef3c1aa1..53e9dd4b1 100644
--- a/tools/gfx/open-gl/render-gl.cpp
+++ b/tools/gfx/open-gl/render-gl.cpp
@@ -620,12 +620,6 @@ public:
slang::BindingType bindingType;
Index count;
Index baseIndex;
-
- // Returns true if this binding range consumes a specialization argument slot.
- bool isSpecializationArg() const
- {
- return bindingType == slang::BindingType::ExistentialValue;
- }
};
struct SubObjectRangeInfo
@@ -1340,8 +1334,8 @@ public:
// contiguous array with a single stride; we need to carefully consider what the layout
// logic does for complex cases with multiple layers of nested arrays and structures.
//
- size_t subObjectRangePendingDataOffset = _getSubObjectRangePendingDataOffset(specializedLayout, subObjectRangeIndex);
- size_t subObjectRangePendingDataStride = _getSubObjectRangePendingDataStride(specializedLayout, subObjectRangeIndex);
+ size_t subObjectRangePendingDataOffset = 0; //subObjectRangeInfo.offset.pendingOrdinaryData;
+ size_t subObjectRangePendingDataStride = 0; //subObjectRangeInfo.stride.pendingOrdinaryData;
// If the range doesn't actually need/use the "pending" allocation at all, then
// we need to detect that case and skip such ranges.
@@ -1369,12 +1363,6 @@ public:
return SLANG_OK;
}
- // As discussed in `_writeOrdinaryData()`, these methods are just stubs waiting for
- // the "flat" Slang refelction information to provide access to the relevant data.
- //
- size_t _getSubObjectRangePendingDataOffset(ShaderObjectLayoutImpl* specializedLayout, Index subObjectRangeIndex) { return 0; }
- size_t _getSubObjectRangePendingDataStride(ShaderObjectLayoutImpl* specializedLayout, Index subObjectRangeIndex) { return 0; }
-
/// Ensure that the `m_ordinaryDataBuffer` has been created, if it is needed
Result _ensureOrdinaryDataBufferCreatedIfNeeded(GLDevice* device)
{
diff --git a/tools/gfx/renderer-shared.cpp b/tools/gfx/renderer-shared.cpp
index 99af6e4ff..72b4c45f5 100644
--- a/tools/gfx/renderer-shared.cpp
+++ b/tools/gfx/renderer-shared.cpp
@@ -291,6 +291,30 @@ ShaderComponentID ShaderCache::getComponentId(slang::TypeReflection* type)
switch (type->getKind())
{
case slang::TypeReflection::Kind::Specialized:
+ {
+ auto baseType = type->getElementType();
+
+ StringBuilder builder;
+ builder.append(UnownedTerminatedStringSlice(baseType->getName()));
+
+ auto rawType = (SlangReflectionType*) type;
+
+ builder.appendChar('<');
+ SlangInt argCount = spReflectionType_getSpecializedTypeArgCount(rawType);
+ for(SlangInt a = 0; a < argCount; ++a)
+ {
+ if(a != 0) builder.appendChar(',');
+ if(auto rawArgType = spReflectionType_getSpecializedTypeArgType(rawType, a))
+ {
+ auto argType = (slang::TypeReflection*) rawArgType;
+ builder.append(argType->getName());
+ }
+ }
+ builder.appendChar('>');
+ key.typeName = builder.getUnownedSlice();
+ key.updateHash();
+ return getComponentId(key);
+ }
// TODO: collect specialization arguments and append them to `key`.
SLANG_UNIMPLEMENTED_X("specialized type");
default:
@@ -442,7 +466,8 @@ Result RendererBase::maybeSpecializePipeline(
specializedPipelineState->unspecializedPipelineState = currentPipeline;
shaderCache.addSpecializedPipeline(pipelineKey, specializedPipelineState);
}
- outNewPipeline = static_cast<PipelineStateBase*>(specializedPipelineState.Ptr());
+ auto specializedPipelineStateBase = static_cast<PipelineStateBase*>(specializedPipelineState.Ptr());
+ outNewPipeline = specializedPipelineStateBase;
}
return SLANG_OK;
}
diff --git a/tools/gfx/vulkan/render-vk.cpp b/tools/gfx/vulkan/render-vk.cpp
index b3f99ff59..a0860f0e5 100644
--- a/tools/gfx/vulkan/render-vk.cpp
+++ b/tools/gfx/vulkan/render-vk.cpp
@@ -660,28 +660,230 @@ public:
VkPipeline m_pipeline = VK_NULL_HANDLE;
};
+ // In order to bind shader parameters to the correct locations, we need to
+ // be able to describe those locations. Most shader parameters in Vulkan
+ // simply consume a single `binding`, but we also need to deal with
+ // parameters that represent push-constant ranges.
+ //
+ // In more complex cases we might be binding an entire "sub-object" like
+ // a parameter block, an entry point, etc. For the general case, we need
+ // to be able to represent a composite offset that includes offsets for
+ // each of the cases that Vulkan supports.
+
+ /// A "simple" binding offset that records `binding`, `set`, etc. offsets
+ struct SimpleBindingOffset
+ {
+ /// An offset in GLSL/SPIR-V `binding`s
+ uint32_t binding = 0;
+
+ /// The descriptor `set` that the `binding` field should be understood as an index into
+ uint32_t bindingSet = 0;
+
+ /// The starting index for any "child" descriptor sets to start at
+ uint32_t childSet = 0;
+
+ // The distinction between `bindingSet` and `childSet` above is subtle, but
+ // potentially very important when objects contain nested parameter blocks.
+ // Consider:
+ //
+ // struct Stuff { ... }
+ // struct Things
+ // {
+ // Texture2D t;
+ // ParameterBlock<Stuff> stuff;
+ // }
+ //
+ // ParameterBlock<Stuff> gStuff;
+ // Texture2D gTex;
+ // ConstantBuffer<Things> gThings;
+ //
+ // In this example, the global-scope parameters like `gTex` and `gThings`
+ // are expected to be laid out in `set=0`, and we also expect `gStuff`
+ // to be laid out as `set=1`. As a result we expect that `gThings.t`
+ // will be laid out as `binding=1,set=0` (right after `gTex`), but
+ // `gThings.stuff` should be laid out as `set=2`.
+ //
+ // In this case, when binding `gThings` we would want a binding offset
+ // that has a `binding` or 1, a `bindingSet` of 0, and a `childSet` of 2.
+ //
+ // TODO: Validate that any of this works as intended.
+
+ /// The offset in push-constant ranges (not bytes)
+ uint32_t pushConstantRange = 0;
+
+ /// Create a default (zero) offset
+ SimpleBindingOffset()
+ {}
+
+ /// Create an offset based on offset information in the given Slang `varLayout`
+ SimpleBindingOffset(slang::VariableLayoutReflection* varLayout)
+ {
+ if(varLayout)
+ {
+ bindingSet = (uint32_t) varLayout->getBindingSpace(SLANG_PARAMETER_CATEGORY_DESCRIPTOR_TABLE_SLOT);
+ binding = (uint32_t) varLayout->getOffset(SLANG_PARAMETER_CATEGORY_DESCRIPTOR_TABLE_SLOT);
+
+ childSet = (uint32_t) varLayout->getOffset(SLANG_PARAMETER_CATEGORY_REGISTER_SPACE);
+
+ pushConstantRange = (uint32_t) varLayout->getOffset(SLANG_PARAMETER_CATEGORY_PUSH_CONSTANT_BUFFER);
+ }
+ }
+
+ /// Add any values in the given `offset`
+ void operator+=(SimpleBindingOffset const& offset)
+ {
+ binding += offset.binding;
+ bindingSet += offset.bindingSet;
+ childSet += offset.childSet;
+ pushConstantRange += offset.pushConstantRange;
+ }
+ };
+
+ // While a "simple" binding offset representation will work in many cases,
+ // once we need to deal with layout for programs with interface-type parameters
+ // that have been statically specialized, we also need to track the offset
+ // for where to bind any "pending" data that arises from the process of static
+ // specialization.
+ //
+ // In order to conveniently track both the "primary" and "pending" offset information,
+ // we will define a more complete `BindingOffset` type that combines simple
+ // binding offsets for the primary and pending parts.
+
+ /// A representation of the offset at which to bind a shader parameter or sub-object
+ struct BindingOffset : SimpleBindingOffset
+ {
+ // Offsets for "primary" data are stored directly in the `BindingOffset`
+ // via the inheritance from `SimpleBindingOffset`.
+
+ /// Offset for any "pending" data
+ SimpleBindingOffset pending;
+
+ /// Create a default (zero) offset
+ BindingOffset()
+ {}
+
+ /// Create an offset from a simple offset
+ explicit BindingOffset(SimpleBindingOffset const& offset)
+ : SimpleBindingOffset(offset)
+ {}
+
+ /// Create an offset based on offset information in the given Slang `varLayout`
+ BindingOffset(slang::VariableLayoutReflection* varLayout)
+ : SimpleBindingOffset(varLayout)
+ , pending(varLayout->getPendingDataLayout())
+ {}
+
+ /// Add any values in the given `offset`
+ void operator+=(SimpleBindingOffset const& offset)
+ {
+ SimpleBindingOffset::operator+=(offset);
+ }
+
+ /// Add any values in the given `offset`
+ void operator+=(BindingOffset const& offset)
+ {
+ SimpleBindingOffset::operator+=(offset);
+ pending += offset.pending;
+ }
+ };
+
class ShaderObjectLayoutImpl : public ShaderObjectLayoutBase
{
public:
+ // A shader object comprises three main kinds of state:
+ //
+ // * Zero or more bytes of ordinary ("uniform") data
+ // * Zero or more *bindings* for textures, buffers, and samplers
+ // * Zero or more *sub-objects* representing nested parameter blocks, etc.
+ //
+ // A shader object *layout* stores information that can be used to
+ // organize these different kinds of state and optimize access to them.
+ //
+ // For example, both texture/buffer/sampler bindings and sub-objects
+ // are organized into logical *binding ranges* by the Slang reflection
+ // API, and a shader object layout will store information about those
+ // ranges in a form that is usable for the Vulkan API:
+
struct BindingRangeInfo
{
slang::BindingType bindingType;
Index count;
Index baseIndex;
- Index descriptorSetIndex;
- Index rangeIndexInDescriptorSet;
- // Returns true if this binding range consumes a specialization argument slot.
- bool isSpecializationArg() const
+ /// The `binding` offset to apply for this range
+ uint32_t bindingOffset;
+
+ /// The `set` offset to apply for this range
+ uint32_t setOffset;
+
+ // Note: The 99% case is that `setOffset` will be zero. For any shader object
+ // that was allocated from an ordinary Slang type (anything other than a root
+ // shader object in fact), all of the bindings will have been allocated into
+ // a single logical descriptor set.
+ //
+ // TODO: Ideally we could refactor so that only the root shader object layout
+ // stores a set offset for its binding ranges, and all other objects skip
+ // storing a field that never actually matters.
+ };
+
+ // Sometimes we just want to iterate over the ranges that represnet
+ // sub-objects while skipping over the others, because sub-object
+ // ranges often require extra handling or more state.
+ //
+ // For that reason we also store pre-computed information about each
+ // sub-object range.
+
+ /// Offset information for a sub-object range
+ struct SubObjectRangeOffset : BindingOffset
+ {
+ SubObjectRangeOffset()
+ {}
+
+ SubObjectRangeOffset(slang::VariableLayoutReflection* varLayout)
+ : BindingOffset(varLayout)
+ {
+ if(auto pendingLayout = varLayout->getPendingDataLayout())
+ {
+ pendingOrdinaryData = (uint32_t) pendingLayout->getOffset(SLANG_PARAMETER_CATEGORY_UNIFORM);
+ }
+ }
+
+ /// The offset for "pending" ordinary data related to this range
+ uint32_t pendingOrdinaryData = 0;
+ };
+
+ /// Stride information for a sub-object range
+ struct SubObjectRangeStride
+ {
+ SubObjectRangeStride()
+ {}
+
+ SubObjectRangeStride(slang::TypeLayoutReflection* typeLayout)
{
- return bindingType == slang::BindingType::ExistentialValue;
+ if(auto pendingLayout = typeLayout->getPendingDataTypeLayout())
+ {
+ pendingOrdinaryData = (uint32_t) pendingLayout->getSize(SLANG_PARAMETER_CATEGORY_UNIFORM);
+ }
}
+
+ /// The strid for "pending" ordinary data related to this range
+ uint32_t pendingOrdinaryData = 0;
};
+ /// Information about a logical binding range as reported by Slang reflection
struct SubObjectRangeInfo
{
- RefPtr<ShaderObjectLayoutImpl> layout;
+ /// The index of the binding range that corresponds to this sub-object range
Index bindingRangeIndex;
+
+ /// The layout expected for objects bound to this range (if known)
+ RefPtr<ShaderObjectLayoutImpl> layout;
+
+ /// The offset to use when binding the first object in this range
+ SubObjectRangeOffset offset;
+
+ /// Stride between consecutive objects in this range
+ SubObjectRangeStride stride;
};
struct DescriptorSetInfo
@@ -710,10 +912,23 @@ public:
Index m_subObjectCount = 0;
Index m_varyingInputCount = 0;
Index m_varyingOutputCount = 0;
- uint32_t m_pushConstantSize = 0;
List<DescriptorSetInfo> m_descriptorSetBuildInfos;
Dictionary<Index, Index> m_mapSpaceToDescriptorSetIndex;
+ /// The number of descriptor sets allocated by child/descendent objects
+ uint32_t m_childDescriptorSetCount = 0;
+
+ /// The total number of `binding`s consumed by this object and its children/descendents
+ uint32_t m_totalBindingCount = 0;
+
+ /// The push-constant ranges that belong to this object itself (if any)
+ List<VkPushConstantRange> m_ownPushConstantRanges;
+
+ /// The number of push-constant ranges owned by child/descendent objects
+ uint32_t m_childPushConstantRangeCount = 0;
+
+ uint32_t m_totalOrdinaryDataSize = 0;
+
Index findOrAddDescriptorSet(Index space)
{
Index index;
@@ -765,45 +980,71 @@ public:
}
}
- Result _addDescriptorSets(
- slang::TypeLayoutReflection* typeLayout,
- bool createImplicitConstantBufferForUniforms,
- slang::VariableLayoutReflection* varLayout = nullptr)
+ /// Add any descriptor ranges implied by this object containing a leaf
+ /// sub-object described by `typeLayout`, at the given `offset`.
+ void _addDescriptorRangesAsValue(
+ slang::TypeLayoutReflection* typeLayout,
+ BindingOffset const& offset)
{
- SlangInt descriptorSetCount = typeLayout->getDescriptorSetCount();
- SlangInt defaultDescriptorSetIndex;
- // If the type has ordinary uniform data fields, we need to make sure to create
- // a descriptor set with a constant buffer binding in the case that the shader
- // object is bound as a stand alone parameter block.
- uint32_t bindingOffset = 0;
- if (createImplicitConstantBufferForUniforms && typeLayout->getSize() != 0)
+ // First we will scan through all the descriptor sets that the Slang reflection
+ // information believes go into making up the given type.
+ //
+ // Note: We are initializing the sets in order so that their order in our
+ // internal data structures should be deterministically based on the order
+ // in which they are listed in Slang's reflection information.
+ //
+ Index descriptorSetCount = typeLayout->getDescriptorSetCount();
+ for (Index i = 0; i < descriptorSetCount; ++i)
{
- defaultDescriptorSetIndex = findOrAddDescriptorSet(0);
- auto& descriptorSetInfo = m_descriptorSetBuildInfos[defaultDescriptorSetIndex];
- VkDescriptorSetLayoutBinding vkBindingRangeDesc = {};
- vkBindingRangeDesc.binding = 0;
- bindingOffset = 1;
- vkBindingRangeDesc.descriptorCount = 1;
- vkBindingRangeDesc.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
- vkBindingRangeDesc.stageFlags = VK_SHADER_STAGE_ALL;
- descriptorSetInfo.vkBindings.add(vkBindingRangeDesc);
+ SlangInt descriptorRangeCount = typeLayout->getDescriptorSetDescriptorRangeCount(i);
+ if (descriptorRangeCount == 0)
+ continue;
+ auto descriptorSetIndex = findOrAddDescriptorSet(offset.bindingSet + typeLayout->getDescriptorSetSpaceOffset(i));
}
- for (SlangInt s = 0; s < descriptorSetCount; ++s)
+ // For actually populating the descriptor sets we prefer to enumerate
+ // the binding ranges of the type instead of the descriptor sets.
+ //
+ Index bindRangeCount = typeLayout->getBindingRangeCount();
+ for( Index i = 0; i < bindRangeCount; ++i )
{
- SlangInt descriptorRangeCount =
- typeLayout->getDescriptorSetDescriptorRangeCount(s);
+ auto bindingRangeIndex = i;
+ auto bindingRangeType = typeLayout->getBindingRangeType(bindingRangeIndex);
+ switch(bindingRangeType)
+ {
+ default:
+ break;
+
+ // We will skip over ranges that represent sub-objects for now, and handle
+ // them in a separate pass.
+ //
+ case slang::BindingType::ParameterBlock:
+ case slang::BindingType::ConstantBuffer:
+ case slang::BindingType::ExistentialValue:
+ case slang::BindingType::PushConstant:
+ continue;
+ }
+
+ // Given a binding range we are interested in, we will then enumerate
+ // its contained descriptor ranges.
+
+ Index descriptorRangeCount = typeLayout->getBindingRangeDescriptorRangeCount(bindingRangeIndex);
if (descriptorRangeCount == 0)
continue;
- auto descriptorSetIndex =
- findOrAddDescriptorSet(typeLayout->getDescriptorSetSpaceOffset(s));
+ auto slangDescriptorSetIndex = typeLayout->getBindingRangeDescriptorSetIndex(bindingRangeIndex);
+ auto descriptorSetIndex = findOrAddDescriptorSet(offset.bindingSet + typeLayout->getDescriptorSetSpaceOffset(slangDescriptorSetIndex));
auto& descriptorSetInfo = m_descriptorSetBuildInfos[descriptorSetIndex];
- for (SlangInt r = 0; r < descriptorRangeCount; ++r)
+
+ Index firstDescriptorRangeIndex = typeLayout->getBindingRangeFirstDescriptorRangeIndex(bindingRangeIndex);
+ for(Index j = 0; j < descriptorRangeCount; ++j)
{
- auto slangBindingType =
- typeLayout->getDescriptorSetDescriptorRangeType(s, r);
+ Index descriptorRangeIndex = firstDescriptorRangeIndex + j;
+ auto slangDescriptorType = typeLayout->getDescriptorSetDescriptorRangeType(slangDescriptorSetIndex, descriptorRangeIndex);
- switch (slangBindingType)
+ // Certain kinds of descriptor ranges reflected by Slang do not
+ // manifest as descriptors at the Vulkan level, so we will skip those.
+ //
+ switch (slangDescriptorType)
{
case slang::BindingType::ExistentialValue:
case slang::BindingType::InlineUniformData:
@@ -813,50 +1054,183 @@ public:
break;
}
- auto vkDescriptorType = _mapDescriptorType(slangBindingType);
+ auto vkDescriptorType = _mapDescriptorType(slangDescriptorType);
VkDescriptorSetLayoutBinding vkBindingRangeDesc = {};
- vkBindingRangeDesc.binding = bindingOffset +
- (uint32_t)typeLayout->getDescriptorSetDescriptorRangeIndexOffset(s, r);
- vkBindingRangeDesc.descriptorCount =
- (uint32_t)typeLayout->getDescriptorSetDescriptorRangeDescriptorCount(
- s, r);
+ vkBindingRangeDesc.binding = offset.binding + (uint32_t)typeLayout->getDescriptorSetDescriptorRangeIndexOffset(slangDescriptorSetIndex, descriptorRangeIndex);
+ vkBindingRangeDesc.descriptorCount = (uint32_t)typeLayout->getDescriptorSetDescriptorRangeDescriptorCount(slangDescriptorSetIndex, descriptorRangeIndex);
vkBindingRangeDesc.descriptorType = vkDescriptorType;
vkBindingRangeDesc.stageFlags = VK_SHADER_STAGE_ALL;
- if (varLayout)
+
+ descriptorSetInfo.vkBindings.add(vkBindingRangeDesc);
+ }
+ }
+
+ // We skipped over the sub-object ranges when adding descriptors above,
+ // and now we will address that oversight by iterating over just
+ // the sub-object ranges.
+ //
+ Index subObjectRangeCount = typeLayout->getSubObjectRangeCount();
+ for(Index subObjectRangeIndex = 0; subObjectRangeIndex < subObjectRangeCount; ++subObjectRangeIndex)
+ {
+ auto bindingRangeIndex = typeLayout->getSubObjectRangeBindingRangeIndex(subObjectRangeIndex);
+ auto bindingType = typeLayout->getBindingRangeType(bindingRangeIndex);
+
+ auto subObjectTypeLayout = typeLayout->getBindingRangeLeafTypeLayout(bindingRangeIndex);
+ SLANG_ASSERT(subObjectTypeLayout);
+
+ BindingOffset subObjectRangeOffset = offset;
+ subObjectRangeOffset += BindingOffset(typeLayout->getSubObjectRangeOffset(subObjectRangeIndex));
+
+ switch(bindingType)
+ {
+ // A `ParameterBlock<X>` never contributes descripto ranges to the
+ // decriptor sets of a parent object.
+ //
+ case slang::BindingType::ParameterBlock:
+ default:
+ break;
+
+ case slang::BindingType::ExistentialValue:
+ // An interest/existential-typed sub-object range will only contribute descriptor
+ // ranges to a parent object in the case where it has been specialied, which
+ // is precisely the case where the Slang reflection information will tell us
+ // about its "pending" layout.
+ //
+ if(auto pendingTypeLayout = subObjectTypeLayout->getPendingDataTypeLayout())
{
- auto category =
- typeLayout->getDescriptorSetDescriptorRangeCategory(s, r);
- vkBindingRangeDesc.binding += (uint32_t)varLayout->getOffset(category);
+ BindingOffset pendingOffset = BindingOffset(subObjectRangeOffset.pending);
+ _addDescriptorRangesAsValue(pendingTypeLayout, pendingOffset);
}
- descriptorSetInfo.vkBindings.add(vkBindingRangeDesc);
+ break;
+
+ case slang::BindingType::ConstantBuffer:
+ {
+ // A `ConstantBuffer<X>` range will contribute any nested descriptor
+ // ranges in `X`, along with a leading descriptor range for a
+ // uniform buffer to hold ordinary/uniform data, if there is any.
+
+ SLANG_ASSERT(subObjectTypeLayout);
+
+ auto containerVarLayout = subObjectTypeLayout->getContainerVarLayout();
+ SLANG_ASSERT(containerVarLayout);
+
+ auto elementVarLayout = subObjectTypeLayout->getElementVarLayout();
+ SLANG_ASSERT(elementVarLayout);
+
+ auto elementTypeLayout = elementVarLayout->getTypeLayout();
+ SLANG_ASSERT(elementTypeLayout);
+
+ BindingOffset containerOffset = subObjectRangeOffset;
+ containerOffset += BindingOffset(subObjectTypeLayout->getContainerVarLayout());
+
+ BindingOffset elementOffset = subObjectRangeOffset;
+ elementOffset += BindingOffset(elementVarLayout);
+
+ _addDescriptorRangesAsConstantBuffer(elementTypeLayout, containerOffset, elementOffset);
+ }
+ break;
+
+ case slang::BindingType::PushConstant:
+ {
+ // This case indicates a `ConstantBuffer<X>` that was marked as being
+ // used for push constants.
+ //
+ // Much of the handling is the same as for an ordinary `ConstantBuffer<X>`,
+ // but of course we need to handle the ordinary data part differently.
+
+ SLANG_ASSERT(subObjectTypeLayout);
+
+ auto containerVarLayout = subObjectTypeLayout->getContainerVarLayout();
+ SLANG_ASSERT(containerVarLayout);
+
+ auto elementVarLayout = subObjectTypeLayout->getElementVarLayout();
+ SLANG_ASSERT(elementVarLayout);
+
+ auto elementTypeLayout = elementVarLayout->getTypeLayout();
+ SLANG_ASSERT(elementTypeLayout);
+
+ BindingOffset containerOffset = subObjectRangeOffset;
+ containerOffset += BindingOffset(subObjectTypeLayout->getContainerVarLayout());
+
+ BindingOffset elementOffset = subObjectRangeOffset;
+ elementOffset += BindingOffset(elementVarLayout);
+
+ _addDescriptorRangesAsPushConstantBuffer(elementTypeLayout, containerOffset, elementOffset);
+ }
+ break;
}
+
}
- return SLANG_OK;
}
- Result setElementTypeLayout(
- slang::TypeLayoutReflection* typeLayout,
- bool buildDescriptorSetLayout)
+ /// Add the descriptor ranges implied by a `ConstantBuffer<X>` where `X` is
+ /// described by `elementTypeLayout`.
+ ///
+ /// The `containerOffset` and `elementOffset` are the binding offsets that
+ /// should apply to the buffer itself and the contents of the buffer, respectively.
+ ///
+ void _addDescriptorRangesAsConstantBuffer(
+ slang::TypeLayoutReflection* elementTypeLayout,
+ BindingOffset const& containerOffset,
+ BindingOffset const& elementOffset)
{
- // First we will use the Slang layout information to allocate
- // the descriptor set layout(s) required to store values
- // of the given type.
- //
- if (buildDescriptorSetLayout)
+ // If the type has ordinary uniform data fields, we need to make sure to create
+ // a descriptor set with a constant buffer binding in the case that the shader
+ // object is bound as a stand alone parameter block.
+ if (elementTypeLayout->getSize(SLANG_PARAMETER_CATEGORY_UNIFORM) != 0)
{
- SLANG_RETURN_ON_FAIL(_addDescriptorSets(typeLayout, true));
+ auto descriptorSetIndex = findOrAddDescriptorSet(containerOffset.bindingSet);
+ auto& descriptorSetInfo = m_descriptorSetBuildInfos[descriptorSetIndex];
+ VkDescriptorSetLayoutBinding vkBindingRangeDesc = {};
+ vkBindingRangeDesc.binding = containerOffset.binding;
+ vkBindingRangeDesc.descriptorCount = 1;
+ vkBindingRangeDesc.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+ vkBindingRangeDesc.stageFlags = VK_SHADER_STAGE_ALL;
+ descriptorSetInfo.vkBindings.add(vkBindingRangeDesc);
}
- typeLayout = _unwrapParameterGroups(typeLayout);
+ _addDescriptorRangesAsValue(elementTypeLayout, elementOffset);
+ }
- m_elementTypeLayout = typeLayout;
+ /// Add the descriptor ranges implied by a `PushConstantBuffer<X>` where `X` is
+ /// described by `elementTypeLayout`.
+ ///
+ /// The `containerOffset` and `elementOffset` are the binding offsets that
+ /// should apply to the buffer itself and the contents of the buffer, respectively.
+ ///
+ void _addDescriptorRangesAsPushConstantBuffer(
+ slang::TypeLayoutReflection* elementTypeLayout,
+ BindingOffset const& containerOffset,
+ BindingOffset const& elementOffset)
+ {
+ // If the type has ordinary uniform data fields, we need to make sure to create
+ // a descriptor set with a constant buffer binding in the case that the shader
+ // object is bound as a stand alone parameter block.
+ auto ordinaryDataSize = (uint32_t) elementTypeLayout->getSize(SLANG_PARAMETER_CATEGORY_UNIFORM);
+ if (ordinaryDataSize != 0)
+ {
+ auto pushConstantRangeIndex = containerOffset.pushConstantRange;
+ VkPushConstantRange vkPushConstantRange = {};
+ vkPushConstantRange.size = ordinaryDataSize;
+ vkPushConstantRange.stageFlags = VK_SHADER_STAGE_ALL; // TODO: be more clever
- // Next we will compute the binding ranges that are used to store
- // the logical contents of the object in memory. These will relate
- // to the descriptor ranges in the various sets, but not always
- // in a one-to-one fashion.
+ while(m_ownPushConstantRanges.getCount() <= pushConstantRangeIndex)
+ {
+ VkPushConstantRange emptyRange = { 0 };
+ m_ownPushConstantRanges.add(emptyRange);
+ }
+
+ m_ownPushConstantRanges[pushConstantRangeIndex] = vkPushConstantRange;
+ }
+
+ _addDescriptorRangesAsValue(elementTypeLayout, elementOffset);
+ }
+ /// Add binding ranges to this shader object layout, as implied by the given `typeLayout`
+ void addBindingRanges(
+ slang::TypeLayoutReflection* typeLayout)
+ {
SlangInt bindingRangeCount = typeLayout->getBindingRangeCount();
for (SlangInt r = 0; r < bindingRangeCount; ++r)
{
@@ -865,10 +1239,6 @@ public:
slang::TypeLayoutReflection* slangLeafTypeLayout =
typeLayout->getBindingRangeLeafTypeLayout(r);
- SlangInt descriptorSetIndex = typeLayout->getBindingRangeDescriptorSetIndex(r);
- SlangInt rangeIndexInDescriptorSet =
- typeLayout->getBindingRangeFirstDescriptorRangeIndex(r);
-
Index baseIndex = 0;
switch (slangBindingType)
{
@@ -882,11 +1252,13 @@ public:
case slang::BindingType::Sampler:
baseIndex = m_samplerCount;
m_samplerCount += count;
+ m_totalBindingCount += 1;
break;
case slang::BindingType::CombinedTextureSampler:
baseIndex = m_combinedTextureSamplerCount;
m_combinedTextureSamplerCount += count;
+ m_totalBindingCount += 1;
break;
case slang::BindingType::VaryingInput:
@@ -901,6 +1273,7 @@ public:
default:
baseIndex = m_resourceViewCount;
m_resourceViewCount += count;
+ m_totalBindingCount += 1;
break;
}
@@ -908,8 +1281,32 @@ public:
bindingRangeInfo.bindingType = slangBindingType;
bindingRangeInfo.count = count;
bindingRangeInfo.baseIndex = baseIndex;
- bindingRangeInfo.descriptorSetIndex = descriptorSetIndex;
- bindingRangeInfo.rangeIndexInDescriptorSet = rangeIndexInDescriptorSet;
+
+ // We'd like to extract the information on the GLSL/SPIR-V
+ // `binding` that this range should bind into (or whatever
+ // other specific kind of offset/index is appropriate to it).
+ //
+ // A binding range represents a logical member of the shader
+ // object type, and it may encompass zero or more *descriptor
+ // ranges* that describe how it is physically bound to pipeline
+ // state.
+ //
+ // If the current bindign range is backed by at least one descriptor
+ // range then we can query the binding offset of that descriptor
+ // range. We expect that in the common case there will be exactly
+ // one descriptor range, and we can extract the information easily.
+ //
+ if(typeLayout->getBindingRangeDescriptorRangeCount(r) != 0)
+ {
+ SlangInt descriptorSetIndex = typeLayout->getBindingRangeDescriptorSetIndex(r);
+ SlangInt descriptorRangeIndex = typeLayout->getBindingRangeFirstDescriptorRangeIndex(r);
+
+ auto set = typeLayout->getDescriptorSetSpaceOffset(descriptorSetIndex);
+ auto bindingOffset = typeLayout->getDescriptorSetDescriptorRangeIndexOffset(descriptorSetIndex, descriptorRangeIndex);
+
+ bindingRangeInfo.setOffset = uint32_t(set);
+ bindingRangeInfo.bindingOffset = uint32_t(bindingOffset);
+ }
m_bindingRanges.add(bindingRangeInfo);
}
@@ -918,6 +1315,7 @@ public:
for (SlangInt r = 0; r < subObjectRangeCount; ++r)
{
SlangInt bindingRangeIndex = typeLayout->getSubObjectRangeBindingRangeIndex(r);
+ auto& bindingRange = m_bindingRanges[bindingRangeIndex];
auto slangBindingType = typeLayout->getBindingRangeType(bindingRangeIndex);
slang::TypeLayoutReflection* slangLeafTypeLayout =
typeLayout->getBindingRangeLeafTypeLayout(bindingRangeIndex);
@@ -930,20 +1328,103 @@ public:
// know the appropraite type/layout of sub-object to allocate.
//
RefPtr<ShaderObjectLayoutImpl> subObjectLayout;
- if (slangBindingType != slang::BindingType::ExistentialValue)
+ switch(slangBindingType)
{
- ShaderObjectLayoutImpl::createForElementType(
- m_renderer,
- slangLeafTypeLayout->getElementTypeLayout(),
- true,
- subObjectLayout.writeRef());
+ default:
+ {
+ auto elementTypeLayout = slangLeafTypeLayout->getElementTypeLayout();
+ ShaderObjectLayoutImpl::createForElementType(
+ m_renderer,
+ elementTypeLayout,
+ subObjectLayout.writeRef());
+ }
+ break;
+
+ case slang::BindingType::ExistentialValue:
+ if(auto pendingTypeLayout = slangLeafTypeLayout->getPendingDataTypeLayout())
+ {
+ ShaderObjectLayoutImpl::createForElementType(
+ m_renderer,
+ pendingTypeLayout,
+ subObjectLayout.writeRef());
+ }
+ break;
}
SubObjectRangeInfo subObjectRange;
subObjectRange.bindingRangeIndex = bindingRangeIndex;
subObjectRange.layout = subObjectLayout;
+
+ // We will use Slang reflection infromation to extract the offset information
+ // for each sub-object range.
+ //
+ // TODO: We should also be extracting the uniform offset here.
+ //
+ subObjectRange.offset = SubObjectRangeOffset(typeLayout->getSubObjectRangeOffset(r));
+ subObjectRange.stride = SubObjectRangeStride(slangLeafTypeLayout);
+
+ switch(slangBindingType)
+ {
+ case slang::BindingType::ParameterBlock:
+ m_childDescriptorSetCount += subObjectLayout->getTotalDescriptorSetCount();
+ m_childPushConstantRangeCount += subObjectLayout->getTotalPushConstantRangeCount();
+ break;
+
+ case slang::BindingType::ConstantBuffer:
+ m_childDescriptorSetCount += subObjectLayout->getChildDescriptorSetCount();
+ m_totalBindingCount += subObjectLayout->getTotalBindingCount();
+ m_childPushConstantRangeCount += subObjectLayout->getTotalPushConstantRangeCount();
+ break;
+
+ case slang::BindingType::ExistentialValue:
+ if(subObjectLayout)
+ {
+ m_childDescriptorSetCount += subObjectLayout->getChildDescriptorSetCount();
+ m_totalBindingCount += subObjectLayout->getTotalBindingCount();
+ m_childPushConstantRangeCount += subObjectLayout->getTotalPushConstantRangeCount();
+
+ // An interface-type range that includes ordinary data can
+ // increase the size of the ordinary data buffer we need to
+ // allocate for the parent object.
+ //
+ uint32_t ordinaryDataEnd = subObjectRange.offset.pendingOrdinaryData
+ + (uint32_t) bindingRange.count * subObjectRange.stride.pendingOrdinaryData;
+
+ if(ordinaryDataEnd > m_totalOrdinaryDataSize)
+ {
+ m_totalOrdinaryDataSize = ordinaryDataEnd;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
m_subObjectRanges.add(subObjectRange);
}
+ }
+
+ Result setElementTypeLayout(
+ slang::TypeLayoutReflection* typeLayout)
+ {
+ typeLayout = _unwrapParameterGroups(typeLayout);
+ m_elementTypeLayout = typeLayout;
+
+ m_totalOrdinaryDataSize = (uint32_t) typeLayout->getSize();
+
+ // Next we will compute the binding ranges that are used to store
+ // the logical contents of the object in memory. These will relate
+ // to the descriptor ranges in the various sets, but not always
+ // in a one-to-one fashion.
+
+ addBindingRanges(typeLayout);
+
+ // Note: This routine does not take responsibility for
+ // adding descriptor ranges at all, because the exact way
+ // that descriptor ranges need to be added varies between
+ // ordinary shader objects, root shader objects, and entry points.
+
return SLANG_OK;
}
@@ -960,11 +1441,53 @@ public:
static Result createForElementType(
VKDevice* renderer,
slang::TypeLayoutReflection* elementType,
- bool createConstantBufferForOrdinaryData,
ShaderObjectLayoutImpl** outLayout)
{
Builder builder(renderer);
- builder.setElementTypeLayout(elementType, createConstantBufferForOrdinaryData);
+ builder.setElementTypeLayout(elementType);
+
+ // When constructing a shader object layout directly from a reflected
+ // type in Slang, we want to compute the descriptor sets and ranges
+ // that would be used if this object were bound as a parameter block.
+ //
+ // It might seem like we need to deal with the other cases for how
+ // the shader object might be bound, but the descriptor ranges we
+ // compute here will only ever be used in parameter-block case.
+ //
+ // One important wrinkle is that we know that the parameter block
+ // allocated for `elementType` will potentially need a buffer `binding`
+ // for any ordinary data it contains.
+
+ bool needsOrdinaryDataBuffer = elementType->getSize(SLANG_PARAMETER_CATEGORY_UNIFORM) != 0;
+ uint32_t ordinaryDataBufferCount = needsOrdinaryDataBuffer ? 1 : 0;
+
+ // When binding the object, we know that the ordinary data buffer will
+ // always use a the first available `binding`, so its offset will be
+ // all zeroes.
+ //
+ BindingOffset containerOffset;
+
+ // In contrast, the `binding`s used by all the other entries in the
+ // parameter block will need to be offset by one if there was
+ // an ordinary data buffer.
+ //
+ BindingOffset elementOffset;
+ elementOffset.binding = ordinaryDataBufferCount;
+
+ // Furthermore, any `binding`s that arise due to "pending" data
+ // in the type of the object (due to specialization for existential types)
+ // will need to come after all the other `binding`s that were
+ // part of the "primary" (unspecialized) data.
+ //
+ uint32_t primaryDescriptorCount = ordinaryDataBufferCount
+ + (uint32_t) elementType->getSize(SLANG_PARAMETER_CATEGORY_DESCRIPTOR_TABLE_SLOT);
+ elementOffset.pending.binding = primaryDescriptorCount;
+
+ // Once we've computed the offset information, we simply add the
+ // descriptor ranges as if things were declared as a `ConstantBuffer<X>`,
+ // since that is how things will be laid out inside the parameter block.
+ //
+ builder._addDescriptorRangesAsConstantBuffer(elementType, containerOffset, elementOffset);
return builder.build(outLayout);
}
@@ -977,9 +1500,55 @@ public:
}
}
- List<DescriptorSetInfo> const& getDescriptorSets() { return m_descriptorSetInfos; }
+ /// Get the number of descriptor sets that are allocated for this object itself
+ /// (if it needed to be bound as a parameter block).
+ ///
+ uint32_t getOwnDescriptorSetCount() { return uint32_t(m_descriptorSetInfos.getCount()); }
+
+ /// Get information about the descriptor sets that would be allocated to
+ /// represent this object itself as a parameter block.
+ ///
+ List<DescriptorSetInfo> const& getOwnDescriptorSets() { return m_descriptorSetInfos; }
+
+ /// Get the number of descriptor sets that would need to be allocated and bound
+ /// to represent the children of this object if it were bound as a parameter
+ /// block.
+ ///
+ /// To a first approximation, this is the number of (transitive) children
+ /// that are declared as `ParameterBlock<X>`.
+ ///
+ uint32_t getChildDescriptorSetCount() { return m_childDescriptorSetCount; }
+
+ /// Get the total number of descriptor sets that would need to be allocated and bound
+ /// to represent this object and its children (transitively) as a parameter block.
+ ///
+ uint32_t getTotalDescriptorSetCount() { return getOwnDescriptorSetCount() + getChildDescriptorSetCount(); }
+
+ /// Get the total number of `binding`s required to represent this type and its
+ /// (transitive) children.
+ ///
+ /// Note that this count does *not* include bindings that would be part of child
+ /// parameter blocks, nor does it include the binding for an ordinary data buffer,
+ /// if one is needed.
+ ///
+ uint32_t getTotalBindingCount() { return m_totalBindingCount; }
+
- uint32_t getPushConstantSize() { return m_pushConstantSize; }
+ /// Get the list of push constant ranges required to bind the state of this object itself.
+ List<VkPushConstantRange> const& getOwnPushConstantRanges() const { return m_ownPushConstantRanges; }
+
+ /// Get the number of push constant ranges required to bind the state of this object itself.
+ uint32_t getOwnPushConstantRangeCount() { return (uint32_t) m_ownPushConstantRanges.getCount(); }
+
+ /// Get the number of push constant ranges required to bind the state of the (transitive)
+ /// children of this object.
+ uint32_t getChildPushConstantRangeCount() { return m_childPushConstantRangeCount; }
+
+ /// Get the total number of push constant ranges required to bind the state of this object
+ /// and its (transitive) children.
+ uint32_t getTotalPushConstantRangeCount() { return getOwnPushConstantRangeCount() + getChildPushConstantRangeCount(); }
+
+ uint32_t getTotalOrdinaryDataSize() const { return m_totalOrdinaryDataSize; }
List<BindingRangeInfo> const& getBindingRanges() { return m_bindingRanges; }
@@ -1014,12 +1583,15 @@ public:
m_bindingRanges = builder->m_bindingRanges;
m_descriptorSetInfos = _Move(builder->m_descriptorSetBuildInfos);
- m_pushConstantSize = builder->m_pushConstantSize;
+ m_ownPushConstantRanges = builder->m_ownPushConstantRanges;
m_resourceViewCount = builder->m_resourceViewCount;
m_samplerCount = builder->m_samplerCount;
m_combinedTextureSamplerCount = builder->m_combinedTextureSamplerCount;
+ m_childDescriptorSetCount = builder->m_childDescriptorSetCount;
+ m_totalBindingCount = builder->m_totalBindingCount;
m_subObjectCount = builder->m_subObjectCount;
m_subObjectRanges = builder->m_subObjectRanges;
+ m_totalOrdinaryDataSize = builder->m_totalOrdinaryDataSize;
// Create VkDescriptorSetLayout for all descriptor sets.
for (auto& descriptorSetInfo : m_descriptorSetInfos)
@@ -1042,7 +1614,13 @@ public:
Index m_samplerCount = 0;
Index m_combinedTextureSamplerCount = 0;
Index m_subObjectCount = 0;
- uint32_t m_pushConstantSize = 0;
+ List<VkPushConstantRange> m_ownPushConstantRanges;
+ uint32_t m_childPushConstantRangeCount = 0;
+
+ uint32_t m_childDescriptorSetCount = 0;
+ uint32_t m_totalBindingCount = 0;
+ uint32_t m_totalOrdinaryDataSize = 0;
+
List<SubObjectRangeInfo> m_subObjectRanges;
};
@@ -1069,15 +1647,18 @@ public:
void addEntryPointParams(slang::EntryPointLayout* entryPointLayout)
{
m_slangEntryPointLayout = entryPointLayout;
- setElementTypeLayout(entryPointLayout->getTypeLayout(), false);
- m_pushConstantSize = (uint32_t)_unwrapParameterGroups(entryPointLayout->getTypeLayout())
- ->getSize(SLANG_PARAMETER_CATEGORY_UNIFORM);
- m_stage = VulkanUtil::getShaderStage(entryPointLayout->getStage());
+ setElementTypeLayout(entryPointLayout->getTypeLayout());
+ m_shaderStageFlag = VulkanUtil::getShaderStage(entryPointLayout->getStage());
+
+ // Note: we do not bother adding any descriptor sets/ranges here,
+ // because the descriptor ranges of an entry point will simply
+ // be allocated as part of the descriptor sets for the root
+ // shader object.
}
slang::EntryPointLayout* m_slangEntryPointLayout = nullptr;
- VkShaderStageFlags m_stage;
+ VkShaderStageFlags m_shaderStageFlag;
};
Result _init(Builder const* builder)
@@ -1087,16 +1668,16 @@ public:
SLANG_RETURN_ON_FAIL(Super::_init(builder));
m_slangEntryPointLayout = builder->m_slangEntryPointLayout;
- m_stage = builder->m_stage;
+ m_shaderStageFlag = builder->m_shaderStageFlag;
return SLANG_OK;
}
- VkShaderStageFlags getStage() const { return m_stage; }
+ VkShaderStageFlags getShaderStageFlag() const { return m_shaderStageFlag; }
slang::EntryPointLayout* getSlangLayout() const { return m_slangEntryPointLayout; };
slang::EntryPointLayout* m_slangEntryPointLayout;
- VkShaderStageFlags m_stage;
+ VkShaderStageFlags m_shaderStageFlag;
};
class RootShaderObjectLayout : public ShaderObjectLayoutImpl
@@ -1113,10 +1694,14 @@ public:
}
}
+ /// Information stored for each entry point of the program
struct EntryPointInfo
{
+ /// Layout of the entry point
RefPtr<EntryPointLayout> layout;
- Index rangeOffset;
+
+ /// Offset for binding the entry point, relative to the start of the program
+ BindingOffset offset;
};
struct Builder : Super::Builder
@@ -1140,34 +1725,74 @@ public:
void addGlobalParams(slang::VariableLayoutReflection* globalsLayout)
{
- setElementTypeLayout(globalsLayout->getTypeLayout(), true);
+ setElementTypeLayout(globalsLayout->getTypeLayout());
+
+ // We need to populate our descriptor sets/ranges with information
+ // from the layout of the global scope.
+ //
+ // While we expect that the parameter in the global scope start
+ // at an offset of zero, it is also worth querying the offset
+ // information because it could impact the locations assigned
+ // to "pending" data in the case of static specialization.
+ //
+ BindingOffset offset(globalsLayout);
+
+ // Note: We are adding descriptor ranges here based directly on
+ // the type of the global-scope layout. The type layout for the
+ // global scope will either be something like a `struct GlobalParams`
+ // that contains all the global-scope parameters or a `ConstantBuffer<GlobalParams>`
+ // and in either case the `_addDescriptorRangesAsValue` can properly
+ // add all the ranges implied.
+ //
+ // As a result we don't require any special-case logic here to
+ // deal with the possibility of a "default" constant buffer allocated
+ // for global-scope parameters of uniform/ordinary type.
+ //
+ _addDescriptorRangesAsValue(globalsLayout->getTypeLayout(), offset);
+
+ // We want to keep track of the offset that was applied to "pending"
+ // data because we will need it again later when it comes time to
+ // actually bind things.
+ //
+ m_pendingDataOffset = offset.pending;
}
void addEntryPoint(EntryPointLayout* entryPointLayout)
{
+ auto slangEntryPointLayout = entryPointLayout->getSlangLayout();
+ auto entryPointVarLayout = slangEntryPointLayout->getVarLayout();
+
+ // The offset information for each entry point needs to
+ // be adjusted by any offset for "pending" data that
+ // was recorded in the global-scope layout.
+ //
+ // TODO(tfoley): Double-check that this is correct.
+
+ BindingOffset entryPointOffset(entryPointVarLayout);
+ entryPointOffset.pending += m_pendingDataOffset;
+
EntryPointInfo info;
info.layout = entryPointLayout;
+ info.offset = entryPointOffset;
+
+ // Similar to the case for the global scope, we expect the
+ // type layout for the entry point parameters to be either
+ // a `struct EntryPointParams` or a `PushConstantBuffer<EntryPointParams>`.
+ // Rather than deal with the different cases here, we will
+ // trust the `_addDescriptorRangesAsValue` code to handle
+ // either case correctly.
+ //
+ _addDescriptorRangesAsValue(entryPointVarLayout->getTypeLayout(), entryPointOffset);
- if (m_descriptorSetBuildInfos.getCount())
- {
- info.rangeOffset = m_descriptorSetBuildInfos[0].vkBindings.getCount();
- }
- else
- {
- info.rangeOffset = 0;
- }
-
- auto slangEntryPointLayout = entryPointLayout->getSlangLayout();
- _addDescriptorSets(
- _unwrapParameterGroups(slangEntryPointLayout->getTypeLayout()),
- false,
- slangEntryPointLayout->getVarLayout());
m_entryPoints.add(info);
}
slang::IComponentType* m_program;
slang::ProgramLayout* m_programLayout;
List<EntryPointInfo> m_entryPoints;
+
+ /// Offset to apply to "pending" data from this object, sub-objects, and entry points
+ SimpleBindingOffset m_pendingDataOffset;
};
Index findEntryPointIndex(VkShaderStageFlags stage)
@@ -1176,7 +1801,7 @@ public:
for (Index i = 0; i < entryPointCount; ++i)
{
auto entryPoint = m_entryPoints[i];
- if (entryPoint.layout->getStage() == stage)
+ if (entryPoint.layout->getShaderStageFlag() == stage)
return i;
}
return -1;
@@ -1214,9 +1839,14 @@ public:
return SLANG_OK;
}
+ SimpleBindingOffset const& getPendingDataOffset() const { return m_pendingDataOffset; }
+
slang::IComponentType* getSlangProgram() const { return m_program; }
slang::ProgramLayout* getSlangProgramLayout() const { return m_programLayout; }
+ /// Get all of the push constant ranges that will be bound for this object and all (transitive) sub-objects
+ List<VkPushConstantRange> const& getAllPushConstantRanges() { return m_allPushConstantRanges; }
+
protected:
Result _init(Builder const* builder)
{
@@ -1227,75 +1857,156 @@ public:
m_program = builder->m_program;
m_programLayout = builder->m_programLayout;
m_entryPoints = _Move(builder->m_entryPoints);
+ m_pendingDataOffset = builder->m_pendingDataOffset;
m_renderer = renderer;
+ // If the program has unbound specialization parameters,
+ // then we will avoid creating a final Vulkan pipeline layout.
+ //
+ // TODO: We should really create the information necessary
+ // for binding as part of a separate object, so that we have
+ // a clean seperation between what is needed for writing into
+ // a shader object vs. what is needed for binding it to the
+ // pipeline. We eventually need to be able to create bindable
+ // state objects from unspecialized programs, in order to
+ // support dynamic dispatch.
+ //
if (m_program->getSpecializationParamCount() != 0)
return SLANG_OK;
- // For fully specialized shader programs, we create a Vulkan pipeline layout now.
+ // Otherwise, we need to create a final (bindable) layout.
+ //
+ // We will use a recursive walk to collect all the `VkDescriptorSetLayout`s
+ // that are required for the global scope, sub-objects, and entry points.
+ //
+ SLANG_RETURN_ON_FAIL(addAllDescriptorSets());
- // First, collect `VkDescriptorSetLayout`s for the global scope and all sub-objects
- // referenced via a `ParameterBlock` from shader object layouts.
- SLANG_RETURN_ON_FAIL(addDescriptorSetLayoutRec(this));
+ // We will also use a recursive walk to collect all the push-constant
+ // ranges needed for this object, sub-objects, and entry points.
+ //
+ SLANG_RETURN_ON_FAIL(addAllPushConstantRanges());
- // Next, collect push constant ranges. We will use one descriptor range for each
- // entry point that has uniform parameters.
- uint32_t pushConstantOffset = 0;
- for (auto& entryPoint : m_entryPoints)
- {
- auto size = entryPoint.layout->getPushConstantSize();
- if (size)
- {
- VkPushConstantRange pushConstantRange = {};
- pushConstantRange.offset = pushConstantOffset;
- pushConstantRange.size = size;
- pushConstantRange.stageFlags = entryPoint.layout->getStage();
- m_pushConstantRanges.add(pushConstantRange);
- pushConstantOffset += size;
- }
- }
+ // Once we've collected the information across the entire
+ // tree of sub-objects
// Now call Vulkan API to create a pipeline layout.
VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = {};
pipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutCreateInfo.setLayoutCount = (uint32_t)m_vkDescriptorSetLayouts.getCount();
pipelineLayoutCreateInfo.pSetLayouts = m_vkDescriptorSetLayouts.getBuffer();
- if (m_pushConstantRanges.getCount())
+ if (m_allPushConstantRanges.getCount())
{
pipelineLayoutCreateInfo.pushConstantRangeCount =
- (uint32_t)m_pushConstantRanges.getCount();
+ (uint32_t)m_allPushConstantRanges.getCount();
pipelineLayoutCreateInfo.pPushConstantRanges =
- m_pushConstantRanges.getBuffer();
+ m_allPushConstantRanges.getBuffer();
}
SLANG_RETURN_ON_FAIL(m_renderer->m_api.vkCreatePipelineLayout(
m_renderer->m_api.m_device, &pipelineLayoutCreateInfo, nullptr, &m_pipelineLayout));
return SLANG_OK;
}
- // Recusively add `VkDescriptorSetLayout` for all descriptor sets used by this and children
- // shader objects and add them to `m_vkDescriptorSetLayouts`.
- Result addDescriptorSetLayoutRec(ShaderObjectLayoutImpl* layout)
+ /// Add all the descriptor sets implied by this root object and sub-objects
+ Result addAllDescriptorSets()
+ {
+ SLANG_RETURN_ON_FAIL(addAllDescriptorSetsRec(this));
+
+ // Note: the descriptor ranges/sets for direct entry point parameters
+ // were already enumerated into the ranges/sets of the root object itself,
+ // so we don't wnat to add them again.
+ //
+ // We do however have to deal with the possibility that an entry
+ // point could introduce "child" descriptor sets, e.g., because it
+ // has a `ParameterBlock<X>` parameter.
+ //
+ for(auto& entryPoint : getEntryPoints())
+ {
+ SLANG_RETURN_ON_FAIL(addChildDescriptorSetsRec(entryPoint.layout));
+ }
+
+ return SLANG_OK;
+ }
+
+ /// Recurisvely add descriptor sets defined by `layout` and sub-objects
+ Result addAllDescriptorSetsRec(ShaderObjectLayoutImpl* layout)
{
- for (auto& descSetInfo : layout->getDescriptorSets())
+ // TODO: This logic assumes that descriptor sets are all contiguous
+ // and have been allocated in a global order that matches the order
+ // of enumeration here.
+
+ for (auto& descSetInfo : layout->getOwnDescriptorSets())
{
m_vkDescriptorSetLayouts.add(descSetInfo.descriptorSetLayout);
}
- // Note: entry point parameters in a `RootShaderObject` has already been included
- // in `layout->getDescriptorSets()` during `RootShaderObjectLayout` construction,
- // so we do not need to enumerate entry point array here.
-
- // However, for sub-objects referenced through `ParameterBlock`s, we do need to
- // add their descriptor sets to our pipeline layout.
- // Binding ranges for sub-objects referenced through `ConstantBuffer`s are also
- // included in this object's layout already, so no need to skip those.
+ SLANG_RETURN_ON_FAIL(addChildDescriptorSetsRec(layout));
+ return SLANG_OK;
+ }
+ /// Recurisvely add descriptor sets defined by sub-objects of `layout`
+ Result addChildDescriptorSetsRec(ShaderObjectLayoutImpl* layout)
+ {
for (auto& subObject : layout->getSubObjectRanges())
{
auto bindingRange = layout->getBindingRange(subObject.bindingRangeIndex);
- if (bindingRange.bindingType == slang::BindingType::ParameterBlock)
+ switch(bindingRange.bindingType)
+ {
+ case slang::BindingType::ParameterBlock:
+ SLANG_RETURN_ON_FAIL(addAllDescriptorSetsRec(subObject.layout));
+ break;
+
+ default:
+ if(auto subObjectLayout = subObject.layout)
+ {
+ SLANG_RETURN_ON_FAIL(addChildDescriptorSetsRec(subObject.layout));
+ }
+ break;
+ }
+ }
+
+ return SLANG_OK;
+ }
+
+ /// Add all the push-constant ranges implied by this root object and sub-objects
+ Result addAllPushConstantRanges()
+ {
+ SLANG_RETURN_ON_FAIL(addAllPushConstantRangesRec(this));
+
+ for(auto& entryPoint : getEntryPoints())
+ {
+ SLANG_RETURN_ON_FAIL(addChildPushConstantRangesRec(entryPoint.layout));
+ }
+
+ return SLANG_OK;
+ }
+
+ /// Recurisvely add push-constant ranges defined by `layout` and sub-objects
+ Result addAllPushConstantRangesRec(ShaderObjectLayoutImpl* layout)
+ {
+ // TODO: This logic assumes that push-constant ranges are all contiguous
+ // and have been allocated in a global order that matches the order
+ // of enumeration here.
+
+ for (auto pushConstantRange : layout->getOwnPushConstantRanges())
+ {
+ pushConstantRange.offset = m_totalPushConstantSize;
+ m_totalPushConstantSize += pushConstantRange.size;
+
+ m_allPushConstantRanges.add(pushConstantRange);
+ }
+
+ SLANG_RETURN_ON_FAIL(addChildPushConstantRangesRec(layout));
+ return SLANG_OK;
+ }
+
+ /// Recurisvely add push-constant ranges defined by sub-objects of `layout`
+ Result addChildPushConstantRangesRec(ShaderObjectLayoutImpl* layout)
+ {
+ for (auto& subObject : layout->getSubObjectRanges())
+ {
+ if(auto subObjectLayout = subObject.layout)
{
- SLANG_RETURN_ON_FAIL(addDescriptorSetLayoutRec(subObject.layout));
+ SLANG_RETURN_ON_FAIL(addAllPushConstantRangesRec(subObject.layout));
}
}
@@ -1308,7 +2019,10 @@ public:
List<EntryPointInfo> m_entryPoints;
VkPipelineLayout m_pipelineLayout = VK_NULL_HANDLE;
Array<VkDescriptorSetLayout, kMaxDescriptorSets> m_vkDescriptorSetLayouts;
- Array<VkPushConstantRange, 8> m_pushConstantRanges;
+ List<VkPushConstantRange> m_allPushConstantRanges;
+ uint32_t m_totalPushConstantSize = 0;
+
+ SimpleBindingOffset m_pendingDataOffset;
VKDevice* m_renderer = nullptr;
};
@@ -1441,7 +2155,6 @@ public:
void flushBindingState(VkPipelineBindPoint pipelineBindPoint)
{
auto& api = *m_api;
- bindRootShaderObjectImpl(pipelineBindPoint);
// Get specialized pipeline state and bind it.
//
@@ -1449,6 +2162,9 @@ public:
m_device->maybeSpecializePipeline(
m_currentPipeline, &m_commandBuffer->m_rootObject, newPipeline);
PipelineStateImpl* newPipelineImpl = static_cast<PipelineStateImpl*>(newPipeline.Ptr());
+
+ bindRootShaderObjectImpl(pipelineBindPoint);
+
auto pipelineBindPointId = getBindPointIndex(pipelineBindPoint);
if (m_boundPipelines[pipelineBindPointId] != newPipelineImpl->m_pipeline)
{
@@ -1459,28 +2175,23 @@ public:
}
};
- union VulkanDescriptorInfo
- {
- VkDescriptorBufferInfo bufferInfo;
- VkDescriptorImageInfo imageInfo;
- };
- struct RootBindingState
+ /// Context information required when binding shader objects to the pipeline
+ struct RootBindingContext
{
- ShortList<VkWriteDescriptorSet, 32> descriptorSetWrites;
- ChunkedList<VulkanDescriptorInfo, 32> descriptorInfos;
- ChunkedList<VkBufferView, 8> bufferViews;
- Array<VkDescriptorSet, kMaxDescriptorSets> descriptorSets;
- ArrayView<VkPushConstantRange> pushConstantRanges;
+ /// The pipeline layout being used for binding
VkPipelineLayout pipelineLayout;
+
+ /// An allocator to use for descriptor sets during binding
DescriptorSetAllocator* descriptorSetAllocator;
+
+ /// The dvice being used
VKDevice* device;
- };
- struct BindingOffset
- {
- uint32_t uniformOffset;
- uint32_t pushConstantRangeOffset;
- uint32_t descriptorSetIndexOffset;
- uint32_t descriptorRangeOffset;
+
+ /// The descriptor sets that are being allocated and bound
+ VkDescriptorSet* descriptorSets;
+
+ /// Information about all the push-constant ranges that should be bound
+ ConstArrayView<VkPushConstantRange> pushConstantRanges;
};
class ShaderObjectImpl : public ShaderObjectBase
@@ -1949,10 +2660,8 @@ public:
// layout logic does for complex cases with multiple layers of nested arrays and
// structures.
//
- size_t subObjectRangePendingDataOffset =
- _getSubObjectRangePendingDataOffset(specializedLayout, subObjectRangeIndex);
- size_t subObjectRangePendingDataStride =
- _getSubObjectRangePendingDataStride(specializedLayout, subObjectRangeIndex);
+ size_t subObjectRangePendingDataOffset = subObjectRangeInfo.offset.pendingOrdinaryData;
+ size_t subObjectRangePendingDataStride = subObjectRangeInfo.stride.pendingOrdinaryData;
// If the range doesn't actually need/use the "pending" allocation at all, then
// we need to detect that case and skip such ranges.
@@ -1987,22 +2696,6 @@ public:
return SLANG_OK;
}
- // As discussed in `_writeOrdinaryData()`, these methods are just stubs waiting for
- // the "flat" Slang refelction information to provide access to the relevant data.
- //
- size_t _getSubObjectRangePendingDataOffset(
- ShaderObjectLayoutImpl* specializedLayout,
- Index subObjectRangeIndex)
- {
- return 0;
- }
- size_t _getSubObjectRangePendingDataStride(
- ShaderObjectLayoutImpl* specializedLayout,
- Index subObjectRangeIndex)
- {
- return 0;
- }
-
public:
struct CombinedTextureSamplerSlot
{
@@ -2011,218 +2704,219 @@ public:
operator bool() { return textureView && sampler; }
};
- // A shared template function for composing a VkWriteDescriptorSet structure.
- // The signature for `WriteDescriptorInfoFunc` is
- // `void(VkWriteDescriptorSet&, int startElement, int elementCount)`, which sets up
- // `VkWriteDescriptorSet::pBufferInfo`, `pImageInfo` or `pTexelBufferView` fields.
- template<typename WriteDescriptorInfoFunc, typename TResourceArrayView>
- static void _writeDescriptorRange(
- RootBindingState* bindingState,
- BindingOffset offset,
- VkDescriptorType descriptorType,
- TResourceArrayView resourceViews,
- const WriteDescriptorInfoFunc& writeDescriptorInfo)
+ /// Write a single desriptor using the Vulkan API
+ static inline void writeDescriptor(
+ RootBindingContext& context,
+ VkWriteDescriptorSet const& write)
{
- auto descriptorSet = bindingState->descriptorSets[offset.descriptorSetIndexOffset];
- bool hasNullBinding = false;
- for (auto& ptr : resourceViews)
- {
- if (!ptr)
- {
- hasNullBinding = true;
- break;
- }
- }
- if (hasNullBinding)
- {
- for (Index i = 0; i < resourceViews.getCount(); i++)
- {
- if (!resourceViews[i])
- continue;
- VkWriteDescriptorSet write = {};
- write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
- write.descriptorCount = 1;
- write.descriptorType = descriptorType;
- write.dstArrayElement = (uint32_t)i;
- write.dstBinding = offset.descriptorRangeOffset;
- write.dstSet = descriptorSet;
- auto infos = bindingState->descriptorInfos.reserveRange(1);
- writeDescriptorInfo(write, (uint32_t)i, 1);
- bindingState->descriptorSetWrites.add(write);
- }
- return;
- }
-
- VkWriteDescriptorSet write = {};
- write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
- write.descriptorCount = (uint32_t)resourceViews.getCount();
- write.descriptorType = descriptorType;
- write.dstArrayElement = 0;
- write.dstBinding = offset.descriptorRangeOffset;
- write.dstSet = descriptorSet;
- writeDescriptorInfo(write, 0, write.descriptorCount);
- bindingState->descriptorSetWrites.add(write);
+ auto device = context.device;
+ device->m_api.vkUpdateDescriptorSets(
+ device->m_device,
+ 1,
+ &write,
+ 0,
+ nullptr);
}
static void writeBufferDescriptor(
- RootBindingState* bindingState,
- BindingOffset offset,
+ RootBindingContext& context,
+ BindingOffset const& offset,
VkDescriptorType descriptorType,
BufferResourceImpl* buffer,
size_t bufferOffset,
size_t bufferSize)
{
- auto descriptorSet = bindingState->descriptorSets[offset.descriptorSetIndexOffset];
+ auto descriptorSet = context.descriptorSets[offset.bindingSet];
+
+ VkDescriptorBufferInfo bufferInfo = {};
+ bufferInfo.buffer = buffer->m_buffer.m_buffer;
+ bufferInfo.offset = bufferOffset;
+ bufferInfo.range = bufferSize;
+
VkWriteDescriptorSet write = {};
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write.descriptorCount = 1;
write.descriptorType = descriptorType;
write.dstArrayElement = 0;
- write.dstBinding = offset.descriptorRangeOffset;
+ write.dstBinding = offset.binding;
write.dstSet = descriptorSet;
- auto& bufferInfo = bindingState->descriptorInfos.reserveRange(1)->bufferInfo;
write.pBufferInfo = &bufferInfo;
- bufferInfo.buffer = buffer->m_buffer.m_buffer;
- bufferInfo.offset = bufferOffset;
- bufferInfo.range = bufferSize;
- bindingState->descriptorSetWrites.add(write);
+
+ writeDescriptor(context, write);
}
static void writeBufferDescriptor(
- RootBindingState* bindingState,
- BindingOffset offset,
+ RootBindingContext& context,
+ BindingOffset const& offset,
VkDescriptorType descriptorType,
BufferResourceImpl* buffer)
{
writeBufferDescriptor(
- bindingState, offset, descriptorType, buffer, 0, buffer->getDesc()->sizeInBytes);
+ context, offset, descriptorType, buffer, 0, buffer->getDesc()->sizeInBytes);
}
+
static void writePlainBufferDescriptor(
- RootBindingState* bindingState,
- BindingOffset offset,
+ RootBindingContext& context,
+ BindingOffset const& offset,
VkDescriptorType descriptorType,
ArrayView<RefPtr<ResourceViewImpl>> resourceViews)
{
- auto writeDescriptorInfo = [=](VkWriteDescriptorSet& write,
- uint32_t startElement,
- uint32_t count)
+ auto descriptorSet = context.descriptorSets[offset.bindingSet];
+
+ Index count = resourceViews.getCount();
+ for(Index i = 0; i < count; ++i)
{
- auto infos = bindingState->descriptorInfos.reserveRange(count);
- write.pBufferInfo = (VkDescriptorBufferInfo*)infos;
- for (uint32_t i = startElement; i < count; i++)
+ auto bufferView = static_cast<PlainBufferResourceViewImpl*>(resourceViews[i].Ptr());
+
+ VkDescriptorBufferInfo bufferInfo = {};
+
+ if(bufferView)
{
- auto bufferView =
- static_cast<PlainBufferResourceViewImpl*>(resourceViews[i].Ptr());
- if (bufferView)
- {
- infos[i].bufferInfo.buffer = bufferView->m_buffer->m_buffer.m_buffer;
- infos[i].bufferInfo.offset = 0;
- infos[i].bufferInfo.range = bufferView->m_buffer->getDesc()->sizeInBytes;
- }
+ bufferInfo.buffer = bufferView->m_buffer->m_buffer.m_buffer;
+ bufferInfo.offset = 0;
+ bufferInfo.range = bufferView->m_buffer->getDesc()->sizeInBytes;
}
- };
- _writeDescriptorRange(
- bindingState, offset, descriptorType, resourceViews, writeDescriptorInfo);
+
+ VkWriteDescriptorSet write = {};
+ write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ write.descriptorCount = 1;
+ write.descriptorType = descriptorType;
+ write.dstArrayElement = uint32_t(i);
+ write.dstBinding = offset.binding;
+ write.dstSet = descriptorSet;
+ write.pBufferInfo = &bufferInfo;
+
+ writeDescriptor(context, write);
+ }
}
static void writeTexelBufferDescriptor(
- RootBindingState* bindingState,
- BindingOffset offset,
+ RootBindingContext& context,
+ BindingOffset const& offset,
VkDescriptorType descriptorType,
ArrayView<RefPtr<ResourceViewImpl>> resourceViews)
{
- auto writeDescriptorInfo = [=](VkWriteDescriptorSet& write,
- uint32_t startElement,
- uint32_t count)
+ auto descriptorSet = context.descriptorSets[offset.bindingSet];
+
+ Index count = resourceViews.getCount();
+ for(Index i = 0; i < count; ++i)
{
- auto views = bindingState->bufferViews.reserveRange(write.descriptorCount);
- write.pTexelBufferView = views;
- for (uint32_t i = startElement; i < count; i++)
- {
- views[i] =
- static_cast<TexelBufferResourceViewImpl*>(resourceViews[i].Ptr())->m_view;
- }
- };
- _writeDescriptorRange(
- bindingState, offset, descriptorType, resourceViews, writeDescriptorInfo);
+ auto resourceView = static_cast<TexelBufferResourceViewImpl*>(resourceViews[i].Ptr());
+
+ VkBufferView bufferView = resourceView->m_view;
+
+ VkWriteDescriptorSet write = {};
+ write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ write.descriptorCount = 1;
+ write.descriptorType = descriptorType;
+ write.dstArrayElement = uint32_t(i);
+ write.dstBinding = offset.binding;
+ write.dstSet = descriptorSet;
+ write.pTexelBufferView = &bufferView;
+
+ writeDescriptor(context, write);
+ }
}
static void writeTextureSamplerDescriptor(
- RootBindingState* bindingState,
- BindingOffset offset,
+ RootBindingContext& context,
+ BindingOffset const& offset,
VkDescriptorType descriptorType,
ArrayView<CombinedTextureSamplerSlot> slots)
{
- auto writeDescriptorInfo = [=](VkWriteDescriptorSet& write,
- uint32_t startElement,
- uint32_t count)
+ auto descriptorSet = context.descriptorSets[offset.bindingSet];
+
+ Index count = slots.getCount();
+ for(Index i = 0; i < count; ++i)
{
- auto infos = bindingState->descriptorInfos.reserveRange(write.descriptorCount);
- write.pImageInfo = (VkDescriptorImageInfo*)infos;
- for (uint32_t i = startElement; i < count; i++)
- {
- auto texture = slots[i].textureView;
- auto sampler = slots[i].sampler;
- auto& imageInfo = ((VkDescriptorImageInfo*)infos)[i];
- imageInfo.imageView = texture->m_view;
- imageInfo.imageLayout = texture->m_layout;
- imageInfo.sampler = sampler->m_sampler;
- }
- };
- _writeDescriptorRange(bindingState, offset, descriptorType, slots, writeDescriptorInfo);
+ auto texture = slots[i].textureView;
+ auto sampler = slots[i].sampler;
+
+ VkDescriptorImageInfo imageInfo = {};
+ imageInfo.imageView = texture->m_view;
+ imageInfo.imageLayout = texture->m_layout;
+ imageInfo.sampler = sampler->m_sampler;
+
+ VkWriteDescriptorSet write = {};
+ write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ write.descriptorCount = 1;
+ write.descriptorType = descriptorType;
+ write.dstArrayElement = uint32_t(i);
+ write.dstBinding = offset.binding;
+ write.dstSet = descriptorSet;
+ write.pImageInfo = &imageInfo;
+
+ writeDescriptor(context, write);
+ }
}
static void writeTextureDescriptor(
- RootBindingState* bindingState,
- BindingOffset offset,
+ RootBindingContext& context,
+ BindingOffset const& offset,
VkDescriptorType descriptorType,
ArrayView<RefPtr<ResourceViewImpl>> resourceViews)
{
- auto writeDescriptorInfo =
- [=](VkWriteDescriptorSet& write, uint32_t startElement, uint32_t count)
+ auto descriptorSet = context.descriptorSets[offset.bindingSet];
+
+ Index count = resourceViews.getCount();
+ for(Index i = 0; i < count; ++i)
{
- auto infos = bindingState->descriptorInfos.reserveRange(write.descriptorCount);
- write.pImageInfo = (VkDescriptorImageInfo*)infos;
- for (uint32_t i = startElement; i < count; i++)
- {
- auto texture = static_cast<TextureResourceViewImpl*>(resourceViews[i].Ptr());
- auto& imageInfo = ((VkDescriptorImageInfo*)infos)[i];
- imageInfo.imageView = texture->m_view;
- imageInfo.imageLayout = texture->m_layout;
- imageInfo.sampler = 0;
- }
- };
- _writeDescriptorRange(
- bindingState, offset, descriptorType, resourceViews, writeDescriptorInfo);
+ auto texture = static_cast<TextureResourceViewImpl*>(resourceViews[i].Ptr());
+
+ VkDescriptorImageInfo imageInfo = {};
+ imageInfo.imageView = texture->m_view;
+ imageInfo.imageLayout = texture->m_layout;
+ imageInfo.sampler = 0;
+
+ VkWriteDescriptorSet write = {};
+ write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ write.descriptorCount = 1;
+ write.descriptorType = descriptorType;
+ write.dstArrayElement = uint32_t(i);
+ write.dstBinding = offset.binding;
+ write.dstSet = descriptorSet;
+ write.pImageInfo = &imageInfo;
+
+ writeDescriptor(context, write);
+ }
}
static void writeSamplerDescriptor(
- RootBindingState* bindingState,
- BindingOffset offset,
+ RootBindingContext& context,
+ BindingOffset const& offset,
VkDescriptorType descriptorType,
ArrayView<RefPtr<SamplerStateImpl>> samplers)
{
- auto writeDescriptorInfo =
- [=](VkWriteDescriptorSet& write, uint32_t startElement, uint32_t count)
+ auto descriptorSet = context.descriptorSets[offset.bindingSet];
+
+ Index count = samplers.getCount();
+ for(Index i = 0; i < count; ++i)
{
- auto infos = bindingState->descriptorInfos.reserveRange(write.descriptorCount);
- write.pImageInfo = (VkDescriptorImageInfo*)infos;
- for (uint32_t i = startElement; i < count; i++)
- {
- auto texture = samplers[i]->m_sampler;
- auto& imageInfo = ((VkDescriptorImageInfo*)infos)[i];
- imageInfo.imageView = 0;
- imageInfo.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
- imageInfo.sampler = samplers[i]->m_sampler;
- }
- };
- _writeDescriptorRange(
- bindingState, offset, descriptorType, samplers, writeDescriptorInfo);
+ auto sampler = samplers[i];
+
+ VkDescriptorImageInfo imageInfo = {};
+ imageInfo.imageView = 0;
+ imageInfo.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
+ imageInfo.sampler = sampler->m_sampler;
+
+ VkWriteDescriptorSet write = {};
+ write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ write.descriptorCount = 1;
+ write.descriptorType = descriptorType;
+ write.dstArrayElement = uint32_t(i);
+ write.dstBinding = offset.binding;
+ write.dstSet = descriptorSet;
+ write.pImageInfo = &imageInfo;
+
+ writeDescriptor(context, write);
+ }
}
/// Ensure that the `m_ordinaryDataBuffer` has been created, if it is needed
- Result _ensureOrdinaryDataBufferCreatedIfNeeded(PipelineCommandEncoder* encoder)
+ Result _ensureOrdinaryDataBufferCreatedIfNeeded(
+ PipelineCommandEncoder* encoder,
+ ShaderObjectLayoutImpl* specializedLayout)
{
// If we have already created a buffer to hold ordinary data, then we should
// simply re-use that buffer rather than re-create it.
@@ -2238,22 +2932,7 @@ public:
return SLANG_OK;
}
- // Computing the size of the ordinary data buffer is *not* just as simple
- // as using the size of the `m_ordinayData` array that we store. The reason
- // for the added complexity is that interface-type fields may lead to the
- // storage being specialized such that it needs extra appended data to
- // store the concrete values that logically belong in those interface-type
- // fields but wouldn't fit in the fixed-size allocation we gave them.
- //
- // TODO: We need to actually implement that logic by using reflection
- // data computed for the specialized type of this shader object.
- // For now we just make the simple assumption described above despite
- // knowing that it is false.
- //
- RefPtr<ShaderObjectLayoutImpl> specializedLayout;
- SLANG_RETURN_ON_FAIL(_getSpecializedLayout(specializedLayout.writeRef()));
-
- m_constantBufferSize = specializedLayout->getElementTypeLayout()->getSize();
+ m_constantBufferSize = specializedLayout->getTotalOrdinaryDataSize();
if (m_constantBufferSize == 0)
{
m_upToDateConstantBufferHeapVersion =
@@ -2287,187 +2966,344 @@ public:
return SLANG_OK;
}
- /// Bind the buffer for ordinary/uniform data, if needed
- Result _bindOrdinaryDataBufferIfNeeded(
- PipelineCommandEncoder* encoder,
- RootBindingState* bindingState,
- BindingOffset& offset)
- {
- // We are going to need to tweak the base binding range index
- // used for descriptor-set writes if and only if we actually
- // bind a buffer for ordinary data.
- //
- auto& baseRangeIndex = offset.descriptorRangeOffset;
-
- // We start by ensuring that the buffer is created, if it is needed.
- //
- SLANG_RETURN_ON_FAIL(_ensureOrdinaryDataBufferCreatedIfNeeded(encoder));
-
- // If we did indeed need/create a buffer, then we must bind it into
- // the given `descriptorSet` and update the base range index for
- // subsequent binding operations to account for it.
- //
- if (m_constantBuffer)
- {
- auto bufferImpl = static_cast<BufferResourceImpl*>(m_constantBuffer);
- writeBufferDescriptor(
- bindingState,
- offset,
- VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
- bufferImpl,
- m_constantBufferOffset,
- m_constantBufferSize);
- offset.descriptorRangeOffset++;
- }
-
- return SLANG_OK;
- }
-
public:
- Result bindDescriptorRanges(
+
+ /// Bind this shader object as a "value"
+ ///
+ /// This is the mode used for binding sub-objects for existential-type
+ /// fields, and is also used as part of the implementation of the
+ /// parameter-block and constant-buffer cases.
+ ///
+ Result bindAsValue(
PipelineCommandEncoder* encoder,
- RootBindingState* bindingState,
- BindingOffset& offset)
+ RootBindingContext& context,
+ BindingOffset const& offset,
+ ShaderObjectLayoutImpl* specializedLayout)
{
- auto layout = getLayout();
-
- // Fill in the descriptor sets based on binding ranges
+ // We start by iterating over the "simple" (non-sub-object) binding
+ // ranges and writing them to the descriptor sets that are being
+ // passed down.
//
- for (auto bindingRangeInfo : layout->getBindingRanges())
+ for (auto bindingRangeInfo : specializedLayout->getBindingRanges())
{
- auto rangeIndex =
- bindingRangeInfo.rangeIndexInDescriptorSet + offset.descriptorRangeOffset;
+ BindingOffset rangeOffset = offset;
+
auto baseIndex = bindingRangeInfo.baseIndex;
auto count = (uint32_t)bindingRangeInfo.count;
switch (bindingRangeInfo.bindingType)
{
case slang::BindingType::ConstantBuffer:
- for (uint32_t i = 0; i < count; ++i)
- {
- ShaderObjectImpl* subObject = m_objects[baseIndex + i];
- subObject->bindObjectIntoConstantBuffer(encoder, bindingState, offset);
- }
- break;
case slang::BindingType::ParameterBlock:
- for (uint32_t i = 0; i < count; ++i)
- {
- ShaderObjectImpl* subObject = m_objects[baseIndex + i];
- auto newOffset = offset;
- subObject->bindObjectIntoParameterBlock(encoder, bindingState, newOffset);
- offset.pushConstantRangeOffset = newOffset.pushConstantRangeOffset;
- }
+ case slang::BindingType::ExistentialValue:
break;
+
case slang::BindingType::Texture:
+ rangeOffset.bindingSet += bindingRangeInfo.setOffset;
+ rangeOffset.binding += bindingRangeInfo.bindingOffset;
writeTextureDescriptor(
- bindingState,
- offset,
+ context,
+ rangeOffset,
VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
m_resourceViews.getArrayView(baseIndex, count));
- offset.descriptorRangeOffset++;
break;
case slang::BindingType::MutableTexture:
+ rangeOffset.bindingSet += bindingRangeInfo.setOffset;
+ rangeOffset.binding += bindingRangeInfo.bindingOffset;
writeTextureDescriptor(
- bindingState,
- offset,
+ context,
+ rangeOffset,
VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
m_resourceViews.getArrayView(baseIndex, count));
- offset.descriptorRangeOffset++;
break;
case slang::BindingType::CombinedTextureSampler:
+ rangeOffset.bindingSet += bindingRangeInfo.setOffset;
+ rangeOffset.binding += bindingRangeInfo.bindingOffset;
writeTextureSamplerDescriptor(
- bindingState,
- offset,
+ context,
+ rangeOffset,
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
m_combinedTextureSamplers.getArrayView(baseIndex, count));
- offset.descriptorRangeOffset++;
break;
case slang::BindingType::Sampler:
+ rangeOffset.bindingSet += bindingRangeInfo.setOffset;
+ rangeOffset.binding += bindingRangeInfo.bindingOffset;
writeSamplerDescriptor(
- bindingState,
- offset,
+ context,
+ rangeOffset,
VK_DESCRIPTOR_TYPE_SAMPLER,
m_samplers.getArrayView(baseIndex, count));
- offset.descriptorRangeOffset++;
break;
case slang::BindingType::RawBuffer:
case slang::BindingType::MutableRawBuffer:
+ rangeOffset.bindingSet += bindingRangeInfo.setOffset;
+ rangeOffset.binding += bindingRangeInfo.bindingOffset;
writePlainBufferDescriptor(
- bindingState,
- offset,
+ context,
+ rangeOffset,
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
m_resourceViews.getArrayView(baseIndex, count));
- offset.descriptorRangeOffset++;
break;
case slang::BindingType::TypedBuffer:
+ rangeOffset.bindingSet += bindingRangeInfo.setOffset;
+ rangeOffset.binding += bindingRangeInfo.bindingOffset;
writeTexelBufferDescriptor(
- bindingState,
- offset,
+ context,
+ rangeOffset,
VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER,
m_resourceViews.getArrayView(baseIndex, count));
- offset.descriptorRangeOffset++;
break;
case slang::BindingType::MutableTypedBuffer:
+ rangeOffset.bindingSet += bindingRangeInfo.setOffset;
+ rangeOffset.binding += bindingRangeInfo.bindingOffset;
writeTexelBufferDescriptor(
- bindingState,
- offset,
+ context,
+ rangeOffset,
VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER,
m_resourceViews.getArrayView(baseIndex, count));
- offset.descriptorRangeOffset++;
break;
case slang::BindingType::VaryingInput:
case slang::BindingType::VaryingOutput:
break;
+ default:
+ SLANG_ASSERT(!"unsupported binding type");
+ return SLANG_FAIL;
+ break;
+ }
+ }
+
+ // Once we've handled the simpel binding ranges, we move on to the
+ // sub-object ranges, which are generally more involved.
+ //
+ for( auto const& subObjectRange : specializedLayout->getSubObjectRanges() )
+ {
+ auto const& bindingRangeInfo = specializedLayout->getBindingRange(subObjectRange.bindingRangeIndex);
+ auto count = bindingRangeInfo.count;
+ auto baseIndex = bindingRangeInfo.baseIndex;
+
+ auto subObjectLayout = subObjectRange.layout;
+
+ // The starting offset to use for the sub-object
+ // has already been computed and stored as part
+ // of the layout, so we can get to the starting
+ // offset for the range easily.
+ //
+ BindingOffset rangeOffset = offset;
+ rangeOffset += subObjectRange.offset;
+
+ switch( bindingRangeInfo.bindingType )
+ {
+ case slang::BindingType::ConstantBuffer:
+ {
+ BindingOffset objOffset = rangeOffset;
+ for (uint32_t i = 0; i < count; ++i)
+ {
+ // Binding a constant buffer sub-object is simple enough:
+ // we just call `bindAsConstantBuffer` on it to bind
+ // the ordinary data buffer (if needed) and any other
+ // bindings it recursively contains.
+ //
+ ShaderObjectImpl* subObject = m_objects[baseIndex + i];
+ subObject->bindAsConstantBuffer(encoder, context, objOffset, subObjectLayout);
+
+ // When dealing with arrays of sub-objects, we need to make
+ // sure to increment the offset for each subsequent object
+ // by the appropriate stride.
+ //
+ // TODO: We should pre-compute these and simply have
+ // `subObjectRange.stride` to go with `subObjectRange.offset`.
+ //
+ objOffset.binding += subObjectLayout->getTotalBindingCount();
+ objOffset.childSet += subObjectLayout->getChildDescriptorSetCount();
+ objOffset.pushConstantRange += subObjectLayout->getTotalPushConstantRangeCount();
+ }
+ }
+ break;
+ case slang::BindingType::ParameterBlock:
+ {
+ BindingOffset objOffset = rangeOffset;
+ for (uint32_t i = 0; i < count; ++i)
+ {
+ // The case for `ParameterBlock<X>` is not that different
+ // from `ConstantBuffer<X>`, except that we call `bindAsParameterBlock`
+ // instead (understandably).
+ //
+ ShaderObjectImpl* subObject = m_objects[baseIndex + i];
+ subObject->bindAsParameterBlock(encoder, context, objOffset, subObjectLayout);
+
+ // The logic for striding from one sub-object to another is also
+ // different, given the differences in how constant buffers and
+ // parameter blocks are allocated.
+ //
+ objOffset.childSet += subObjectLayout->getChildDescriptorSetCount();
+ objOffset.pushConstantRange += subObjectLayout->getTotalPushConstantRangeCount();
+ }
+ }
+ break;
+
case slang::BindingType::ExistentialValue:
+ // Interface/existential-type sub-object ranges are the most complicated case.
//
- // TODO: If the existential value is one that "fits" into the storage available,
- // then we should write its data directly into that area. Otherwise, we need
- // to bind its content as "pending" data which will come after any other data
- // beloning to the same set (that is, it's starting descriptorRangeIndex will
- // need to be one after the number of ranges accounted for in the original type)
+ // First, we can only bind things if we have static specialization information
+ // to work with, which is exactly the case where `subObjectLayout` will be non-null.
//
+ if( subObjectLayout )
+ {
+ // Second, the offset where we want to start bindign for existential-type
+ // ranges is a bit different, because we don't wnat to bind at the "primary"
+ // offset that got passed down, but instead at the "pending" offset.
+ //
+ // For the purposes of nested binding, what used to be the pending offset
+ // will now be used as the primary offset.
+ //
+ BindingOffset objOffset = BindingOffset(rangeOffset.pending);
+ for (uint32_t i = 0; i < count; ++i)
+ {
+ // An existential-type sub-object is always bound just as a value,
+ // which handles its nested bindings and descriptor sets, but
+ // does not deal with ordianry data. The ordinary data should
+ // have been handled as part of the buffer for a parent object
+ // already.
+ //
+ ShaderObjectImpl* subObject = m_objects[baseIndex + i];
+ subObject->bindAsValue(encoder, context, objOffset, subObjectLayout);
+
+ objOffset.binding += subObjectLayout->getTotalBindingCount();
+ objOffset.childSet += subObjectLayout->getChildDescriptorSetCount();
+ objOffset.pushConstantRange += subObjectLayout->getTotalPushConstantRangeCount();
+ }
+ }
break;
default:
- SLANG_ASSERT(!"unsupported binding type");
+ SLANG_ASSERT(!"unsupported sub-object type");
return SLANG_FAIL;
break;
}
}
+
+ return SLANG_OK;
+ }
+
+ /// Allocate the descriptor sets needed for binding this object (but not nested parameter blocks)
+ Result allocateDescriptorSets(
+ PipelineCommandEncoder* encoder,
+ RootBindingContext& context,
+ BindingOffset const& offset,
+ ShaderObjectLayoutImpl* specializedLayout)
+ {
+ auto baseDescriptorSetIndex = offset.childSet;
+
+ // The number of sets to allocate and their layouts was already pre-computed
+ // as part of the shader object layout, so we use that information here.
+ //
+ for (auto descriptorSetInfo : specializedLayout->getOwnDescriptorSets())
+ {
+ auto descriptorSetHandle = context.descriptorSetAllocator->allocate(
+ descriptorSetInfo.descriptorSetLayout).handle;
+
+ // For each set, we need to write it into the set of descriptor sets
+ // being used for binding. This is done both so that other steps
+ // in binding can find the set to fill it in, but also so that
+ // we can bind all the descriptor sets to the pipeline when the
+ // time comes.
+ //
+ auto descriptorSetIndex = baseDescriptorSetIndex + descriptorSetInfo.space;
+ context.descriptorSets[descriptorSetIndex] = descriptorSetHandle;
+ }
+
return SLANG_OK;
}
- virtual Result bindObjectIntoConstantBuffer(
+ /// Bind this object as a `ParameterBlock<X>`.
+ Result bindAsParameterBlock(
PipelineCommandEncoder* encoder,
- RootBindingState* bindingState,
- BindingOffset& offset)
+ RootBindingContext& context,
+ BindingOffset const& inOffset,
+ ShaderObjectLayoutImpl* specializedLayout)
{
- SLANG_RETURN_ON_FAIL(_bindOrdinaryDataBufferIfNeeded(encoder, bindingState, offset));
+ // Because we are binding into a nested parameter block,
+ // any texture/buffer/sampler bindings will now want to
+ // write into the sets we allocate for this object and
+ // not the sets for any parent object(s).
+ //
+ BindingOffset offset = inOffset;
+ offset.bindingSet = offset.childSet;
+ offset.binding = 0;
+
+ // TODO: We should also be writing to `offset.pending` here,
+ // because any resource/sampler bindings related to "pending"
+ // data should *also* be writing into the chosen set.
+ //
+ // The challenge here is that we need to compute the right
+ // value for `offset.pending.binding`, so that it writes after
+ // all the other bindings.
+
+ // Writing the bindings for a parameter block is relatively easy:
+ // we just need to allocate the descriptor set(s) needed for this
+ // object and then fill it in like a `ConstantBuffer<X>`.
+ //
+ SLANG_RETURN_ON_FAIL(allocateDescriptorSets(encoder, context, offset, specializedLayout));
+ SLANG_RETURN_ON_FAIL(bindAsConstantBuffer(encoder, context, offset, specializedLayout));
- SLANG_RETURN_ON_FAIL(bindDescriptorRanges(encoder, bindingState, offset));
return SLANG_OK;
}
- virtual Result bindObjectIntoParameterBlock(
+ /// Bind the ordinary data buffer if needed, and adjust `ioOffset` accordingly
+ Result bindOrdinaryDataBufferIfNeeded(
PipelineCommandEncoder* encoder,
- RootBindingState* bindingState,
- BindingOffset& offset)
- {
- auto& descriptorSetInfos = getLayout()->getDescriptorSets();
- offset.descriptorSetIndexOffset = (uint32_t)bindingState->descriptorSets.getCount();
- offset.uniformOffset = 0;
- offset.descriptorRangeOffset = 0;
- for (auto info : descriptorSetInfos)
+ RootBindingContext& context,
+ BindingOffset& ioOffset,
+ ShaderObjectLayoutImpl* specializedLayout)
+ {
+ // We start by ensuring that the buffer is created, if it is needed.
+ //
+ SLANG_RETURN_ON_FAIL(_ensureOrdinaryDataBufferCreatedIfNeeded(encoder, specializedLayout));
+
+ // If we did indeed need/create a buffer, then we must bind it into
+ // the given `descriptorSet` and update the base range index for
+ // subsequent binding operations to account for it.
+ //
+ if (m_constantBuffer)
{
- bindingState->descriptorSets.add(
- bindingState->descriptorSetAllocator->allocate(info.descriptorSetLayout)
- .handle);
+ auto bufferImpl = static_cast<BufferResourceImpl*>(m_constantBuffer);
+ writeBufferDescriptor(
+ context,
+ ioOffset,
+ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+ bufferImpl,
+ m_constantBufferOffset,
+ m_constantBufferSize);
+ ioOffset.binding++;
}
- SLANG_RETURN_ON_FAIL(bindObjectIntoConstantBuffer(encoder, bindingState, offset));
+
+ return SLANG_OK;
+ }
+
+ /// Bind this object as a `ConstantBuffer<X>`.
+ Result bindAsConstantBuffer(
+ PipelineCommandEncoder* encoder,
+ RootBindingContext& context,
+ BindingOffset const& inOffset,
+ ShaderObjectLayoutImpl* specializedLayout)
+ {
+ // To bind an object as a constant buffer, we first
+ // need to bind its ordinary data (if any) into an
+ // ordinary data buffer, and then bind it as a "value"
+ // which handles any of its recursively-contained bindings.
+ //
+ // The one detail is taht when binding the ordinary data
+ // buffer we need to adjust the `binding` index used for
+ // subsequent operations based on whether or not an ordinary
+ // data buffer was used (and thus consumed a `binding`).
+ //
+ BindingOffset offset = inOffset;
+ SLANG_RETURN_ON_FAIL(bindOrdinaryDataBufferIfNeeded(encoder, context, /*inout*/ offset, specializedLayout));
+ SLANG_RETURN_ON_FAIL(bindAsValue(encoder, context, offset, specializedLayout));
return SLANG_OK;
}
@@ -2547,28 +3383,60 @@ public:
EntryPointLayout* getLayout() { return static_cast<EntryPointLayout*>(m_layout.Ptr()); }
- virtual Result bindObjectIntoConstantBuffer(
+ /// Bind this shader object as an entry point
+ Result bindAsEntryPoint(
PipelineCommandEncoder* encoder,
- RootBindingState* bindingState,
- BindingOffset& offset) override
+ RootBindingContext& context,
+ BindingOffset const& inOffset,
+ EntryPointLayout* layout)
{
- // Set data in `m_ordinaryData` into the push constant range.
+ BindingOffset offset = inOffset;
+
+ // Any ordinary data in an entry point is assumed to be allocated
+ // as a push-constant range.
+ //
+ // TODO: Can we make this operation not bake in that assumption?
+ //
+ // TODO: Can/should this function be renamed as just `bindAsPushConstantBuffer`?
+ //
if (m_ordinaryData.getCount())
{
- auto pushConstantRange =
- bindingState->pushConstantRanges[offset.pushConstantRangeOffset];
+ // The index of the push constant range to bind should be
+ // passed down as part of the `offset`, and we will increment
+ // it here so that any further recursively-contained push-constant
+ // ranges use the next index.
+ //
+ auto pushConstantRangeIndex = offset.pushConstantRange++;
+
+ // Information about the push constant ranges (including offsets
+ // and stage flags) was pre-computed for the entire program and
+ // stored on the binding context.
+ //
+ auto const& pushConstantRange = context.pushConstantRanges[pushConstantRangeIndex];
+
+ // We expect that the size of the range as reflected matches the
+ // amount of ordinary data stored on this object.
+ //
+ // TODO: This would not be the case if specialization for interface-type
+ // parameters led to the entry point having "pending" ordinary data.
+ //
+ SLANG_ASSERT(pushConstantRange.size == (uint32_t) m_ordinaryData.getCount());
+
+ auto pushConstantData = m_ordinaryData.getBuffer();
+
encoder->m_api->vkCmdPushConstants(
encoder->m_commandBuffer->m_commandBuffer,
- bindingState->pipelineLayout,
+ context.pipelineLayout,
pushConstantRange.stageFlags,
pushConstantRange.offset,
pushConstantRange.size,
- m_ordinaryData.getBuffer());
- offset.pushConstantRangeOffset++;
+ pushConstantData);
}
- // Process the rest of binding ranges.
- SLANG_RETURN_ON_FAIL(bindDescriptorRanges(encoder, bindingState, offset));
+ // Any remaining bindings in the object can be handled through the
+ // "value" case.
+ //
+ SLANG_RETURN_ON_FAIL(bindAsValue(encoder, context, offset, layout));
return SLANG_OK;
}
@@ -2614,18 +3482,50 @@ public:
return SLANG_OK;
}
- virtual Result bindObjectIntoParameterBlock(
- PipelineCommandEncoder* encoder,
- RootBindingState* bindingState,
- BindingOffset& offset) override
+ /// Bind this object as a root shader object
+ Result bindAsRoot(
+ PipelineCommandEncoder* encoder,
+ RootBindingContext& context,
+ RootShaderObjectLayout* layout)
{
- SLANG_RETURN_ON_FAIL(Super::bindObjectIntoParameterBlock(encoder, bindingState, offset));
+ BindingOffset offset = {};
+ offset.pending = layout->getPendingDataOffset();
- // Bind all entry points.
- for (auto& entryPoint : m_entryPoints)
+ // Note: the operations here are quite similar to what `bindAsParameterBlock` does.
+ // The key difference in practice is that we do *not* make use of the adjustment
+ // that `bindOrdinaryDataBufferIfNeeded` applied to the offset passed into it.
+ //
+ // The reason for this difference in behavior is that the layout information
+ // for root shader parameters is in practice *already* offset appropriately
+ // (so that it ends up using absolute offsets).
+ //
+ // TODO: One more wrinkle here is that the `ordinaryDataBufferOffset` below
+ // might not be correct if `binding=0,set=0` was already claimed via explicit
+ // binding information. We should really be getting the offset information for
+ // the ordinary data buffer directly from the reflection information for
+ // the global scope.
+
+ SLANG_RETURN_ON_FAIL(allocateDescriptorSets(encoder, context, offset, layout));
+
+ BindingOffset ordinaryDataBufferOffset = offset;
+ SLANG_RETURN_ON_FAIL(bindOrdinaryDataBufferIfNeeded(encoder, context, /*inout*/ ordinaryDataBufferOffset, layout));
+
+ SLANG_RETURN_ON_FAIL(bindAsValue(encoder, context, offset, layout));
+
+ auto entryPointCount = layout->getEntryPoints().getCount();
+ for( Index i = 0; i < entryPointCount; ++i )
{
- entryPoint->bindObjectIntoConstantBuffer(encoder, bindingState, offset);
+ auto entryPoint = m_entryPoints[i];
+ auto const& entryPointInfo = layout->getEntryPoint(i);
+
+ // Note: we do *not* need to add the entry point offset
+ // information to the global `offset` because the
+ // `RootShaderObjectLayout` has already baked any offsets
+ // from the global layout into the `entryPointInfo`.
+
+ entryPoint->bindAsEntryPoint(encoder, context, entryPointInfo.offset, entryPointInfo.layout);
}
+
return SLANG_OK;
}
@@ -3873,33 +4773,50 @@ Result VKDevice::PipelineCommandEncoder::bindRootShaderObjectImpl(
if (!specializedLayout)
return SLANG_FAIL;
- RootBindingState bindState = {};
- bindState.pushConstantRanges = specializedLayout->m_pushConstantRanges.getView();
- bindState.pipelineLayout = specializedLayout->m_pipelineLayout;
- bindState.device = m_device;
- bindState.descriptorSetAllocator = &m_commandBuffer->m_transientHeap->m_descSetAllocator;
-
- // Write bindings into descriptor sets. This step allocate descriptor sets and collects
- // all `VkWriteDescriptorSet` operations in `bindState.descriptorSetWrites`.
- BindingOffset offset = {};
- rootObjectImpl->bindObjectIntoParameterBlock(this, &bindState, offset);
-
- // Execute descriptor writes collected in `bindState.descriptorSetWrites`.
- m_device->m_api.vkUpdateDescriptorSets(
- m_device->m_device,
- (uint32_t)bindState.descriptorSetWrites.getCount(),
- bindState.descriptorSetWrites.getArrayView().arrayView.getBuffer(),
- 0,
- nullptr);
+ // We will set up the context required when binding shader objects
+ // to the pipeline. Note that this is mostly just being packaged
+ // together to minimize the number of parameters that have to
+ // be dealt with in the complex recursive call chains.
+ //
+ RootBindingContext context;
+ context.pipelineLayout = specializedLayout->m_pipelineLayout;
+ context.device = m_device;
+ context.descriptorSetAllocator = &m_commandBuffer->m_transientHeap->m_descSetAllocator;
+ context.pushConstantRanges = specializedLayout->getAllPushConstantRanges().getArrayView();
+
+ // The context includes storage for the descriptor sets we will bind,
+ // and the number of sets we need to make space for is determined
+ // by the specialized program layout.
+ //
+ List<VkDescriptorSet> descriptorSetsStorage;
+ auto descriptorSetCount = specializedLayout->getTotalDescriptorSetCount();
+
+ descriptorSetsStorage.setCount(descriptorSetCount);
+ auto descriptorSets = descriptorSetsStorage.getBuffer();
+
+ context.descriptorSets = descriptorSets;
+
+ // We kick off recursive binding of shader objects to the pipeline (plus
+ // the state in `context`).
+ //
+ // Note: this logic will directly write any push-constant ranges needed,
+ // and will also fill in any descriptor sets. Currently it does not
+ // *bind* the descriptor sets it fills in.
+ //
+ // TODO: It could probably bind the descriptor sets as well.
+ //
+ rootObjectImpl->bindAsRoot(this, context, specializedLayout);
- // Bind descriptor sets.
+ // Once we've filled in all the descriptor sets, we bind them
+ // to the pipeline at once.
+ //
m_device->m_api.vkCmdBindDescriptorSets(
m_commandBuffer->m_commandBuffer,
bindPoint,
specializedLayout->m_pipelineLayout,
0,
- (uint32_t)bindState.descriptorSets.getCount(),
- bindState.descriptorSets.getBuffer(),
+ (uint32_t) descriptorSetCount,
+ descriptorSets,
0,
nullptr);
@@ -5540,7 +6457,7 @@ Result VKDevice::createShaderObjectLayout(
{
RefPtr<ShaderObjectLayoutImpl> layout;
SLANG_RETURN_ON_FAIL(
- ShaderObjectLayoutImpl::createForElementType(this, typeLayout, true, layout.writeRef()));
+ ShaderObjectLayoutImpl::createForElementType(this, typeLayout, layout.writeRef()));
returnRefPtrMove(outLayout, layout);
return SLANG_OK;
}