summaryrefslogtreecommitdiffstats
path: root/tools/gfx/d3d12/render-d3d12.cpp
diff options
context:
space:
mode:
authorYong He <yonghe@outlook.com>2021-05-21 16:38:33 -0700
committerGitHub <noreply@github.com>2021-05-21 16:38:33 -0700
commit7f8a9994d0bd99a171a1daa0bce46d92c02ccffd (patch)
tree0b187e63ab5b9ce6f5ab41266fedaec44091a217 /tools/gfx/d3d12/render-d3d12.cpp
parent172538fdb418f7a2faab1f5a410f3b2cb8e18ba5 (diff)
[gfx] Support StructuredBuffer<IInterface>. (#1851)
Co-authored-by: T. Foley <tfoleyNV@users.noreply.github.com>
Diffstat (limited to 'tools/gfx/d3d12/render-d3d12.cpp')
-rw-r--r--tools/gfx/d3d12/render-d3d12.cpp293
1 files changed, 55 insertions, 238 deletions
diff --git a/tools/gfx/d3d12/render-d3d12.cpp b/tools/gfx/d3d12/render-d3d12.cpp
index da169c03b..26b774b1a 100644
--- a/tools/gfx/d3d12/render-d3d12.cpp
+++ b/tools/gfx/d3d12/render-d3d12.cpp
@@ -232,17 +232,9 @@ public:
}
};
- class ResourceViewImpl : public IResourceView, public ComObject
+ class ResourceViewImpl : public ResourceViewBase
{
public:
- SLANG_COM_OBJECT_IUNKNOWN_ALL
- IResourceView* getInterface(const Guid& guid)
- {
- if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_IResourceView)
- return static_cast<IResourceView*>(this);
- return nullptr;
- }
- public:
RefPtr<Resource> m_resource;
D3D12Descriptor m_descriptor;
RefPtr<D3D12GeneralDescriptorHeap> m_allocator;
@@ -718,7 +710,11 @@ public:
uint32_t count;
/// A "flat" index for this range in whatever array provides backing storage for it
- uint32_t flatIndex;
+ uint32_t baseIndex;
+
+ /// An index into the sub-object array if this binding range is treated
+ /// as a sub-object.
+ uint32_t subObjectIndex;
};
/// Offset information for a sub-object range
@@ -802,12 +798,17 @@ public:
/// The number of root parameter consumed by (transitive) sub-objects
uint32_t m_childRootParameterCount = 0;
- /// The total size in bytes of the ordinary data for this object and transitive sub-objects
+ /// The total size in bytes of the ordinary data for this object and transitive sub-object.
uint32_t m_totalOrdinaryDataSize = 0;
+ /// The container type of this shader object. When `m_containerType` is
+ /// `StructuredBuffer` or `UnsizedArray`, this shader object represents a collection
+ /// instead of a single object.
+ ShaderObjectContainerType m_containerType = ShaderObjectContainerType::None;
+
Result setElementTypeLayout(slang::TypeLayoutReflection* typeLayout)
{
- typeLayout = _unwrapParameterGroups(typeLayout);
+ typeLayout = _unwrapParameterGroups(typeLayout, m_containerType);
m_elementTypeLayout = typeLayout;
// If the type contains any ordinary data, then we must reserve a buffer
@@ -849,12 +850,24 @@ public:
case slang::BindingType::ConstantBuffer:
case slang::BindingType::ParameterBlock:
case slang::BindingType::ExistentialValue:
- bindingRangeInfo.flatIndex = m_subObjectCount;
+ bindingRangeInfo.baseIndex = m_subObjectCount;
+ bindingRangeInfo.subObjectIndex = m_subObjectCount;
m_subObjectCount += count;
break;
-
+ case slang::BindingType::RawBuffer:
+ case slang::BindingType::MutableRawBuffer:
+ if (slangLeafTypeLayout->getType()->getElementType() != nullptr)
+ {
+ // A structured buffer occupies both a resource slot and
+ // a sub-object slot.
+ bindingRangeInfo.subObjectIndex = m_subObjectCount;
+ m_subObjectCount += count;
+ }
+ bindingRangeInfo.baseIndex = m_ownCounts.resource;
+ m_ownCounts.resource += count;
+ break;
case slang::BindingType::Sampler:
- bindingRangeInfo.flatIndex = m_ownCounts.sampler;
+ bindingRangeInfo.baseIndex = m_ownCounts.sampler;
m_ownCounts.sampler += count;
break;
@@ -867,7 +880,7 @@ public:
break;
default:
- bindingRangeInfo.flatIndex = m_ownCounts.resource;
+ bindingRangeInfo.baseIndex = m_ownCounts.resource;
m_ownCounts.resource += count;
break;
}
@@ -1092,8 +1105,6 @@ public:
BindingRangeInfo const& getBindingRange(Index index) { return m_bindingRanges[index]; }
- slang::TypeLayoutReflection* getElementTypeLayout() { return m_elementTypeLayout; }
-
uint32_t getResourceSlotCount() { return m_ownCounts.resource; }
uint32_t getSamplerSlotCount() { return m_ownCounts.sampler; }
Index getSubObjectSlotCount() { return m_subObjectCount; }
@@ -1128,6 +1139,8 @@ public:
initBase(renderer, builder->m_elementTypeLayout);
+ m_containerType = builder->m_containerType;
+
m_bindingRanges = _Move(builder->m_bindingRanges);
m_subObjectRanges = builder->m_subObjectRanges;
@@ -2033,7 +2046,11 @@ public:
RefPtr<RootShaderObjectLayoutImpl> m_rootObjectLayout;
};
- class ShaderObjectImpl : public ShaderObjectBase
+ class ShaderObjectImpl
+ : public ShaderObjectBaseImpl<
+ ShaderObjectImpl,
+ ShaderObjectLayoutImpl,
+ SimpleShaderObjectData>
{
public:
static Result create(
@@ -2064,25 +2081,14 @@ public:
return SLANG_OK;
}
- ShaderObjectLayoutImpl* getLayout()
- {
- return static_cast<ShaderObjectLayoutImpl*>(m_layout.Ptr());
- }
-
- SLANG_NO_THROW slang::TypeLayoutReflection* SLANG_MCALL getElementTypeLayout()
- SLANG_OVERRIDE
- {
- return m_layout->getElementTypeLayout();
- }
-
SLANG_NO_THROW Result SLANG_MCALL
setData(ShaderOffset const& inOffset, void const* data, size_t inSize) SLANG_OVERRIDE
{
Index offset = inOffset.uniformOffset;
Index size = inSize;
- char* dest = m_ordinaryData.getBuffer();
- Index availableSize = m_ordinaryData.getCount();
+ char* dest = m_data.getBuffer();
+ Index availableSize = m_data.getCount();
// TODO: We really should bounds-check access rather than silently ignoring sets
// that are too large, but we have several test cases that set more data than
@@ -2103,133 +2109,6 @@ public:
return SLANG_OK;
}
- virtual SLANG_NO_THROW Result SLANG_MCALL
- setObject(ShaderOffset const& offset, IShaderObject* object) 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 subObject = static_cast<ShaderObjectImpl*>(object);
-
- auto bindingRangeIndex = offset.bindingRangeIndex;
- auto& bindingRange = layout->getBindingRange(bindingRangeIndex);
-
- 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.
- // We may also need to assign some data from the sub-object into the ordinary data
- // buffer for the parent object.
- //
- if (bindingRange.bindingType == slang::BindingType::ExistentialValue)
- {
- // A leaf field of interface type is laid out inside of the parent object
- // as a tuple of `(RTTI, WitnessTable, Payload)`. The layout of these fields
- // is a contract between the compiler and any runtime system, so we will
- // need to rely on details of the binary layout.
-
- // We start by querying the layout/type of the concrete value that the application
- // is trying to store into the field, and also the layout/type of the leaf
- // existential-type field itself.
- //
- auto concreteTypeLayout = subObject->getElementTypeLayout();
- auto concreteType = concreteTypeLayout->getType();
- //
- auto existentialTypeLayout =
- layout->getElementTypeLayout()->getBindingRangeLeafTypeLayout(
- bindingRangeIndex);
- auto existentialType = existentialTypeLayout->getType();
-
- // The first field of the tuple (offset zero) is the run-time type information
- // (RTTI) ID for the concrete type being stored into the field.
- //
- // TODO: We need to be able to gather the RTTI type ID from `object` and then
- // use `setData(offset, &TypeID, sizeof(TypeID))`.
-
- // The second field of the tuple (offset 8) is the ID of the "witness" for the
- // conformance of the concrete type to the interface used by this field.
- //
- auto witnessTableOffset = offset;
- witnessTableOffset.uniformOffset += 8;
- //
- // Conformances of a type to an interface are computed and then stored by the
- // Slang runtime, so we can look up the ID for this particular conformance (which
- // will create it on demand).
- //
- ComPtr<slang::ISession> slangSession;
- SLANG_RETURN_ON_FAIL(getRenderer()->getSlangSession(slangSession.writeRef()));
- //
- // Note: If the type doesn't actually conform to the required interface for
- // this sub-object range, then this is the point where we will detect that
- // fact and error out.
- //
- uint32_t conformanceID = 0xFFFFFFFF;
- SLANG_RETURN_ON_FAIL(slangSession->getTypeConformanceWitnessSequentialID(
- concreteType, existentialType, &conformanceID));
- //
- // Once we have the conformance ID, then we can write it into the object
- // at the required offset.
- //
- SLANG_RETURN_ON_FAIL(
- setData(witnessTableOffset, &conformanceID, sizeof(conformanceID)));
-
- // The third field of the tuple (offset 16) is the "payload" that is supposed to
- // hold the data for a value of the given concrete type.
- //
- auto payloadOffset = offset;
- payloadOffset.uniformOffset += 16;
-
- // There are two cases we need to consider here for how the payload might be used:
- //
- // * If the concrete type of the value being bound is one that can "fit" into the
- // available payload space, then it should be stored in the payload.
- //
- // * If the concrete type of the value cannot fit in the payload space, then it
- // will need to be stored somewhere else.
- //
- if (_doesValueFitInExistentialPayload(concreteTypeLayout, existentialTypeLayout))
- {
- // If the value can fit in the payload area, then we will go ahead and copy
- // its bytes into that area.
- //
- setData(
- payloadOffset,
- subObject->m_ordinaryData.getBuffer(),
- subObject->m_ordinaryData.getCount());
- }
- else
- {
- // If the value does *not *fit in the payload area, then there is nothing
- // we can do at this point (beyond saving a reference to the sub-object, which
- // was handled above).
- //
- // Once all the sub-objects have been set into the parent object, we can
- // compute a specialized layout for it, and that specialized layout can tell
- // us where the data for these sub-objects has been laid out.
- return SLANG_E_NOT_IMPLEMENTED;
- }
- }
- return SLANG_OK;
- }
-
- virtual SLANG_NO_THROW Result SLANG_MCALL
- getObject(ShaderOffset const& offset, IShaderObject** outObject) SLANG_OVERRIDE
- {
- SLANG_ASSERT(outObject);
- 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);
-
- returnComPtr(outObject, m_objects[bindingRange.flatIndex + offset.bindingArrayIndex]);
- return SLANG_OK;
- }
-
SLANG_NO_THROW Result SLANG_MCALL
setResource(ShaderOffset const& offset, IResourceView* resourceView) SLANG_OVERRIDE
{
@@ -2242,15 +2121,15 @@ public:
auto resourceViewImpl = static_cast<ResourceViewImpl*>(resourceView);
auto& bindingRange = layout->getBindingRange(offset.bindingRangeIndex);
- auto descriptorSlotIndex = bindingRange.flatIndex + (int32_t)offset.bindingArrayIndex;
+ auto descriptorSlotIndex = bindingRange.baseIndex + (int32_t)offset.bindingArrayIndex;
// Hold a reference to the resource to prevent its destruction.
- m_boundResources[bindingRange.flatIndex + offset.bindingArrayIndex] =
+ m_boundResources[bindingRange.baseIndex + offset.bindingArrayIndex] =
resourceViewImpl->m_resource;
ID3D12Device* d3dDevice = static_cast<D3D12Device*>(getDevice())->m_device;
d3dDevice->CopyDescriptorsSimple(
1,
m_descriptorSet.resourceTable.getCpuHandle(
- bindingRange.flatIndex + (int32_t)offset.bindingArrayIndex),
+ bindingRange.baseIndex + (int32_t)offset.bindingArrayIndex),
resourceViewImpl->m_descriptor.cpuHandle,
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
return SLANG_OK;
@@ -2270,7 +2149,7 @@ public:
d3dDevice->CopyDescriptorsSimple(
1,
m_descriptorSet.samplerTable.getCpuHandle(
- bindingRange.flatIndex +
+ bindingRange.baseIndex +
(int32_t)offset.bindingArrayIndex),
samplerImpl->m_descriptor.cpuHandle,
D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
@@ -2313,66 +2192,6 @@ public:
}
public:
- // Appends all types that are used to specialize the element type of this shader object in
- // `args` list.
- virtual Result collectSpecializationArgs(ExtendedShaderObjectTypeList& args) override
- {
- auto& subObjectRanges = getLayout()->getSubObjectRanges();
- // The following logic is built on the assumption that all fields that involve
- // existential types (and therefore require specialization) will results in a sub-object
- // range in the type layout. This allows us to simply scan the sub-object ranges to find
- // out all specialization arguments.
- Index subObjectRangeCount = subObjectRanges.getCount();
- for (Index subObjectRangeIndex = 0; subObjectRangeIndex < subObjectRangeCount;
- subObjectRangeIndex++)
- {
- auto const& subObjectRange = subObjectRanges[subObjectRangeIndex];
- auto const& bindingRange =
- getLayout()->getBindingRange(subObjectRange.bindingRangeIndex);
-
- Index count = bindingRange.count;
- SLANG_ASSERT(count == 1);
-
- Index subObjectIndexInRange = 0;
- auto subObject = m_objects[bindingRange.flatIndex + subObjectIndexInRange];
-
- switch (bindingRange.bindingType)
- {
- case slang::BindingType::ExistentialValue:
- {
- // A binding type of `ExistentialValue` means the sub-object represents a
- // interface-typed field. In this case the specialization argument for this
- // field is the actual specialized type of the bound shader object. If the
- // shader object's type is an ordinary type without existential fields, then
- // the type argument will simply be the ordinary type. But if the sub
- // object's type is itself a specialized type, we need to make sure to use
- // that type as the specialization argument.
-
- ExtendedShaderObjectType specializedSubObjType;
- SLANG_RETURN_ON_FAIL(
- subObject->getSpecializedShaderObjectType(&specializedSubObjType));
- args.add(specializedSubObjType);
- break;
- }
- case slang::BindingType::ParameterBlock:
- case slang::BindingType::ConstantBuffer:
- // Currently we only handle the case where the field's type is
- // `ParameterBlock<SomeStruct>` or `ConstantBuffer<SomeStruct>`, where
- // `SomeStruct` is a struct type (not directly an interface type). In this case,
- // we just recursively collect the specialization arguments from the bound sub
- // object.
- SLANG_RETURN_ON_FAIL(subObject->collectSpecializationArgs(args));
- // TODO: we need to handle the case where the field is of the form
- // `ParameterBlock<IFoo>`. We should treat this case the same way as the
- // `ExistentialValue` case here, but currently we lack a mechanism to
- // distinguish the two scenarios.
- break;
- }
- // TODO: need to handle another case where specialization happens on resources
- // fields e.g. `StructuredBuffer<IFoo>`.
- }
- return SLANG_OK;
- }
protected:
Result init(
@@ -2399,8 +2218,8 @@ public:
size_t uniformSize = layout->getElementTypeLayout()->getSize();
if (uniformSize)
{
- m_ordinaryData.setCount(uniformSize);
- memset(m_ordinaryData.getBuffer(), 0, uniformSize);
+ m_data.setCount(uniformSize);
+ memset(m_data.getBuffer(), 0, uniformSize);
}
// Each shader object will own CPU descriptor heap memory
@@ -2458,7 +2277,7 @@ public:
RefPtr<ShaderObjectImpl> subObject;
SLANG_RETURN_ON_FAIL(
ShaderObjectImpl::create(device, subObjectLayout, subObject.writeRef()));
- m_objects[bindingRangeInfo.flatIndex + i] = subObject;
+ m_objects[bindingRangeInfo.subObjectIndex + i] = subObject;
}
}
@@ -2474,8 +2293,8 @@ public:
size_t destSize,
ShaderObjectLayoutImpl* specializedLayout)
{
- auto src = m_ordinaryData.getBuffer();
- auto srcSize = size_t(m_ordinaryData.getCount());
+ auto src = m_data.getBuffer();
+ auto srcSize = size_t(m_data.getCount());
SLANG_ASSERT(srcSize <= destSize);
@@ -2550,7 +2369,7 @@ public:
for (uint32_t i = 0; i < count; ++i)
{
- auto subObject = m_objects[bindingRangeInfo.flatIndex + i];
+ auto subObject = m_objects[bindingRangeInfo.subObjectIndex + i];
RefPtr<ShaderObjectLayoutImpl> subObjectLayout;
SLANG_RETURN_ON_FAIL(
@@ -2863,7 +2682,7 @@ public:
{
auto& subObjectRange = specializedLayout->getSubObjectRange(i);
auto& bindingRange = specializedLayout->getBindingRange(subObjectRange.bindingRangeIndex);
- auto baseIndex = bindingRange.flatIndex;
+ auto subObjectIndex = bindingRange.subObjectIndex;
auto subObjectLayout = subObjectRange.layout.Ptr();
BindingOffset rangeOffset = offset;
@@ -2878,7 +2697,7 @@ public:
auto objOffset = rangeOffset;
for (uint32_t j = 0; j < bindingRange.count; j++)
{
- auto& object = m_objects[baseIndex + j];
+ auto& object = m_objects[subObjectIndex + j];
object->bindAsConstantBuffer(context, descriptorSet, objOffset, subObjectLayout);
objOffset += rangeStride;
}
@@ -2890,7 +2709,7 @@ public:
auto objOffset = rangeOffset;
for (uint32_t j = 0; j < bindingRange.count; j++)
{
- auto& object = m_objects[baseIndex + j];
+ auto& object = m_objects[subObjectIndex + j];
object->bindAsParameterBlock(context, objOffset, subObjectLayout);
objOffset += rangeStride;
}
@@ -2903,7 +2722,7 @@ public:
auto objOffset = rangeOffset;
for (uint32_t j = 0; j < bindingRange.count; j++)
{
- auto& object = m_objects[baseIndex + j];
+ auto& object = m_objects[subObjectIndex + j];
object->bindAsValue(context, descriptorSet, objOffset, subObjectLayout);
objOffset += rangeStride;
}
@@ -2915,10 +2734,6 @@ public:
return SLANG_OK;
}
- /// Any "ordinary" / uniform data for this object
- List<char> m_ordinaryData;
-
- List<RefPtr<ShaderObjectImpl>> m_objects;
/// A CPU-memory descriptor set holding any descriptors used to represent the resources/samplers in this object's state
DescriptorSet m_descriptorSet;
@@ -2967,7 +2782,9 @@ public:
auto renderer = getRenderer();
RefPtr<ShaderObjectLayoutImpl> layout;
SLANG_RETURN_ON_FAIL(renderer->getShaderObjectLayout(
- extendedType.slangType, (ShaderObjectLayoutBase**)layout.writeRef()));
+ extendedType.slangType,
+ m_layout->getContainerType(),
+ (ShaderObjectLayoutBase**)layout.writeRef()));
returnRefPtrMove(outLayout, layout);
return SLANG_OK;