diff options
| author | Yong He <yonghe@outlook.com> | 2021-05-21 16:38:33 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-05-21 16:38:33 -0700 |
| commit | 7f8a9994d0bd99a171a1daa0bce46d92c02ccffd (patch) | |
| tree | 0b187e63ab5b9ce6f5ab41266fedaec44091a217 /tools/gfx/d3d11 | |
| parent | 172538fdb418f7a2faab1f5a410f3b2cb8e18ba5 (diff) | |
[gfx] Support StructuredBuffer<IInterface>. (#1851)
Co-authored-by: T. Foley <tfoleyNV@users.noreply.github.com>
Diffstat (limited to 'tools/gfx/d3d11')
| -rw-r--r-- | tools/gfx/d3d11/render-d3d11.cpp | 272 |
1 files changed, 55 insertions, 217 deletions
diff --git a/tools/gfx/d3d11/render-d3d11.cpp b/tools/gfx/d3d11/render-d3d11.cpp index 6f6ba4faf..d4a55543c 100644 --- a/tools/gfx/d3d11/render-d3d11.cpp +++ b/tools/gfx/d3d11/render-d3d11.cpp @@ -212,17 +212,9 @@ protected: }; - 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: enum class Type { SRV, @@ -662,6 +654,10 @@ protected: /// `t` registers. /// uint32_t registerOffset; + + /// An index into the sub-object array if this binding range is treated + /// as a sub-object. + Index subObjectIndex; }; // Sometimes we just want to iterate over the ranges that represnet @@ -753,10 +749,15 @@ protected: Index m_subObjectCount = 0; 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; @@ -783,9 +784,31 @@ protected: case slang::BindingType::ParameterBlock: case slang::BindingType::ExistentialValue: 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; + } + if (slangBindingType == slang::BindingType::RawBuffer) + { + bindingRangeInfo.baseIndex = m_srvCount; + m_srvCount += count; + m_srvRanges.add(r); + } + else + { + bindingRangeInfo.baseIndex = m_uavCount; + m_uavCount += count; + m_uavRanges.add(r); + } + break; case slang::BindingType::Sampler: bindingRangeInfo.baseIndex = m_samplerCount; m_samplerCount += count; @@ -794,8 +817,6 @@ protected: case slang::BindingType::CombinedTextureSampler: break; - - case slang::BindingType::MutableRawBuffer: case slang::BindingType::MutableTexture: case slang::BindingType::MutableTypedBuffer: bindingRangeInfo.baseIndex = m_uavCount; @@ -953,8 +974,6 @@ protected: BindingRangeInfo const& getBindingRange(Index index) { return m_bindingRanges[index]; } - slang::TypeLayoutReflection* getElementTypeLayout() { return m_elementTypeLayout; } - Index getSRVCount() { return m_srvCount; } Index getSamplerCount() { return m_samplerCount; } Index getUAVCount() { return m_uavCount; } @@ -1002,6 +1021,7 @@ protected: m_totalOrdinaryDataSize = builder->m_totalOrdinaryDataSize; + m_containerType = builder->m_containerType; return SLANG_OK; } @@ -1127,7 +1147,11 @@ protected: SimpleBindingOffset m_pendingDataOffset; }; - class ShaderObjectImpl : public ShaderObjectBase + class ShaderObjectImpl + : public ShaderObjectBaseImpl< + ShaderObjectImpl, + ShaderObjectLayoutImpl, + SimpleShaderObjectData> { public: static Result create( @@ -1153,24 +1177,14 @@ protected: 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 @@ -1191,128 +1205,7 @@ protected: 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.baseIndex + 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; - } - - 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.baseIndex + offset.bindingArrayIndex]); - return SLANG_OK; - } + SLANG_NO_THROW Result SLANG_MCALL setResource(ShaderOffset const& offset, IResourceView* resourceView) SLANG_OVERRIDE @@ -1359,56 +1252,6 @@ protected: } 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.baseIndex + 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: @@ -1430,8 +1273,8 @@ protected: 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); } m_srvs.setCount(layout->getSRVCount()); @@ -1467,7 +1310,7 @@ protected: RefPtr<ShaderObjectImpl> subObject; SLANG_RETURN_ON_FAIL( ShaderObjectImpl::create(device, subObjectLayout, subObject.writeRef())); - m_objects[bindingRangeInfo.baseIndex + i] = subObject; + m_objects[bindingRangeInfo.subObjectIndex + i] = subObject; } } @@ -1482,8 +1325,8 @@ protected: { // 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()); + auto src = m_data.getBuffer(); + auto srcSize = size_t(m_data.getCount()); SLANG_ASSERT(srcSize <= destSize); memcpy(dest, src, srcSize); @@ -1554,7 +1397,7 @@ protected: for (Slang::Index i = 0; i < count; ++i) { - auto subObject = m_objects[bindingRangeInfo.baseIndex + i]; + auto subObject = m_objects[bindingRangeInfo.subObjectIndex + i]; RefPtr<ShaderObjectLayoutImpl> subObjectLayout; SLANG_RETURN_ON_FAIL(subObject->_getSpecializedLayout(subObjectLayout.writeRef())); @@ -1758,7 +1601,7 @@ protected: auto subObjectLayout = subObjectRange.layout; auto const& bindingRange = specializedLayout->getBindingRange(subObjectRange.bindingRangeIndex); Index count = bindingRange.count; - Index baseIndex = bindingRange.baseIndex; + Index subObjectIndex = bindingRange.subObjectIndex; // The starting offset for a sub-object range was computed // from Slang reflection information, so we can apply it here. @@ -1782,7 +1625,7 @@ protected: BindingOffset objOffset = rangeOffset; for(Index i = 0; i < count; ++i) { - auto subObject = m_objects[ baseIndex + i ]; + auto subObject = m_objects[subObjectIndex + i]; // Unsurprisingly, we bind each object in the range as // a constant buffer. @@ -1810,7 +1653,7 @@ protected: for(Index i = 0; i < count; ++i) { - auto subObject = m_objects[ baseIndex + i ]; + auto subObject = m_objects[subObjectIndex + i]; subObject->bindAsValue(context, BindingOffset(objOffset), subObjectLayout); objOffset += objStride; @@ -1830,10 +1673,6 @@ protected: // 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; @@ -1843,9 +1682,6 @@ protected: /// 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. /// @@ -1879,7 +1715,9 @@ protected: 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; |
