summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--source/slang/slang-reflection-api.cpp304
-rw-r--r--tools/gfx/cuda/render-cuda.cpp77
-rw-r--r--tools/gfx/render-graphics-common.cpp2
3 files changed, 246 insertions, 137 deletions
diff --git a/source/slang/slang-reflection-api.cpp b/source/slang/slang-reflection-api.cpp
index dcfed07b0..1cfb8fecc 100644
--- a/source/slang/slang-reflection-api.cpp
+++ b/source/slang/slang-reflection-api.cpp
@@ -1172,6 +1172,35 @@ namespace Slang
}
}
+ SlangBindingType _calcBindingType(
+ LayoutResourceKind kind)
+ {
+ switch( kind )
+ {
+ default:
+ return SLANG_BINDING_TYPE_UNKNOWN;
+
+ // Some cases of `LayoutResourceKind` can be mapped
+ // directly to a `BindingType` because there is only
+ // one case of types that have that resource kind.
+
+ #define CASE(FROM, TO) \
+ case LayoutResourceKind::FROM: return SLANG_BINDING_TYPE_##TO
+
+ CASE(ConstantBuffer, CONSTANT_BUFFER);
+ CASE(SamplerState, SAMPLER);
+ CASE(VaryingInput, VARYING_INPUT);
+ CASE(VaryingOutput, VARYING_OUTPUT);
+ CASE(ExistentialObjectParam, EXISTENTIAL_VALUE);
+ CASE(PushConstantBuffer, PUSH_CONSTANT);
+ CASE(Uniform, INLINE_UNIFORM_DATA);
+ // TODO: register space
+
+ #undef CASE
+ }
+ }
+
+
SlangBindingType _calcBindingType(
Slang::TypeLayout* typeLayout,
@@ -1193,29 +1222,7 @@ namespace Slang
// multiple different kinds of binding, depending on where/how
// it is used (e.g., as a varying parameter, a root constant, etc.).
//
- switch( kind )
- {
- default:
- return SLANG_BINDING_TYPE_UNKNOWN;
-
- // Some cases of `LayoutResourceKind` can be mapped
- // directly to a `BindingType` because there is only
- // one case of types that have that resource kind.
-
- #define CASE(FROM, TO) \
- case LayoutResourceKind::FROM: return SLANG_BINDING_TYPE_##TO
-
- CASE(ConstantBuffer, CONSTANT_BUFFER);
- CASE(SamplerState, SAMPLER);
- CASE(VaryingInput, VARYING_INPUT);
- CASE(VaryingOutput, VARYING_OUTPUT);
- CASE(ExistentialObjectParam, EXISTENTIAL_VALUE);
- CASE(PushConstantBuffer, PUSH_CONSTANT);
- CASE(Uniform, INLINE_UNIFORM_DATA);
- // TODO: register space
-
- #undef CASE
- }
+ return _calcBindingType(kind);
}
static DeclRefType* asInterfaceType(Type* type)
@@ -1538,147 +1545,186 @@ namespace Slang
}
else if(asInterfaceType(typeLayout->type))
{
- // An `interface` type should introduce a sub-object range,
- // with no concrete descriptor ranges to store its value
- // (since we don't know until runtime what type of
- // value will be plugged in).
+ // 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.
//
-
- LayoutResourceKind kind = LayoutResourceKind::ExistentialObjectParam;
- auto count = multiplier;
- auto spaceOffset = _calcSpaceOffset(path, kind);
-
- Int descriptorSetIndex = _findOrAddDescriptorSet(spaceOffset);
- auto descriptorSet = m_extendedInfo->m_descriptorSets[descriptorSetIndex];
-
TypeLayout::ExtendedInfo::BindingRangeInfo bindingRange;
bindingRange.leafTypeLayout = typeLayout;
bindingRange.bindingType = SLANG_BINDING_TYPE_EXISTENTIAL_VALUE;
bindingRange.count = multiplier;
- bindingRange.descriptorSetIndex = descriptorSetIndex;
- bindingRange.firstDescriptorRangeIndex = descriptorSet->descriptorRanges.getCount();
- bindingRange.descriptorRangeCount = 1;
+ bindingRange.descriptorSetIndex = 0;
+ bindingRange.descriptorRangeCount = 0;
+ bindingRange.firstDescriptorRangeIndex = 0;
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.
+
m_extendedInfo->m_bindingRanges.add(bindingRange);
m_extendedInfo->m_subObjectRanges.add(subObjectRange);
}
- // TODO: We need to handle `interface` types here, because they are
- // another case that introduces a "sub-object" for the purposes of
- // application-side allocation.
- //
- // TODO: There are a few cases of "leaf" fields that might
- // still result in multiple descriptors (or at least multiple
- // `LayoutResourceKind`s) depending on the target.
- //
- // For eample, combined texture-sampler types should be treated
- // as "leaf" fields for this code (since a portable engine would
- // need to abstract over them), but would map to two descriptors
- // on targets that don't actually support combining them.
else
{
- Int resourceKindCount = typeLayout->resourceInfos.getCount();
- if(resourceKindCount == 0)
+ // Here we have the catch-all case that handles "leaf" fields
+ // that should never introduce a sub-object range, but might
+ // need to introduce a binding range and descriptor ranges.
+ //
+ // First, we want to determine what type of binding this
+ // leaf field should map to, if any. We being by querying
+ // the type itself, since there are many distinct descriptor
+ // types for textures/buffers that can only be determined
+ // by type, rather than by a `LayoutResourceKind`.
+ //
+ auto bindingType = _calcResourceBindingType(typeLayout);
+
+ // It is possible that the type alone isn't enough to tell
+ // us a specific binding type, at which point we need to
+ // start looking at the actual resources the type layout
+ // consumes.
+ //
+ if(bindingType == SLANG_BINDING_TYPE_UNKNOWN)
{
- // This is a field that consumes no resources, and as
- // such does not need a binding or descriptor ranges
- // allocated for it.
+ // We will search through all the resource kinds that
+ // the type layout consumes, to see if we can find
+ // one that indicates a binding type we actually
+ // want to reflect.
//
- return;
- }
- else
- {
- // `resourceKindCount` should be 1 in most cases.
- // However if this field is a buffer of existential type,
- // the resourceInfo array will contain additional ExistentialTypeParam
- // and ExistentialObjectParam entries. These entries doesn't affect the
- // logic here, so we only need to care about the first entry.
- auto& resInfo = typeLayout->resourceInfos[0];
- LayoutResourceKind kind = resInfo.kind;
-
- auto bindingType = _calcBindingType(typeLayout, kind);
- if(bindingType == SLANG_BINDING_TYPE_INLINE_UNIFORM_DATA)
+ for( auto resInfo : typeLayout->resourceInfos )
{
- // We do not consider uniform resource usage
- // in the ranges we compute.
+ auto kind = resInfo.kind;
+ if(kind == LayoutResourceKind::Uniform)
+ continue;
+
+ auto kindBindingType = _calcBindingType(kind);
+ if(kindBindingType == SLANG_BINDING_TYPE_UNKNOWN)
+ continue;
+
+ // If we find a relevant binding type based on
+ // one of the resource kinds that are consumed,
+ // then we immediately stop the search and use
+ // the first one found (whether or not later
+ // entries might also provide something relevant).
//
- // TODO: We may need to revise that rule for types that
- // represent resources, even when one or more targets
- // map those resource types to ordinary/uniform data.
- //
- return;
+ bindingType = kindBindingType;
+ break;
}
+ }
- // This leaf field will map to a single binding range and,
- // if it is appropriate, a single descriptor range.
- //
- auto count = resInfo.count * multiplier;
- auto indexOffset = _calcIndexOffset(path, kind);
- auto spaceOffset = _calcSpaceOffset(path, kind);
+ // After we've tried to determine a binding type, if
+ // we have nothing to go on then we don't want to add
+ // a binding range.
+ //
+ if(bindingType == SLANG_BINDING_TYPE_UNKNOWN)
+ return;
+
+ // We now know that the leaf field will map to a single binding range,
+ // and zero or more descriptor ranges.
+ //
+ TypeLayout::ExtendedInfo::BindingRangeInfo bindingRange;
+ bindingRange.leafTypeLayout = typeLayout;
+ bindingRange.bindingType = bindingType;
+ bindingRange.count = multiplier;
+ bindingRange.descriptorSetIndex = 0;
+ bindingRange.firstDescriptorRangeIndex = 0;
+ bindingRange.descriptorRangeCount = 0;
- Int descriptorSetIndex = -1;
- Int firstDescriptorIndex = 0;
- RefPtr<TypeLayout::ExtendedInfo::DescriptorSetInfo> descriptorSet;
+ // We will associate the binding range with a specific descriptor
+ // set on demand *if* we discover that it shold contain any
+ // descriptor ranges.
+ //
+ RefPtr<TypeLayout::ExtendedInfo::DescriptorSetInfo> descriptorSet;
+
+
+ // We will add a descriptor range for each relevant resource kind
+ // that the type layout consumes.
+ //
+ for(auto resInfo : typeLayout->resourceInfos)
+ {
+ auto kind = resInfo.kind;
switch( kind )
{
+ default:
+ break;
+
+
+ // There are many resource kinds that we do not want
+ // to expose as descriptor ranges simply because they
+ // do not actually allocate descriptors on our target
+ // APIs.
+ //
+ // Notably included here are uniform/ordinary data and
+ // varying input/output (including the ray-tracing cases).
+ //
+ // It is worth noting that we *do* allow root/push-constant
+ // ranges to be reflected as "descriptor" ranges here,
+ // despite the fact that they are not descriptor-bound
+ // under D3D12/Vulkan.
+ //
+ // In practice, even with us filtering out some cases here,
+ // an application/renderer layer will need to filter/translate
+ // or descriptor ranges into API-specific ones, and a one-to-one
+ // mapping should not be assumed.
+ //
+ // TODO: Make some clear decisions about what should and should
+ // not appear here.
+ //
+ case LayoutResourceKind::Uniform:
case LayoutResourceKind::RegisterSpace:
case LayoutResourceKind::VaryingInput:
case LayoutResourceKind::VaryingOutput:
case LayoutResourceKind::HitAttributes:
case LayoutResourceKind::RayPayload:
- // Resource kinds that represent "varying" input/output
- // do not manifest as entries in API descriptor tables.
- //
- // TODO: Neither do root constants, if we are being
- // precise. This API really needs to carefully match
- // the semantics of the target platform/API in terms
- // of what things are descriptor-bound and which are
- // not, so that a user can easily allocate the platform-specific
- // descriptor sets using this info.
- //
- // (That said, we are purposefully *not* breaking apart
- // samplers and SRV/UAV/CBV stuff for our D3D reflection
- // of descriptor sets. It seems like the policy here
- // really requires careful thought)
- //
- // TODO: Maybe the best answer is to leave decomposition
- // of stuff into descriptor sets up to the application
- // layer? This is especially true if a common case would
- // be an application that doesn't support arbitrary manual
- // binding of parameters to register/spaces.
- //
- break;
+ continue;
+ }
- default:
- {
- TypeLayout::ExtendedInfo::DescriptorRangeInfo descriptorRange;
- descriptorRange.kind = kind;
- descriptorRange.bindingType = bindingType;
- descriptorRange.count = count;
- descriptorRange.indexOffset = indexOffset;
+ // We will prefer to use a binding type derived from the specific
+ // resource kind, but will fall back to information from the
+ // type layout when that is not available.
+ //
+ // TODO: This logic probably needs a bit more work to handle
+ // the case of a combined texture-sampler field that is being
+ // compiled for an API with separate textures and samplers.
+ //
+ auto kindBindingType = _calcBindingType(kind);
+ if( kindBindingType == SLANG_BINDING_TYPE_UNKNOWN )
+ {
+ kindBindingType = bindingType;
+ }
- descriptorSetIndex = _findOrAddDescriptorSet(spaceOffset);
- descriptorSet = m_extendedInfo->m_descriptorSets[descriptorSetIndex];
+ // We now expect to allocate a descriptor range for this
+ // `resInfo` representing resouce usage.
+ //
+ auto count = resInfo.count * multiplier;
+ auto indexOffset = _calcIndexOffset(path, kind);
+ auto spaceOffset = _calcSpaceOffset(path, kind);
- firstDescriptorIndex = descriptorSet->descriptorRanges.getCount();
- descriptorSet->descriptorRanges.add(descriptorRange);
- }
- break;
- }
+ TypeLayout::ExtendedInfo::DescriptorRangeInfo descriptorRange;
+ descriptorRange.kind = kind;
+ descriptorRange.bindingType = kindBindingType;
+ descriptorRange.count = count;
+ descriptorRange.indexOffset = indexOffset;
+ if(!descriptorSet)
+ {
+ Int descriptorSetIndex = _findOrAddDescriptorSet(spaceOffset);
+ descriptorSet = m_extendedInfo->m_descriptorSets[descriptorSetIndex];
- TypeLayout::ExtendedInfo::BindingRangeInfo bindingRange;
- bindingRange.leafTypeLayout = typeLayout;
- bindingRange.bindingType = _calcBindingType(typeLayout, kind);
- bindingRange.count = count;
- bindingRange.descriptorSetIndex = descriptorSetIndex;
- bindingRange.firstDescriptorRangeIndex = firstDescriptorIndex;
- bindingRange.descriptorRangeCount = 1;
+ bindingRange.descriptorSetIndex = descriptorSetIndex;
+ bindingRange.firstDescriptorRangeIndex = descriptorSet->descriptorRanges.getCount();
+ }
- m_extendedInfo->m_bindingRanges.add(bindingRange);
+ descriptorSet->descriptorRanges.add(descriptorRange);
+ bindingRange.descriptorRangeCount++;
}
+
+ m_extendedInfo->m_bindingRanges.add(bindingRange);
}
}
};
diff --git a/tools/gfx/cuda/render-cuda.cpp b/tools/gfx/cuda/render-cuda.cpp
index 4f87bdfc9..906d553cb 100644
--- a/tools/gfx/cuda/render-cuda.cpp
+++ b/tools/gfx/cuda/render-cuda.cpp
@@ -250,6 +250,20 @@ public:
slang::BindingType bindingType;
Index count;
Index baseIndex; // Flat index for sub-ojects
+
+ // TODO: The `uniformOffset` field should be removed,
+ // since it cannot be supported by the Slang reflection
+ // API once we fix some design issues.
+ //
+ // It is only being used today for pre-allocation of sub-objects
+ // for constant buffers and parameter blocks (which should be
+ // deprecated/removed anyway).
+ //
+ // Note: We would need to bring this field back, plus
+ // a lot of other complexity, if we ever want to support
+ // setting of resources/buffers directly by a binding
+ // range index and array index.
+ //
Index uniformOffset; // Uniform offset for a resource typed field.
};
@@ -310,6 +324,14 @@ public:
SlangInt rangeIndexInDescriptorSet =
m_elementTypeLayout->getBindingRangeFirstDescriptorRangeIndex(r);
+ // TODO: This logic assumes that for any binding range that might consume
+ // multiple kinds of resources, the descriptor range for its uniform
+ // usage will be the first one in the range.
+ //
+ // We need to decide whether that assumption is one we intend to support
+ // applications making, or whether they should be forced to perform a
+ // linear search over the descriptor ranges for a specific binding range.
+ //
auto uniformOffset = m_elementTypeLayout->getDescriptorSetDescriptorRangeIndexOffset(
descriptorSetIndex, rangeIndexInDescriptorSet);
@@ -464,6 +486,17 @@ public:
return SLANG_OK;
}
virtual SLANG_NO_THROW Result SLANG_MCALL
+ setDeviceData(size_t offset, void* data, size_t size)
+ {
+ size = Math::Min(size, bufferResource->getDesc()->sizeInBytes - offset);
+ SLANG_CUDA_RETURN_ON_FAIL(cudaMemcpy(
+ (uint8_t*)bufferResource->m_cudaMemory + offset,
+ data,
+ size,
+ cudaMemcpyHostToDevice));
+ return SLANG_OK;
+ }
+ virtual SLANG_NO_THROW Result SLANG_MCALL
getObject(ShaderOffset const& offset, IShaderObject** object)
{
auto subObjectIndex =
@@ -480,17 +513,34 @@ public:
virtual SLANG_NO_THROW Result SLANG_MCALL
setObject(ShaderOffset const& offset, IShaderObject* object)
{
- auto subObjectIndex =
- getLayout()->m_bindingRanges[offset.bindingRangeIndex].baseIndex + offset.bindingArrayIndex;
- SLANG_ASSERT(
- offset.uniformOffset ==
- getLayout()->m_bindingRanges[offset.bindingRangeIndex].uniformOffset +
- offset.bindingArrayIndex * sizeof(void*));
+ auto layout = getLayout();
+ SLANG_ASSERT(offset.bindingRangeIndex >= 0);
+ SLANG_ASSERT(offset.bindingRangeIndex < layout->m_bindingRanges.getCount());
+ auto& bindingRange = layout->m_bindingRanges[offset.bindingRangeIndex];
+
+ auto subObjectIndex = bindingRange.baseIndex + offset.bindingArrayIndex;
auto cudaObject = dynamic_cast<CUDAShaderObject*>(object);
if (subObjectIndex >= objects.getCount())
objects.setCount(subObjectIndex + 1);
objects[subObjectIndex] = cudaObject;
- return setData(offset, &cudaObject->bufferResource->m_cudaMemory, sizeof(void*));
+
+ switch( bindingRange.bindingType )
+ {
+ default:
+ SLANG_RETURN_ON_FAIL(setData(offset, &cudaObject->bufferResource->m_cudaMemory, sizeof(void*)));
+ break;
+
+ case slang::BindingType::ExistentialValue:
+ // TODO: handle the "does it fit" logic
+ {
+ auto valueSize = cudaObject->m_layout->getElementTypeLayout()->getSize();
+ auto valueOffset = 16;
+ SLANG_RETURN_ON_FAIL(setDeviceData(offset.uniformOffset + valueOffset, cudaObject->getBuffer(), valueSize));
+ }
+ break;
+ }
+
+ return SLANG_OK;
}
virtual SLANG_NO_THROW Result SLANG_MCALL
setResource(ShaderOffset const& offset, IResourceView* resourceView)
@@ -615,6 +665,19 @@ public:
return SLANG_OK;
}
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ setDeviceData(size_t offset, void* data, size_t size)
+ {
+ size = Math::Min(size, uniformBufferSize - offset);
+ SLANG_CUDA_RETURN_ON_FAIL(cudaMemcpy(
+ (uint8_t*)hostBuffer + offset,
+ data,
+ size,
+ cudaMemcpyDeviceToHost));
+ return SLANG_OK;
+ }
+
+
virtual SLANG_NO_THROW void* SLANG_MCALL getBuffer() override
{
return hostBuffer;
diff --git a/tools/gfx/render-graphics-common.cpp b/tools/gfx/render-graphics-common.cpp
index 5f083538d..9e95df95c 100644
--- a/tools/gfx/render-graphics-common.cpp
+++ b/tools/gfx/render-graphics-common.cpp
@@ -153,6 +153,7 @@ public:
switch (slangBindingType)
{
case slang::BindingType::ExistentialValue:
+ case slang::BindingType::InlineUniformData:
continue;
default:
break;
@@ -172,7 +173,6 @@ public:
auto category = typeLayout->getDescriptorSetDescriptorRangeCategory(s, r);
descriptorRangeDesc.binding += varLayout->getOffset(category);
}
-
descriptorSetInfo->slotRangeDescs.add(descriptorRangeDesc);
}
}