diff options
| author | jsmall-nvidia <jsmall@nvidia.com> | 2020-01-15 14:58:45 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-01-15 14:58:45 -0500 |
| commit | 662721ba4ab0e38924701df4c876a86eb8390968 (patch) | |
| tree | deef68220d0aebbdfff370918a3d42fcf12fd72c /tools | |
| parent | ef41dfc605f7868c0ccc7dde05982232b7d49589 (diff) | |
Bind Location (#1166)
* First pass at BindLocation.
* Added BindSet::init - for initializing with two input constant buffers. Needs better name, and perhaps should be another class.
* Fix handling of constant buffer stripping.
Improved initialization.
* Trying to generalize BindLocation a little more.
Split out CPULikeBindRoot.
* More work to make BindLocation et al work with non uniform bindings.
* Added parsing to a location.
* WIP: Trying to get CPU working with BindLocation.
* Describe problem of knowing the type of the reference point in the binding table.
* More ideas on getBindings fix.
* Remove BindSet as member of BindLocation.
* Added BindLocation::Invalid
* Made BindLocation able to be key in hash
* Use BindLocation for bindings on BindingSet.
* Added cuda and nvrtc categories to test infrastructure.
Disabled CUDA synthetic tests by default.
Fixed such that all tests now produce something in BindLocation style.
* Use m_userIndex instead of m_userData on Resource.
Move the binding setup out of cpu-compute-util (as no longer CPU specific)
* Removed CPUBinding - used BindLocation/BindSet instead.
Fixed some bugs around indexOf around uniform indirection.
* Renamed BindSet::Resource -> BindSet::Value.
* Document BindLocation.
* Fixes for Clang/GCC
Improve invariant requirement handling when constructing from BindPoints.
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/render-test/bind-location.cpp | 1117 | ||||
| -rw-r--r-- | tools/render-test/bind-location.h | 428 | ||||
| -rw-r--r-- | tools/render-test/cpu-compute-util.cpp | 344 | ||||
| -rw-r--r-- | tools/render-test/cpu-compute-util.h | 15 | ||||
| -rw-r--r-- | tools/render-test/cpu-memory-binding.cpp | 641 | ||||
| -rw-r--r-- | tools/render-test/cpu-memory-binding.h | 88 | ||||
| -rw-r--r-- | tools/render-test/render-test-main.cpp | 7 | ||||
| -rw-r--r-- | tools/render-test/render-test-tool.vcxproj | 4 | ||||
| -rw-r--r-- | tools/render-test/render-test-tool.vcxproj.filters | 8 | ||||
| -rw-r--r-- | tools/render-test/shader-input-layout.cpp | 135 | ||||
| -rw-r--r-- | tools/render-test/shader-input-layout.h | 10 | ||||
| -rw-r--r-- | tools/slang-test/options.h | 4 | ||||
| -rw-r--r-- | tools/slang-test/slang-test-main.cpp | 10 |
13 files changed, 1884 insertions, 927 deletions
diff --git a/tools/render-test/bind-location.cpp b/tools/render-test/bind-location.cpp new file mode 100644 index 000000000..6548e861c --- /dev/null +++ b/tools/render-test/bind-location.cpp @@ -0,0 +1,1117 @@ + +#include "bind-location.h" + +#include "../../slang-com-helper.h" + +#include "../../source/core/slang-token-reader.h" + +namespace renderer_test { +using namespace Slang; + +/* static */const BindLocation BindLocation::Invalid; + +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! BindSet !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +BindSet::BindSet(): + m_arena(4096, 16) +{ +} + +BindSet::~BindSet() +{ + for (auto value : m_values) + { + value->~Value(); + } +} + +void BindSet::setAt(const BindLocation& loc, Value* value) +{ + SLANG_ASSERT(loc.isValid()); + if (loc.isInvalid()) + { + return; + } + + // Note we don't remove when value == null, such that it is stored if should be nullptr + Value** valuePtr = m_bindings.TryGetValueOrAdd(loc, value); + if (valuePtr) + { + *valuePtr = value; + } +} + +void BindSet::setAt(const BindLocation& loc, SlangParameterCategory category, Value* value) +{ + SLANG_ASSERT(loc.isValid()); + if (loc.isInvalid()) + { + return; + } + + const BindPoint* point = loc.getValidBindPointForCategory(category); + if (point) + { + if (loc.m_bindPointSet == nullptr) + { + // Can only have one category, so just set on that + setAt(loc, value); + } + else + { + + BindLocation catLoc(loc.m_typeLayout, category, *point, loc.m_value); + setAt(catLoc, value); + } + } + else + { + SLANG_ASSERT(!"Does not have category"); + } +} + +BindSet::Value* BindSet::getAt(const BindLocation& loc) const +{ + SLANG_ASSERT(loc.isValid()); + if (loc.isInvalid()) + { + return nullptr; + } + Value** valuePtr = m_bindings.TryGetValue(loc); + return valuePtr ? *valuePtr : nullptr; +} + +BindSet::Value* BindSet::_createBufferValue(slang::TypeReflection::Kind kind, slang::TypeLayoutReflection* typeLayout, size_t bufferSizeInBytes, size_t initialSizeInBytes, const void* initialData) +{ + SLANG_ASSERT(typeLayout == nullptr || typeLayout->getKind() == kind); + + Value* value = new (m_arena.allocateAligned(sizeof(Value), SLANG_ALIGN_OF(Value))) Value(); + + value->m_kind = kind; + value->m_sizeInBytes = bufferSizeInBytes; + value->m_elementCount = 0; + value->m_type = typeLayout; + value->m_userIndex = -1; + + value->m_data = (uint8_t*)m_arena.allocateAligned(bufferSizeInBytes, 16); + + SLANG_ASSERT(initialSizeInBytes <= value->m_sizeInBytes); + if (initialData) + { + ::memcpy(value->m_data, initialData, initialSizeInBytes); + ::memset(value->m_data + initialSizeInBytes, 0, bufferSizeInBytes - initialSizeInBytes); + } + else + { + ::memset(value->m_data, 0, value->m_sizeInBytes); + } + + m_values.add(value); + return value; +} + +/* static */bool BindSet::isTextureType(slang::TypeLayoutReflection* typeLayout) +{ + switch (typeLayout->getKind()) + { + case slang::TypeReflection::Kind::Resource: + { + auto type = typeLayout->getType(); + auto shape = type->getResourceShape(); + + switch (shape & SLANG_RESOURCE_BASE_SHAPE_MASK) + { + case SLANG_TEXTURE_2D: + case SLANG_TEXTURE_1D: + case SLANG_TEXTURE_3D: + case SLANG_TEXTURE_CUBE: + case SLANG_TEXTURE_BUFFER: + { + return true; + } + } + } + default: break; + } + + return false; +} + +BindSet::Value* BindSet::createTextureValue(slang::TypeLayoutReflection* typeLayout) +{ + if (!isTextureType(typeLayout)) + { + SLANG_ASSERT(!"Not a texture type"); + return nullptr; + } + + Value* value = new (m_arena.allocateAligned(sizeof(Value), SLANG_ALIGN_OF(Value))) Value(); + + value->m_kind = typeLayout->getKind(); + value->m_sizeInBytes = 0; + value->m_elementCount = 0; + value->m_type = typeLayout; + value->m_data = nullptr; + value->m_userIndex = -1; + + m_values.add(value); + + return value; +} + +BindSet::Value* BindSet::createBufferValue(slang::TypeReflection::Kind kind, size_t sizeInBytes, const void* initialData) +{ + return _createBufferValue(kind, nullptr, sizeInBytes, sizeInBytes, initialData); +} + +BindSet::Value* BindSet::createBufferValue(slang::TypeLayoutReflection* typeLayout, size_t sizeInBytes, const void* initialData) +{ + const auto kind = typeLayout->getKind(); + switch (kind) + { + case slang::TypeReflection::Kind::ParameterBlock: + case slang::TypeReflection::Kind::ConstantBuffer: + { + return _createBufferValue(kind, typeLayout, sizeInBytes, sizeInBytes, initialData); + } + case slang::TypeReflection::Kind::Resource: + { + auto type = typeLayout->getType(); + auto shape = type->getResourceShape(); + + switch (shape & SLANG_RESOURCE_BASE_SHAPE_MASK) + { + case SLANG_STRUCTURED_BUFFER: + { + auto elementTypeLayout = typeLayout->getElementTypeLayout(); + size_t elementSize = elementTypeLayout->getSize(SLANG_PARAMETER_CATEGORY_UNIFORM); + + // We don't know the size of the buffer, but we can work it out, based on what is initialized + size_t elementCount = size_t((sizeInBytes + elementSize - 1) / elementSize); + size_t bufferSize = elementCount * elementSize; + + Value* value = _createBufferValue(kind, typeLayout, bufferSize, sizeInBytes, initialData); + value->m_elementCount = elementCount; + return value; + } + case SLANG_BYTE_ADDRESS_BUFFER: + { + return _createBufferValue(kind, typeLayout, (sizeInBytes + 3) & ~size_t(3), sizeInBytes, initialData); + } + } + break; + } + + + default: break; + } + + SLANG_ASSERT(!"Unable to construct this type of buffer"); + return nullptr; +} + +void BindSet::destroyValue(Value* value) +{ + // TODO(JS): NOTE we do not free the old buffer. This is not a memory leak, because + // it is tracked elsewhere, but there is an argument to destroy it. + const Index index = m_values.indexOf(value); + SLANG_ASSERT(index >= 0); + if (index >= 0) + { + m_values.fastRemoveAt(index); + + // I guess we should remove any bindings to it whilst we are at it + List<BindLocation> locations; + for (const auto& pair : m_bindings) + { + const auto& location = pair.Key; + if (location.m_value == value) + { + locations.add(location); + } + } + + for (auto location : locations) + { + m_bindings.Remove(location); + } + + // Run the dtor + value->~Value(); + } +} + +void BindSet::calcChildResourceLocations(const BindLocation& location, List<BindLocation>& outLocations) +{ + auto typeLayout = location.getTypeLayout(); + + const auto kind = typeLayout->getKind(); + switch (kind) + { + case slang::TypeReflection::Kind::Array: + { + auto elementTypeLayout = typeLayout->getElementTypeLayout(); + auto elementCount = int(typeLayout->getElementCount()); + + // We only iterate over the array, if it's a fixed array (not an unbounded array) + // as it is then the elements are much like the fields of a struct and so 'children'. + if (elementCount != 0) + { + for (Index i = 0; i < elementCount; ++i) + { + BindLocation elementLocation = toIndex(location, i); + calcChildResourceLocations(elementLocation, outLocations); + } + } + break; + } + case slang::TypeReflection::Kind::Struct: + { + auto structTypeLayout = typeLayout; + + auto fieldCount = structTypeLayout->getFieldCount(); + for (uint32_t ff = 0; ff < fieldCount; ++ff) + { + auto field = structTypeLayout->getFieldByIndex(ff); + BindLocation fieldLocation = toField(location, field); + + calcChildResourceLocations(fieldLocation, outLocations); + } + break; + } + + default: break; + } +} + +void BindSet::calcValueLocations(const BindLocation& location, Slang::List<BindLocation>& outLocations) +{ + auto typeLayout = location.getTypeLayout(); + + const auto kind = typeLayout->getKind(); + switch (kind) + { + case slang::TypeReflection::Kind::Array: + { + auto elementTypeLayout = typeLayout->getElementTypeLayout(); + auto elementCount = int(typeLayout->getElementCount()); + + // If it's unbounded, it could point directly to a value/resource. We can't iterate over it + // as 'children' because being an external value/resource (or in a register space) they + // are not part of the underling location. + if (elementCount == 0) + { + outLocations.add(location); + } + break; + } + + case slang::TypeReflection::Kind::SamplerState: + + case slang::TypeReflection::Kind::ParameterBlock: + case slang::TypeReflection::Kind::ConstantBuffer: + case slang::TypeReflection::Kind::Resource: + case slang::TypeReflection::Kind::TextureBuffer: + case slang::TypeReflection::Kind::ShaderStorageBuffer: + { + //auto elementTypeLayout = typeLayout->getElementTypeLayout(); + //const size_t elementSize = elementTypeLayout->getSize(); + + outLocations.add(location); + break; + } + default: + { + calcChildResourceLocations(location, outLocations); + break; + } + } +} + +BindLocation BindSet::toField(const BindLocation& loc, slang::VariableLayoutReflection* field) const +{ + const Index categoryCount = Index(field->getCategoryCount()); + if (categoryCount == 0) + { + return BindLocation::Invalid; + } + + if (loc.m_bindPointSet) + { + BindPoints bindPoints; + bindPoints.setInvalid(); + + // Copy over and add the ones found here + for (Index i = 0; i < categoryCount; ++i) + { + auto category = field->getCategoryByIndex((unsigned int)i); + + auto const& point = loc.m_bindPointSet->m_points[category]; + if (point.isInvalid()) + { + return BindLocation::Invalid; + } + + auto space = field->getBindingSpace(category); + auto offset = field->getOffset(category); + + // Set using new space, and offset + bindPoints[category] = BindPoint(space, point.m_offset + offset); + } + + return BindLocation(field->getTypeLayout(), bindPoints, loc.m_value); + } + else + { + SLANG_ASSERT(categoryCount == 1); + auto category = field->getCategoryByIndex(0); + + // If I'm going from mixed, then I will have multiple items being tracked (so won't be here) + // If I'm not, then I'm getting an inplace field. It must be relative + // So it would seem I never need to call getBindingIndex, and since I can't do that it must be relative. + // AND if it's relative well it must be in the same category. + + if (category == loc.m_category) + { + auto space = field->getBindingSpace(category); + auto offset = field->getOffset(category); + + return BindLocation(field->getTypeLayout(), category, BindPoint(space, loc.m_point.m_offset + offset), loc.m_value); + } + } + + return BindLocation::Invalid; +} + +BindLocation BindSet::toField(const BindLocation& loc, const char* name) const +{ + if (!loc.isValid()) + { + return loc; + } + + auto typeLayout = loc.m_typeLayout; + const auto kind = typeLayout->getKind(); + + // Strip constantBuffer wrapping, only really applies when we have handles to value/resource + // embedded in other types (like on CPU and CUDA) + if (loc.m_value && + (kind == slang::TypeReflection::Kind::ConstantBuffer || kind == slang::TypeReflection::Kind::ParameterBlock)) + { + // Follow the to associated value/resource + BindSet::Value* value = getAt(loc); + if (value) + { + typeLayout = typeLayout->getElementTypeLayout(); + return toField(BindLocation(typeLayout, SLANG_PARAMETER_CATEGORY_UNIFORM, BindPoint(0, 0), value), name); + } + } + + if (kind == slang::TypeReflection::Kind::Struct) + { + slang::VariableLayoutReflection* varLayout = nullptr; + auto fieldCount = typeLayout->getFieldCount(); + for (uint32_t ff = 0; ff < fieldCount; ++ff) + { + auto field = typeLayout->getFieldByIndex(ff); + if (strcmp(field->getName(), name) == 0) + { + return toField(loc, field); + } + } + } + + // Invalid + return BindLocation::Invalid; +} + +BindLocation BindSet::toIndex(const BindLocation& loc, Index index) const +{ + if (!loc.isValid()) + { + return loc; + } + SLANG_ASSERT(index >= 0); + if (index < 0) + { + return BindLocation::Invalid; + } + + auto typeLayout = loc.m_typeLayout; + const auto kind = typeLayout->getKind(); + + // If it's a zero sized array, we may need to special case indirecting through a buffer that holds it's contents + if (kind != slang::TypeReflection::Kind::Array) + { + return BindLocation::Invalid; + } + + // Find where the uniform data will be held. If we have a unsized array, for some targets the actual content's might be in a different location + BindSet::Value* uniformValue = loc.m_value; + if (typeLayout->getElementCount() == 0) + { + // If we have a value/resource at this location, then we need to offset through that + BindSet::Value* arrayValue = getAt(loc); + if (arrayValue) + { + uniformValue = arrayValue; + + // Check it's in range. + // NOTE we can't check this if the unbounded binding is in another space for example. + if (index >= Index(uniformValue->m_elementCount)) + { + return BindLocation::Invalid; + } + } + } + + auto elementTypeLayout = typeLayout->getElementTypeLayout(); + + const Index categoryCount = Index(elementTypeLayout->getCategoryCount()); + + if (loc.m_bindPointSet) + { + BindPoints bindPoints; + bindPoints.setInvalid(); + + // Copy over and add the ones found here + for (Index i = 0; i < categoryCount; ++i) + { + auto category = elementTypeLayout->getCategoryByIndex((unsigned int)i); + const auto elementStride = typeLayout->getElementStride(category); + + size_t baseOffset = loc.m_bindPointSet->m_points[category].m_offset; + + if (category == SLANG_PARAMETER_CATEGORY_UNIFORM && uniformValue != loc.m_value) + { + baseOffset = 0; + } + + const auto& basePoint = loc.m_bindPointSet->m_points[category]; + SLANG_ASSERT(basePoint.isValid()); + bindPoints[category] = BindPoint(basePoint.m_space, baseOffset + elementStride * index); + } + + return BindLocation(elementTypeLayout, bindPoints, uniformValue); + } + else + { + SLANG_ASSERT(categoryCount == 1); + auto category = elementTypeLayout->getCategoryByIndex(0); + + const auto elementStride = typeLayout->getElementStride(category); + + size_t baseOffset = 0; + if (category == slang::ParameterCategory::Uniform && uniformValue != loc.m_value) + { + // base of 0 is appropriate as it is the child value + } + else + { + // TODO(JS): + // Hmm, if its a different category, then not entirely clear what to do here. + // Just zero as we can't use the base we have. + // This might just be an error + + baseOffset = (category == loc.m_category) ? loc.m_point.m_offset : 0; + } + + BindPoint point(loc.m_point.m_space, baseOffset + elementStride * index); + + return BindLocation(elementTypeLayout, category, point, uniformValue); + } + + return BindLocation::Invalid; +} + + +SlangResult BindSet::setBufferContents(const BindLocation& loc, const void* initialData, size_t sizeInBytes) const +{ + BindSet::Value* value = getAt(loc); + if (value) + { + // Truncate if initial data is larger than the buffer + sizeInBytes = (sizeInBytes > value->m_sizeInBytes) ? value->m_sizeInBytes : sizeInBytes; + + SLANG_ASSERT(value->m_sizeInBytes >= sizeInBytes); + ::memcpy(value->m_data, initialData, sizeInBytes); + return SLANG_OK; + } + return SLANG_FAIL; +} + +void BindSet::getBindings(List<BindLocation>& outLocations, List<Value*>& outResources) const +{ + outResources.clear(); + outLocations.clear(); + for (const auto& pair : m_bindings) + { + outLocations.add(pair.Key); + outResources.add(pair.Value); + } +} + +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! BindLocation !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +BindLocation::BindLocation(slang::TypeLayoutReflection* typeLayout, const BindPoints& points, BindSet_Value* value) : + m_typeLayout(typeLayout) +{ + Slang::Index categoryIndex; + auto categoryCount = points.calcValidCount(&categoryIndex); + + if (categoryCount == 0) + { + // If no categories - mark as invalid + m_typeLayout = nullptr; + m_category = slang::ParameterCategory::None; + } + else if (categoryCount == 1) + { + // If 1, we don't want m_bindPointSet + m_category = SlangParameterCategory(categoryIndex); + m_point = points.m_points[categoryIndex]; + } + else + { + // We don't use these + m_category = SLANG_PARAMETER_CATEGORY_NONE; + m_point.setInvalid(); + + // Create a bindpoint set + m_bindPointSet = new BindPointSet(points); + } +} + +BindLocation::BindLocation(slang::TypeLayoutReflection* typeLayout, SlangParameterCategory category, const BindPoint& point, BindSet_Value* value) : + m_category(category), + m_point(point), + m_typeLayout(typeLayout), + m_value(value) +{ +} + +BindPoint* BindLocation::getValidBindPointForCategory(SlangParameterCategory category) +{ + BindPoint* point = nullptr; + if (m_bindPointSet) + { + point = &m_bindPointSet->m_points.m_points[category]; + } + else if (m_category == category) + { + point = &m_point; + } + return (point && point->isValid()) ? point : nullptr; +} + +const BindPoint* BindLocation::getValidBindPointForCategory(SlangParameterCategory category) const +{ + const BindPoint* point = nullptr; + if (m_bindPointSet) + { + point = &m_bindPointSet->m_points.m_points[category]; + } + else if (m_category == category) + { + point = &m_point; + } + return (point && point->isValid()) ? point : nullptr; +} + +BindPoint BindLocation::getBindPointForCategory(SlangParameterCategory category) const +{ + if (m_bindPointSet) + { + return m_bindPointSet->m_points.m_points[category]; + } + else if (m_category == category) + { + return m_point; + } + return BindPoint::makeInvalid(); +} + +void BindLocation::setPoints(const BindPoints& points) +{ + Index found; + auto const validCount = points.calcValidCount(&found); + + // There is nothing tracked, so we are done. + if (validCount == 0) + { + setEmptyBinding(); + return; + } + + if (validCount == 1) + { + m_bindPointSet.setNull(); + m_point = points.m_points[found]; + m_category = SlangParameterCategory(found); + return; + } + + if (m_bindPointSet->isUniquelyReferenced()) + { + m_bindPointSet->m_points = points; + } + else + { + m_bindPointSet = new BindPointSet(points); + } +} + +void BindLocation::addOffset(SlangParameterCategory category, ptrdiff_t offset) +{ + BindPoint* point = getValidBindPointForCategory(category); + if (point) + { + point->m_offset += offset; + } +} + +void* BindLocation::getUniform(size_t sizeInBytes) const +{ + const BindPoint* point = getValidBindPointForCategory(SLANG_PARAMETER_CATEGORY_UNIFORM); + if (m_value && point) + { + size_t offset = point->m_offset; + // Make sure it's in range + if (offset + sizeInBytes <= m_value->m_sizeInBytes) + { + return m_value->m_data + offset; + } + } + return nullptr; +} + +SlangResult BindLocation::setUniform(const void* data, size_t sizeInBytes) const +{ + // It has to be a location with uniform + const BindPoint* point = getValidBindPointForCategory(SLANG_PARAMETER_CATEGORY_UNIFORM); + if (m_value && point) + { + size_t offset = point->m_offset; + // Make sure it's in range + SLANG_ASSERT(offset + sizeInBytes <= m_value->m_sizeInBytes); + + // Okay copy the contents + ::memcpy(m_value->m_data + offset, data, sizeInBytes); + return SLANG_OK; + } + return SLANG_FAIL; +} + +bool BindLocation::operator==(const ThisType& rhs) const +{ + if (m_typeLayout != rhs.m_typeLayout || + m_value != rhs.m_value) + { + return false; + } + + // If same, then if it's set they must be equal + // If not set, then must be the same category/point + if (m_bindPointSet == rhs.m_bindPointSet) + { + return m_bindPointSet || (m_category == rhs.m_category && m_point == rhs.m_point); + } + + // Only way these can be equal now, is if both are m_bindPointSet are different pointers, but same value + return (m_bindPointSet && rhs.m_bindPointSet) && (m_bindPointSet->m_points == rhs.m_bindPointSet->m_points); +} + +int BindLocation::GetHashCode() const +{ + if (!m_typeLayout) + { + return 1; + } + if (m_bindPointSet) + { + return m_bindPointSet->GetHashCode(); + } + else + { + return Slang::combineHash(Slang::combineHash(m_category, Slang::GetHashCode(m_typeLayout)), m_point.GetHashCode()); + } +} + + +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! BindRoot !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +SlangResult BindRoot::parse(const String& text, const String& sourcePath, WriterHelper outStream, BindLocation& outLocation) +{ + SLANG_ASSERT(m_bindSet); + + // We will parse the 'name' as may be path to a value/resource + TokenReader parser(text); + + BindLocation location = BindLocation::Invalid; + + { + Token nameToken = parser.ReadToken(); + if (nameToken.Type != TokenType::Identifier) + { + outStream.print("Invalid input syntax at line %d", int(parser.NextToken().Position.Line)); + return SLANG_FAIL; + } + location = find(nameToken.Content.getBuffer()); + if (location.isInvalid()) + { + outStream.print("Unable to find entry in '%s' for '%s' (for CPU name must be specified) \n", sourcePath.getBuffer(), text.getBuffer()); + return SLANG_FAIL; + } + } + + while (!parser.IsEnd()) + { + Token token = parser.NextToken(0); + + if (token.Type == TokenType::LBracket) + { + parser.ReadToken(); + int index = parser.ReadInt(); + SLANG_ASSERT(index >= 0); + + location = m_bindSet->toIndex(location, index); + if (location.isInvalid()) + { + outStream.print("Unable to find entry in '%d' in '%s'\n", index, text.getBuffer()); + return SLANG_FAIL; + } + parser.ReadMatchingToken(TokenType::RBracket); + } + else if (token.Type == TokenType::Dot) + { + parser.ReadToken(); + Token identifierToken = parser.ReadMatchingToken(TokenType::Identifier); + + location = m_bindSet->toField(location, identifierToken.Content.getBuffer()); + if (location.isInvalid()) + { + outStream.print("Unable to find field '%s' in '%s'\n", identifierToken.Content.getBuffer(), text.getBuffer()); + return SLANG_FAIL; + } + } + else if (token.Type == TokenType::Comma) + { + // Break out + break; + } + else + { + return SLANG_FAIL; + } + } + + outLocation = location; + return SLANG_OK; +} + +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! CPULikeBindRoot !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +SlangResult CPULikeBindRoot::init(BindSet* bindSet, slang::ShaderReflection* reflection, int entryPointIndex) +{ + m_bindSet = bindSet; + + m_reflection = reflection; + m_rootValue = nullptr; + m_entryPointValue = nullptr; + + { + size_t globalConstantBuffer = reflection->getGlobalConstantBufferSize(); + + size_t rootSizeInBytes = 0; + const int parameterCount = reflection->getParameterCount(); + for (int i = 0; i < parameterCount; ++i) + { + auto parameter = reflection->getParameterByIndex(i); + + auto offset = parameter->getOffset(); + + auto typeLayout = parameter->getTypeLayout(); + auto sizeInBytes = typeLayout->getSize(); + + size_t endOffset = offset + sizeInBytes; + + rootSizeInBytes = (endOffset > rootSizeInBytes) ? endOffset : rootSizeInBytes; + } + SLANG_ASSERT(rootSizeInBytes == globalConstantBuffer); + + if (rootSizeInBytes) + { + // Allocate the 'root' buffer + m_rootValue = m_bindSet->createBufferValue(slang::TypeReflection::Kind::ConstantBuffer, rootSizeInBytes); + } + } + + { + auto entryPointCount = int(reflection->getEntryPointCount()); + if (entryPointIndex < 0 || entryPointIndex >= entryPointCount) + { + SLANG_ASSERT(!"Entry point index out of range"); + return SLANG_FAIL; + } + + m_entryPoint = reflection->getEntryPointByIndex(entryPointIndex); + size_t entryPointParamsSizeInBytes = 0; + + const int parameterCount = int(m_entryPoint->getParameterCount()); + for (int i = 0; i < parameterCount; i++) + { + slang::VariableLayoutReflection* parameter = m_entryPoint->getParameterByIndex(i); + + // If has a semantic, then isn't uniform parameter + if (auto semanticName = parameter->getSemanticName()) + { + continue; + } + + auto offset = parameter->getOffset(); + + auto typeLayout = parameter->getTypeLayout(); + auto sizeInBytes = typeLayout->getSize(); + + size_t endOffset = offset + sizeInBytes; + entryPointParamsSizeInBytes = (endOffset > entryPointParamsSizeInBytes) ? endOffset : entryPointParamsSizeInBytes; + } + + if (entryPointParamsSizeInBytes) + { + m_entryPointValue = m_bindSet->createBufferValue(slang::TypeReflection::Kind::ConstantBuffer, entryPointParamsSizeInBytes); + } + } + + return SLANG_OK; +} + +slang::VariableLayoutReflection* CPULikeBindRoot::getParameterByName(const char* name) +{ + const int parameterCount = m_reflection->getParameterCount(); + for (int i = 0; i < parameterCount; ++i) + { + auto parameter = m_reflection->getParameterByIndex(i); + const char* paramName = parameter->getName(); + if (strcmp(name, paramName) == 0) + { + return parameter; + } + } + + return nullptr; +} + +slang::VariableLayoutReflection* CPULikeBindRoot::getEntryPointParameterByName(const char* name) +{ + const int parameterCount = int(m_entryPoint->getParameterCount()); + for (int i = 0; i < parameterCount; ++i) + { + auto parameter = m_entryPoint->getParameterByIndex(i); + // If has a semantic we will ignore + if (parameter->getSemanticName()) + { + continue; + } + if (strcmp(parameter->getName(), name) == 0) + { + return parameter; + } + } + return nullptr; +} + +BindLocation CPULikeBindRoot::find(const char* name) +{ + Value* value = nullptr; + slang::VariableLayoutReflection* varLayout = nullptr; + + if (m_rootValue) + { + varLayout = getParameterByName(name); + value = m_rootValue; + } + + if (!varLayout && m_entryPointValue) + { + value = m_entryPointValue; + varLayout = getEntryPointParameterByName(name); + } + + if (!varLayout) + { + return BindLocation::Invalid; + } + + // We don't need to worry about bindSpace because variable must be stored in the buffer + // auto space = varLayout->getBindingSpace(); + // TODO(JS): Where is getBindingIndex supposed to be used. It seems the offset here will do the right thing + auto offset = varLayout->getOffset(SLANG_PARAMETER_CATEGORY_UNIFORM); + + return BindLocation(varLayout->getTypeLayout(), SLANG_PARAMETER_CATEGORY_UNIFORM, BindPoint(0, offset), value); +} + +SlangResult CPULikeBindRoot::setArrayCount(const BindLocation& location, int count) +{ + if (!location.isValid()) + { + return SLANG_FAIL; + } + + // I can see if a resource has already been set + Value* value = m_bindSet->getAt(location); + + auto typeLayout = location.getTypeLayout(); + const auto kind = typeLayout->getKind(); + + if (!(typeLayout->getKind() == slang::TypeReflection::Kind::Array && typeLayout->getElementCount() == 0)) + { + return SLANG_FAIL; + } + + const size_t elementStride = typeLayout->getElementStride(SLANG_PARAMETER_CATEGORY_UNIFORM); + auto elementTypeLayout = typeLayout->getElementTypeLayout(); + + if (value) + { + // Making smaller, just reduce the count. + // NOTE! Nothing is done here about deallocating resources which are perhaps no longer reachable. + // This isn't a leakage problem tho, as all buffers are released automatically when scope is left. + if (count <= int(value->m_elementCount) || count <= int(value->m_sizeInBytes / elementStride)) + { + value->m_elementCount = count; + return SLANG_OK; + } + + const size_t maxElementCount = (value->m_sizeInBytes / elementStride); + if (count <= maxElementCount) + { + // Just initialize the space + ::memset(value->m_data + elementStride * value->m_elementCount, 0, (count - value->m_elementCount) * elementStride); + value->m_elementCount = count; + return SLANG_OK; + } + } + + // Ok allocate a buffer that can hold all the elements + + const size_t newBufferSize = count * elementStride; + + Value* newValue = m_bindSet->createBufferValue(slang::TypeReflection::Kind::Array, newBufferSize); + newValue->m_elementCount = count; + + // Copy over the data from the old buffer if there is any + if (value && value->m_elementCount) + { + ::memcpy(newValue->m_data, value->m_data, value->m_elementCount * elementStride); + } + + // Remove the old buffer as no longer needed + + if (value) + { + m_bindSet->destroyValue(value); + } + + // Set the new buffer + m_bindSet->setAt(location, newValue); + return SLANG_OK; +} + + +void CPULikeBindRoot::getRoots(Slang::List<BindLocation>& outLocations) +{ + if (m_entryPointValue) + { + const int parameterCount = int(m_entryPoint->getParameterCount()); + for (int i = 0; i < parameterCount; ++i) + { + auto parameter = m_entryPoint->getParameterByIndex(i); + // If has a semantic we will ignore + if (parameter->getSemanticName()) + { + continue; + } + + auto offset = parameter->getOffset(SLANG_PARAMETER_CATEGORY_UNIFORM); + + BindLocation location(parameter->getTypeLayout(), SLANG_PARAMETER_CATEGORY_UNIFORM, BindPoint(0, offset), m_entryPointValue); + outLocations.add(location); + } + } + + if (m_rootValue) + { + const int parameterCount = m_reflection->getParameterCount(); + for (int i = 0; i < parameterCount; ++i) + { + auto parameter = m_reflection->getParameterByIndex(i); + + auto offset = parameter->getOffset(SLANG_PARAMETER_CATEGORY_UNIFORM); + + BindLocation location(parameter->getTypeLayout(), SLANG_PARAMETER_CATEGORY_UNIFORM, BindPoint(0, offset), m_rootValue); + outLocations.add(location); + } + } +} + +static void _addDefaultBuffersRec(BindSet* bindSet, const BindLocation& loc) +{ + // See if there is a value/resource attached there + auto typeLayout = loc.getTypeLayout(); + + const auto kind = typeLayout->getKind(); + switch (kind) + { + case slang::TypeReflection::Kind::ParameterBlock: + case slang::TypeReflection::Kind::ConstantBuffer: + { + BindSet::Value* value = bindSet->getAt(loc); + + auto elementTypeLayout = typeLayout->getElementTypeLayout(); + + if (!value) + { + //SLANG_ASSERT(typeLayout->getSize() == sizeof(void*)); + const size_t elementSize = elementTypeLayout->getSize(); + + // We create using typeLayout (as opposed to elementTypeLayout), because it also holds the wrapping + // 'resource' type. + value = bindSet->createBufferValue(typeLayout, elementSize); + SLANG_ASSERT(value); + + bindSet->setAt(loc, value); + } + + // Recurse into buffer, using the elementType + BindLocation childLocation(elementTypeLayout, SLANG_PARAMETER_CATEGORY_UNIFORM, BindPoint(0, 0), value ); + _addDefaultBuffersRec(bindSet, childLocation); + return; + } + default: break; + } + + // Recurse + { + List<BindLocation> childLocations; + bindSet->calcChildResourceLocations(loc, childLocations); + for (auto& childLocation : childLocations) + { + _addDefaultBuffersRec(bindSet, childLocation); + } + } +} + +void CPULikeBindRoot::addDefaultValues() +{ + + List<BindLocation> rootLocations; + getRoots(rootLocations); + + for (auto& location : rootLocations) + { + _addDefaultBuffersRec(m_bindSet, location); + } +} + +} // renderer_test diff --git a/tools/render-test/bind-location.h b/tools/render-test/bind-location.h new file mode 100644 index 000000000..0ce99731d --- /dev/null +++ b/tools/render-test/bind-location.h @@ -0,0 +1,428 @@ +#ifndef BIND_LOCATION_H +#define BIND_LOCATION_H + +#include "core/slang-basic.h" +#include "core/slang-free-list.h" +#include "core/slang-memory-arena.h" +#include "core/slang-writer.h" + +#include "slang.h" + +namespace renderer_test { + +/* +Bind Set/Point/Value +==================== + +The following classes are designed as a mechanism to simplify binding within the test system. The underlying issues are + +* How binding occurs is very dependent on the underlying target (CPU is different from Dx for example) + + CPU everything is just backed by uniform 'memory'/GPU uses different registers for different types + + With unbound arrays CPU can just indirect to a buffer, on GPU it might need use of register spaces or some other mechanism (as in VK) + + CPU groups together global/entry point parameters, GPU typically does not +* Having a mechanism that will the data/binding for the test independent of the actual target, allows that code/implementation to be shared across many targets. +* How a resource/state is configured within binding also varies significantly between targets + +One way to handle this disparity, would be to build an abstraction layer, that could create the device specific +resources/state and set them. This is not the approach taken here though. The idea here is to have a mechanism to +be able to build structures in memory, and record where binding takes place without having to create any +device specific resources or state. This data can then be used to construct and then bind as is appropriate. + +The process broadly for test system is is + +1) Set up any default buffers required for a target (for example the uniform/entry point buffers for CPU) +2) Add any default Value/buffers that are needed by traversing reflection +3) Create/Set the Values for the elements of ShaderInputLayoutEntry +4) Go through the values set on the BindSet, creating Resources/State etc appropriate for the target +5) Go through the bindings setting the Resource bindings as appropriate for the target +6) Execute +7) If the computation takes place outside of Values backing memory, copy back the data for output entries +8) Write the output entries + +To do this we need a mechanism to store a binding location. In the general case a BindingLocation might +track the location of many different categories of data. + +We also need a way to record what we want to create on the device for execution. To do this we have the +BindSet::Value. 'Value' was used instead of 'Resource' because the types of things the Value might represent +may not be resource like or might be multiple resources. In simple use cases though a 'Value' is typically +synonymous with some kind of Resource on the device. + +A Value knows the underlying type it represents as was determined via the slang layout/reflection. That an added +feature of 'Values' is there are able hold a buffer that is typically mapped onto some linear buffer on the +device. Doing so means that we do not need to store BindLocation mappings for say uniform data (like float or +matrix), it can just be stored in the memory buffer. When the resources are constructed for execution, we can +just copy over that data. + +This all sounds well and good but there is a final underlying important aspect. That is that some resource +like bindings may have to be stored in a buffer. For example on a CPU we could have a constant buffer that contained +another constant buffer as a field. On CPU this field would be converted into a pointer which needs to be set up. On CUDA this might be some +device specific value. So before we can copy the memory representation to a device specific buffer we must convert +any such bindings into something appropriate in the memory buffer associated with the Value. To do this we can traverse +a record of all of the bindings (which are held on the BindSet), and then set the appropriate date for the device from +data stored in the associated 'Value'. + +A final observation is that on CPU targets, the memory buffer held in the Value can just be used directly. + +NOTE: + +That these classes are written so they can be used to track locations across multiple categories such that binding +can work across many different types of targets. For the moment the mechanism/s are only tested on CPU like binding, +and there are quirks in how locations are traversed that have knowledge of how such bindings work. It may be necessary +for this to work more generally to only allow certain kinds of transitions based on some well defined specific +binding styles. +*/ + +/* A bind point records a specific binding point (typically for a category). It records a space and an offset. +As with Slangs layout reflection, the offset meaning is dependent on category. It might be an offset to +a 'register'. If category is 'uniform' it might be a memory offset. The space defines the 'space' a register +is in. +Note that m_space is ignored (but must be valid) for uniform offsets. +*/ +struct BindPoint +{ + typedef BindPoint ThisType; + + /// + bool isValid() const { return m_space >= 0; } + bool isInvalid() const { return m_space < 0; } + + void setInvalid() { m_space = -1; m_offset = 0; } + + bool operator==(const ThisType& rhs) const { return m_space == rhs.m_space && m_offset == rhs.m_offset; } + bool operator!=(const ThisType& rhs) const { return !(*this == rhs); } + + int GetHashCode() const { return Slang::combineHash(Slang::GetHashCode(m_space), Slang::GetHashCode(m_offset)); } + + BindPoint() = default; + BindPoint(Slang::Index space, size_t offset):m_space(space), m_offset(offset) {} + + static BindPoint makeInvalid() { return BindPoint(-1, 0); } + + Slang::Index m_space = 0; ///< The register space + size_t m_offset = 0; ///< The offset, might be a byte address or a register index +}; + +/* Stores the BindPoints by category. */ +struct BindPoints +{ + typedef BindPoints ThisType; + + Slang::Index findSingle() const + { + Slang::Index found; + if (calcValidCount(&found) == 1) + { + return found; + } + return -1; + } + Slang::Index calcValidCount(Slang::Index* outFoundIndex) const + { + using namespace Slang; + Index found = -1; + Index validCount = 0; + for (Index i = 0; i < Index(SLANG_PARAMETER_CATEGORY_COUNT); ++i) + { + const auto& point = m_points[i]; + if (point.isValid()) + { + found = i; + validCount++; + } + } + if (outFoundIndex) + { + *outFoundIndex = found; + } + return validCount; + } + void setInvalid() + { + for (auto& point : m_points) + { + point.setInvalid(); + } + } + + bool operator==(const ThisType& rhs) const + { + for (Slang::Index i = 0; i < SLANG_PARAMETER_CATEGORY_COUNT; ++i) + { + if (m_points[i] != rhs.m_points[i]) + { + return false; + } + } + return true; + } + bool operator!=(const ThisType& rhs) const { return !(*this == rhs); } + + int GetHashCode() const + { + int hash = 0x5435abbc; + int bits = 0; + int bit = 1; + for (Slang::Index i = 0; i < SLANG_PARAMETER_CATEGORY_COUNT; ++i, bit += bit) + { + const auto& point = m_points[i]; + if (point.isValid()) + { + hash = Slang::combineHash(hash, point.GetHashCode()); + bits |= bit; + } + } + // The categories set is important too, so merge that in + return Slang::combineHash(bits, hash); + } + + BindPoint& operator[](SlangParameterCategory category) { return m_points[Slang::Index(category)]; } + const BindPoint& operator[](SlangParameterCategory category) const { return m_points[Slang::Index(category)]; } + + BindPoint m_points[SLANG_PARAMETER_CATEGORY_COUNT]; +}; + +/* A BindPointSet is really just a reference counted 'BindPoints'. This allows for BindPoints to be shared between +multiple BindLocations if they hold the same value. */ +class BindPointSet : public Slang::RefObject +{ +public: + typedef Slang::RefObject Super; + + int GetHashCode() const { return m_points.GetHashCode(); } + + BindPointSet(const BindPoints& points) : + m_points(points) + { + } + BindPointSet() {} + + BindPoints m_points; +}; + +/* A BindSet::Value represents a 'value' associated with a binding. Typically it will be a Resource type +like a Buffer/Texture on a target device. As well as recording type information, it can also store a chunk +of memory that can hold uniform data, and may hold bindings for some kinds of targets (for example CPU pointers). +Additionally if the Value holds some kind of array, the amount of elements in the array can be stored in m_elementCount. + +All Value are constructed stored and tracked on a BindSet. When a BindSet is destroyed any associated Value will become +destroyed. +*/ +struct BindSet_Value +{ + slang::TypeReflection::Kind m_kind; ///< The kind, used if type is not set. Same as m_type.kind otherwise + slang::TypeLayoutReflection* m_type; ///< The type + uint8_t* m_data; + size_t m_sizeInBytes; ///< Total size in bytes + size_t m_elementCount; ///< Only applicable on an array like type, else 0 + + /// Can be set by user code to indicate the origin of contents/definition of a value, such that actual resource can be later constructed. + /// -1 is used to indicate it is not set. + Slang::Index m_userIndex = -1; + + Slang::RefPtr<Slang::RefObject> m_target; ///< Can be used to store data related to an actual target resource. +}; + +class BindSet; + +/* Specifies a binding location (including the associated slang reflection type information) + +It really can be in 3 type of state. +1) Invalid - not a valid binding (m_typeLayout is null, m_pointSet is not used. +2) Holds a single bind point defined by category and BindPoint m_point (m_category and m_point are used) +3) Hold multiple bind points by category (in this case m_bindPointSet is used) + +NOTE! it is an invariant - that the BindLocation must always be in the 'simplest' form that can represent it. +That is if there is only a single binding it *cannot* be stored as a m_bindPointSet with a single category + +That construction through BindPoints, will do this determination automatically. + +A BindLocation can be stored in a Hash. +*/ +struct BindLocation +{ + typedef BindLocation ThisType; + + bool isValid() const { return m_typeLayout != nullptr; } + bool isInvalid() const { return m_typeLayout == nullptr; } + + const BindPointSet* getPointSet() const { return m_bindPointSet; } + void setPoints(const BindPoints& points); + + /// Add an offset + void addOffset(SlangParameterCategory category, ptrdiff_t offset); + + /// True if holds tracking for this category + bool hasCategory(SlangParameterCategory category) const { return getBindPointForCategory(category).isValid(); } + + BindPoint getBindPointForCategory(SlangParameterCategory category) const; + BindPoint* getValidBindPointForCategory(SlangParameterCategory category); + const BindPoint* getValidBindPointForCategory(SlangParameterCategory category) const; + slang::TypeLayoutReflection* getTypeLayout() const { return m_typeLayout; } + + void setEmptyBinding() { m_bindPointSet.setNull(); m_point = BindPoint::makeInvalid(); m_category = SLANG_PARAMETER_CATEGORY_NONE; } + + template <typename T> + T* getUniform() const { return reinterpret_cast<T*>(getUniform(sizeof(T))); } + void* getUniform(size_t size) const; + + /// Set uniform data + SlangResult setUniform(const void* data, size_t sizeInBytes) const; + + bool operator==(const ThisType& rhs) const; + bool operator!=(const ThisType& rhs) const { return !(*this == rhs); } + + /// Get the hash code + int GetHashCode() const; + + /// Default Ctor - constructs as invalid + BindLocation() {} + BindLocation(slang::TypeLayoutReflection* typeLayout, const BindPoints& points, BindSet_Value* value = nullptr); + BindLocation(slang::TypeLayoutReflection* typeLayout, SlangParameterCategory category, const BindPoint& point, BindSet_Value* value = nullptr); + + BindLocation(const ThisType& rhs) = default; + + /// An invalid location. + /// Better to return this than use default Ctor as indicates validity in code directly. + static const BindLocation Invalid; + + slang::TypeLayoutReflection* m_typeLayout = nullptr; ///< The type layout + + BindSet_Value* m_value = nullptr; ///< The value if we are in one. + + SlangParameterCategory m_category = SLANG_PARAMETER_CATEGORY_NONE; ///< If there isn't a set this defines the category + BindPoint m_point; ///< If there isn't a bind point set, this defines the point + + /// Holds multiple BindPoints. + /// To keep invariants (such that GetHashCode and == work), it can only be set if + /// there is more than one category. If there is just one, m_category and m_point *MUST* be used. + /// NOTE! Can only be written to if there is a single reference. + Slang::RefPtr<BindPointSet> m_bindPointSet; +}; + +/* A BindSet holds all of the Value and bindings. It is designed to be used such that it can hold +all of the bind state needed for setting up a specific binding. + +Unfortunately it is not enough to lookup via a path for a Binding, because different targets represents the +'root' variables and values in different ways. The BindRoot interface is designed to handle this aspect. +*/ +class BindSet +{ +public: + typedef BindSet_Value Value; + + Value* getAt(const BindLocation& loc) const; + void setAt(const BindLocation& loc, Value* value); + void setAt(const BindLocation& loc, SlangParameterCategory category, Value* value); + + Value* createBufferValue(slang::TypeLayoutReflection* type, size_t sizeInBytes, const void* initialData = nullptr); + Value* createBufferValue(slang::TypeReflection::Kind kind, size_t sizeInBytes, const void* initialData = nullptr); + + Value* createTextureValue(slang::TypeLayoutReflection* type); + + /// Calculate from the current location everything that is referenced + void calcValueLocations(const BindLocation& location, Slang::List<BindLocation>& outLocations); + void calcChildResourceLocations(const BindLocation& location, Slang::List<BindLocation>& outLocations); + + void destroyValue(Value* value); + + BindLocation toField(const BindLocation& loc, slang::VariableLayoutReflection* field) const; + BindLocation toField(const BindLocation& loc, const char* name) const; + BindLocation toIndex(const BindLocation& location, Slang::Index index) const; + + SlangResult setBufferContents(const BindLocation& loc, const void* initialData, size_t sizeInBytes) const; + + /// Get all of the values + const Slang::List<Value*>& getValues() const { return m_values; } + /// Get all of the bindings + void getBindings(Slang::List<BindLocation>& outLocations, Slang::List<Value*>& outValues) const; + + /// Ctor + BindSet(); + + /// Dtor + ~BindSet(); + + /// True if is a texture type + static bool isTextureType(slang::TypeLayoutReflection* typeLayout); + +protected: + Value* _createBufferValue(slang::TypeReflection::Kind kind, slang::TypeLayoutReflection* typeLayout, size_t bufferSizeInBytes, size_t sizeInBytes, const void* initalData); + + Slang::List<Value*> m_values; + + Slang::Dictionary<BindLocation, Value*> m_bindings; + + Slang::MemoryArena m_arena; +}; + +/* BindRoot is an interface for finding the roots bindings by name. It is an interface because different targets have different ways of +representing how root values are located. +More specifically a CPU target holds the uniform and entry point variables in two buffers. +*/ +class BindRoot : public Slang::RefObject +{ +public: + typedef RefObject Super; + + typedef BindSet::Value Value; + + virtual BindLocation find(const char* name) = 0; + /// The setting of an array count is dependent on the underlying implementation. + /// On the CPU this means making sure there is a buffer that is large enough + /// And using that for storage. + /// But this does NOT set the actual location in the appropriate manner - that is + /// something that has to be done by the process that sets all the 'resource' handles etc elsewhere + virtual SlangResult setArrayCount(const BindLocation& location, int count) = 0; + + /// Find all of the roots + virtual void getRoots(Slang::List<BindLocation>& outLocations) = 0; + + /// Parse (specifying some location in HLSL style expression) slice to get to a location. + SlangResult parse(const Slang::String& text, const Slang::String& sourcePath, Slang::WriterHelper streamOut, BindLocation& outLocation); + + /// Get the bindset + BindSet* getBindSet() const { return m_bindSet; } + +protected: + + BindSet* m_bindSet = nullptr; +}; + +/* A CPULike implementation of the BindRoot. This can be used for any binding that holds +the entry point variables/uniforms in buffers. This type also stores the Value/Buffers for +the 'root', and entry point, so they can be directly accessed. +*/ +class CPULikeBindRoot : public BindRoot +{ +public: + typedef BindRoot Super; + + // BindRoot + virtual BindLocation find(const char* name) SLANG_OVERRIDE; + virtual SlangResult setArrayCount(const BindLocation& location, int count) SLANG_OVERRIDE; + virtual void getRoots(Slang::List<BindLocation>& outLocations) SLANG_OVERRIDE; + + slang::VariableLayoutReflection* getParameterByName(const char* name); + slang::VariableLayoutReflection* getEntryPointParameterByName(const char* name); + + void addDefaultValues(); + + Value* getRootValue() const { return m_rootValue; } + Value* getEntryPointValue() const { return m_entryPointValue; } + + void* getRootData() { return m_rootValue ? m_rootValue->m_data : nullptr; } + void* getEntryPointData() { return m_entryPointValue ? m_entryPointValue->m_data : nullptr; } + + SlangResult init(BindSet* bindSet, slang::ShaderReflection* reflection, int entryPointIndex); + +protected: + // Used when we have uniform buffers (as used on CPU/CUDA) + slang::ShaderReflection* m_reflection = nullptr; + Value* m_rootValue = nullptr; + Value* m_entryPointValue = nullptr; + slang::EntryPointReflection* m_entryPoint; +}; + +} // renderer_test + +#endif //BIND_LOCATION_H diff --git a/tools/render-test/cpu-compute-util.cpp b/tools/render-test/cpu-compute-util.cpp index ef5755275..e94a6d6e1 100644 --- a/tools/render-test/cpu-compute-util.cpp +++ b/tools/render-test/cpu-compute-util.cpp @@ -7,13 +7,15 @@ #include "../../source/core/slang-std-writers.h" #include "../../source/core/slang-token-reader.h" +#include "bind-location.h" + #define SLANG_PRELUDE_NAMESPACE CPPPrelude #include "../../prelude/slang-cpp-types.h" namespace renderer_test { using namespace Slang; -/* static */SlangResult CPUComputeUtil::writeBindings(const ShaderInputLayout& layout, const List<CPUMemoryBinding::Buffer>& buffers, const String& fileName) +/* static */SlangResult CPUComputeUtil::writeBindings(const ShaderInputLayout& layout, const List<BindSet::Value*>& buffers, const String& fileName) { FILE * f = fopen(fileName.getBuffer(), "wb"); if (!f) @@ -28,13 +30,13 @@ using namespace Slang; const auto& entry = entries[i]; if (entry.isOutput) { - const auto& buffer = buffers[i]; + BindSet::Value* buffer = buffers[i]; - unsigned int* ptr = (unsigned int*)buffer.m_data; + unsigned int* ptr = (unsigned int*)buffer->m_data; const int size = int(entry.bufferData.getCount()); // Must be the same size or less than allocated buffer - SLANG_ASSERT(size * sizeof(unsigned int) <= buffer.m_sizeInBytes); + SLANG_ASSERT(size * sizeof(unsigned int) <= buffer->m_sizeInBytes); for (int i = 0; i < size; ++i) { @@ -99,220 +101,200 @@ static CPUComputeUtil::Resource* _newOneTexture2D(int elemCount) const auto& sourcePath = compilationAndLayout.sourcePath; - auto& binding = outContext.binding; - - binding.init(reflection, 0); + outContext.m_bindRoot.init(&outContext.m_bindSet, reflection, 0); - auto& buffers = outContext.buffers; - buffers.clear(); + // This will set up constant buffer that are contained from the roots + outContext.m_bindRoot.addDefaultValues(); - // Okay we need to find all of the bindings and match up to those in the layout - const ShaderInputLayout& layout = compilationAndLayout.layout; + // Okay lets iterate adding buffers + auto outStream = StdWriters::getOut(); + SLANG_RETURN_ON_FAIL(ShaderInputLayout::addBindSetValues(compilationAndLayout.layout.entries, compilationAndLayout.sourcePath, outStream, outContext.m_bindRoot)); { - auto outStream = StdWriters::getOut(); - auto& entries = layout.entries; - buffers.setCount(entries.getCount()); + const auto& entries = compilationAndLayout.layout.entries; + outContext.m_buffers.setCount(entries.getCount()); - for (int entryIndex = 0; entryIndex < entries.getCount(); ++entryIndex) + const auto& values = outContext.m_bindSet.getValues(); + for (BindSet::Value* value : values) { - auto& entry = entries[entryIndex]; - - if (entry.name.getLength() == 0) + if (value->m_userIndex >= 0) { - outStream.print("No 'name' specified for resources in '%s'\n", sourcePath.getBuffer()); - return SLANG_FAIL; + outContext.m_buffers[value->m_userIndex] = value; } + } + } + + // Okay we need to find all of the bindings and match up to those in the layout + const ShaderInputLayout& layout = compilationAndLayout.layout; - // We will parse the 'name' as may be path to a resource - TokenReader parser(entry.name); + // The final stage is to actual set up the CPU based variables - CPUMemoryBinding::Location location; + { + // First create all of the resources for the values + // We don't need to create anything backed by a buffer on CPU, as the memory buffer as provided + // by BindSet::Resource can just be used + { + const auto& values = outContext.m_bindSet.getValues(); + for (BindSet::Value* value : values) { - Token nameToken = parser.ReadToken(); - if (nameToken.Type != TokenType::Identifier) + auto typeLayout = value->m_type; + if (typeLayout == nullptr) { - outStream.print("Invalid input syntax at line %d", int(parser.NextToken().Position.Line)); - return SLANG_FAIL; + // We need type layout here to create anything + continue; } - location = binding.find(nameToken.Content.getBuffer()); - if (location.isInvalid()) + + // TODO(JS): + // Here we should be using information about what textures hold to create appropriate + // textures. For now we only support 2d textures that always return 1. + const auto kind = typeLayout->getKind(); + switch (kind) { - outStream.print("Unable to find entry in '%s' for '%s' (for CPU name must be specified) \n", sourcePath.getBuffer(), entry.name.getBuffer()); - return SLANG_FAIL; - } - } + case slang::TypeReflection::Kind::Resource: + { + auto type = typeLayout->getType(); + auto shape = type->getResourceShape(); - while (!parser.IsEnd()) - { - Token token = parser.NextToken(0); + //auto access = type->getResourceAccess(); - if (token.Type == TokenType::LBracket) - { - parser.ReadToken(); - int index = parser.ReadInt(); - SLANG_ASSERT(index >= 0); + switch (shape & SLANG_RESOURCE_BASE_SHAPE_MASK) + { + case SLANG_TEXTURE_2D: + { + SLANG_ASSERT(value->m_userIndex >= 0); + auto& srcEntry = layout.entries[value->m_userIndex]; - location = location.toIndex(index); - if (location.isInvalid()) - { - outStream.print("Unable to find entry in '%d' in '%s'\n", index, entry.name.getBuffer()); - return SLANG_FAIL; - } - parser.ReadMatchingToken(TokenType::RBracket); - } - else if (token.Type == TokenType::Dot) - { - parser.ReadToken(); - Token identifierToken = parser.ReadMatchingToken(TokenType::Identifier); + // TODO(JS): + // We should use the srcEntry to determine what data to store in the texture, + // it's dimensions etc. For now we just support it being 1. - location = location.toField(identifierToken.Content.getBuffer()); - if (location.isInvalid()) - { - outStream.print("Unable to find field '%s' in '%s'\n", identifierToken.Content.getBuffer(), entry.name.getBuffer()); - return SLANG_FAIL; + slang::TypeReflection* typeReflection = typeLayout->getResourceResultType(); + + int count = 1; + if (typeReflection->getKind() == slang::TypeReflection::Kind::Vector) + { + count = int(typeReflection->getElementCount()); + } + + // TODO(JS): Should use the input setup to work how to create this texture + // Store the target specific value + value->m_target = _newOneTexture2D(count); + break; + } + case SLANG_TEXTURE_1D: + case SLANG_TEXTURE_3D: + case SLANG_TEXTURE_CUBE: + case SLANG_TEXTURE_BUFFER: + { + // Need a CPU impl for these... + // For now we can just leave as target will just be nullptr + break; + } + + case SLANG_BYTE_ADDRESS_BUFFER: + case SLANG_STRUCTURED_BUFFER: + { + // On CPU we just use the memory in the BindSet buffer, so don't need to create anything + break; + } + + } } - } - else if (token.Type == TokenType::Comma) - { - // Break out - break; - } - else - { - throw TextFormatException(String("Invalid input syntax at line ") + parser.NextToken().Position.Line); + default: break; } } + } - auto& srcEntry = layout.entries[entryIndex]; + // Now we need to go through all of the bindings and set the appropriate data + { + List<BindLocation> locations; + List<BindSet::Value*> values; + outContext.m_bindSet.getBindings(locations, values); - auto typeLayout = location.getTypeLayout(); - const auto kind = typeLayout->getKind(); - switch (kind) + for (Index i = 0; i < locations.getCount(); ++i) { - case slang::TypeReflection::Kind::Array: - { - auto elementCount = int(typeLayout->getElementCount()); - if (elementCount == 0) - { - if (srcEntry.type == ShaderInputType::Array) - { - // We need to set the size - CPUMemoryBinding::Buffer buffer; - SLANG_RETURN_ON_FAIL(binding.setArrayCount(location, srcEntry.arrayDesc.size, buffer)); - } - break; - } - SLANG_RETURN_ON_FAIL(binding.setInplace(location, srcEntry.bufferData.getBuffer(), srcEntry.bufferData.getCount() * sizeof(unsigned int))); - break; - } - case slang::TypeReflection::Kind::Vector: - case slang::TypeReflection::Kind::Matrix: - case slang::TypeReflection::Kind::Scalar: - case slang::TypeReflection::Kind::Struct: - { - SLANG_RETURN_ON_FAIL(binding.setInplace(location, srcEntry.bufferData.getBuffer(), srcEntry.bufferData.getCount() * sizeof(unsigned int))); - break; - } - default: - break; - case slang::TypeReflection::Kind::ConstantBuffer: - { - SLANG_RETURN_ON_FAIL(binding.setBufferContents(location, srcEntry.bufferData.getBuffer(), srcEntry.bufferData.getCount() * sizeof(unsigned int))); - break; - } - case slang::TypeReflection::Kind::ParameterBlock: - { - auto elementTypeLayout = typeLayout->getElementTypeLayout(); - SLANG_UNUSED(elementTypeLayout); - break; - } - case slang::TypeReflection::Kind::TextureBuffer: - { - auto elementTypeLayout = typeLayout->getElementTypeLayout(); - SLANG_UNUSED(elementTypeLayout); - break; - } - case slang::TypeReflection::Kind::ShaderStorageBuffer: - { - auto elementTypeLayout = typeLayout->getElementTypeLayout(); - SLANG_UNUSED(elementTypeLayout); - break; - } - case slang::TypeReflection::Kind::GenericTypeParameter: - { - const char* name = typeLayout->getName(); - SLANG_UNUSED(name); - break; - } - case slang::TypeReflection::Kind::Interface: - { - const char* name = typeLayout->getName(); - SLANG_UNUSED(name); - break; - } - case slang::TypeReflection::Kind::Resource: - { - auto type = typeLayout->getType(); - auto shape = type->getResourceShape(); + const auto& location = locations[i]; + BindSet::Value* value = values[i]; - //auto access = type->getResourceAccess(); + // Okay now we need to set up the actual handles that CPU will follow. + auto typeLayout = location.getTypeLayout(); - switch (shape & SLANG_RESOURCE_BASE_SHAPE_MASK) + const auto kind = typeLayout->getKind(); + switch (kind) + { + case slang::TypeReflection::Kind::Array: { - default: - assert(!"unhandled case"); - break; - case SLANG_TEXTURE_2D: + auto elementCount = int(typeLayout->getElementCount()); + if (elementCount == 0) { - slang::TypeReflection* typeReflection = location.getTypeLayout()->getResourceResultType(); + CPPPrelude::Array<uint8_t>* array = location.getUniform<CPPPrelude::Array<uint8_t> >(); - int count = 1; - if (typeReflection->getKind() == slang::TypeReflection::Kind::Vector) + // If set, we setup the data needed for array on CPU side + if (value && array) { - count = int(typeReflection->getElementCount()); + array->data = value->m_data; + array->count = value->m_elementCount; } - - RefPtr<Resource> resource = _newOneTexture2D(count); - outContext.m_resources.add(resource); - - SLANG_RETURN_ON_FAIL(binding.setObject(location, resource->getInterface())); - break; - } - case SLANG_TEXTURE_1D: - case SLANG_TEXTURE_3D: - case SLANG_TEXTURE_CUBE: - case SLANG_TEXTURE_BUFFER: - { - // Just set to null for now - SLANG_RETURN_ON_FAIL(binding.setObject(location, nullptr)); - break; - } - case SLANG_BYTE_ADDRESS_BUFFER: - case SLANG_STRUCTURED_BUFFER: - { - CPUMemoryBinding::Buffer buffer; - SLANG_RETURN_ON_FAIL(binding.setNewBuffer(location, srcEntry.bufferData.getBuffer(), srcEntry.bufferData.getCount() * sizeof(unsigned int), buffer)); - buffers[entryIndex] = buffer; - break; } + break; } - if (shape & SLANG_TEXTURE_ARRAY_FLAG) + case slang::TypeReflection::Kind::ConstantBuffer: + case slang::TypeReflection::Kind::ParameterBlock: { - + // These map down to pointers. In our case the contents of the resource + void* data = value ? value->m_data : nullptr; + location.setUniform(&data, sizeof(data)); + break; } - if (shape & SLANG_TEXTURE_MULTISAMPLE_FLAG) + case slang::TypeReflection::Kind::Resource: { + auto type = typeLayout->getType(); + auto shape = type->getResourceShape(); - } + //auto access = type->getResourceAccess(); - break; + switch (shape & SLANG_RESOURCE_BASE_SHAPE_MASK) + { + default: + assert(!"unhandled case"); + break; + case SLANG_TEXTURE_1D: + case SLANG_TEXTURE_3D: + case SLANG_TEXTURE_CUBE: + case SLANG_TEXTURE_BUFFER: + case SLANG_TEXTURE_2D: + { + Resource* targetResource = value ? static_cast<Resource*>(value->m_target.Ptr()) : nullptr; + *location.getUniform<void*>() = targetResource ? targetResource->getInterface() : nullptr; + break; + } + case SLANG_STRUCTURED_BUFFER: + { + if (value) + { + auto& dstBuf = *location.getUniform<CPPPrelude::StructuredBuffer<uint8_t> >(); + dstBuf.data = (uint8_t*)value->m_data; + dstBuf.count = value->m_elementCount; + } + break; + } + case SLANG_BYTE_ADDRESS_BUFFER: + { + if (value) + { + auto& dstBuf = *location.getUniform<CPPPrelude::ByteAddressBuffer>(); + dstBuf.data = (uint32_t*)value->m_data; + dstBuf.sizeInBytes = value->m_sizeInBytes; + } + break; + } + } + } } } } } - return SLANG_OK; } @@ -336,8 +318,8 @@ static CPUComputeUtil::Resource* _newOneTexture2D(int elemCount) } out.m_style = style; - out.m_uniformState = (void*)context.binding.m_rootBuffer.m_data; - out.m_uniformEntryPointParams = (void*)context.binding.m_entryPointBuffer.m_data; + out.m_uniformState = (void*)context.m_bindRoot.getRootData(); + out.m_uniformEntryPointParams = (void*)context.m_bindRoot.getEntryPointData(); switch (style) { @@ -513,11 +495,11 @@ static CPUComputeUtil::Resource* _newOneTexture2D(int elemCount) const auto& entry = entries[i]; if (entry.isOutput) { - const auto& buffer = context.buffers[i]; - const auto& checkBuffer = checkContext.buffers[i]; + BindSet::Value* buffer = context.m_buffers[i]; + BindSet::Value* checkBuffer = checkContext.m_buffers[i]; - if (buffer.m_sizeInBytes != checkBuffer.m_sizeInBytes || - memcmp(buffer.m_data, checkBuffer.m_data, buffer.m_sizeInBytes) != 0) + if (buffer->m_sizeInBytes != checkBuffer->m_sizeInBytes || + ::memcmp(buffer->m_data, checkBuffer->m_data, buffer->m_sizeInBytes) != 0) { return SLANG_FAIL; } diff --git a/tools/render-test/cpu-compute-util.h b/tools/render-test/cpu-compute-util.h index 179985f6f..21c40ba43 100644 --- a/tools/render-test/cpu-compute-util.h +++ b/tools/render-test/cpu-compute-util.h @@ -1,10 +1,11 @@ #ifndef CPU_COMPUTE_UTIL_H #define CPU_COMPUTE_UTIL_H -#include "cpu-memory-binding.h" #include "slang-support.h" #include "options.h" +#include "bind-location.h" + #include "../../source/core/slang-smart-pointer.h" namespace renderer_test { @@ -28,11 +29,11 @@ struct CPUComputeUtil struct Context { /// Holds the binding information - CPUMemoryBinding binding; + BindSet m_bindSet; + CPULikeBindRoot m_bindRoot; + /// Buffers are held in same order as entries in layout (useful for dumping out bindings) - List<CPUMemoryBinding::Buffer> buffers; - /// Holds the resources created (in calcBindings) - List<RefPtr<Resource> > m_resources; + List<BindSet::Value*> m_buffers; }; struct ExecuteInfo @@ -53,12 +54,12 @@ struct CPUComputeUtil static SlangResult checkStyleConsistency(ISlangSharedLibrary* sharedLib, const uint32_t dispatchSize[3], const ShaderCompilerUtil::OutputAndLayout& compilationAndLayout); static SlangResult calcBindings(const ShaderCompilerUtil::OutputAndLayout& compilationAndLayout, Context& outContext); - + static SlangResult calcExecuteInfo(ExecuteStyle style, ISlangSharedLibrary* sharedLib, const uint32_t dispatchSize[3], const ShaderCompilerUtil::OutputAndLayout& compilationAndLayout, Context& context, ExecuteInfo& out); static SlangResult execute(const ExecuteInfo& info); - static SlangResult writeBindings(const ShaderInputLayout& layout, const List<CPUMemoryBinding::Buffer>& buffers, const Slang::String& fileName); + static SlangResult writeBindings(const ShaderInputLayout& layout, const List<BindSet::Value*>& buffers, const Slang::String& fileName); }; diff --git a/tools/render-test/cpu-memory-binding.cpp b/tools/render-test/cpu-memory-binding.cpp deleted file mode 100644 index be4ad96e7..000000000 --- a/tools/render-test/cpu-memory-binding.cpp +++ /dev/null @@ -1,641 +0,0 @@ - -#include "cpu-memory-binding.h" - -#include "../../slang-com-helper.h" - -#define SLANG_PRELUDE_NAMESPACE CPPPrelude -#include "../../prelude/slang-cpp-types.h" - -namespace renderer_test { -using namespace Slang; - -CPUMemoryBinding::CPUMemoryBinding() -{ - m_arena.init(1024, 16); -} - -CPUMemoryBinding::Buffer CPUMemoryBinding::_allocateBuffer(size_t size) -{ - Buffer buffer; - - // Use 16 byte alignment so works for all common types including typical simd - void* data = m_arena.allocateAligned(size, 16); - ::memset(data, 0, size); - - buffer.m_data = (uint8_t*)data; - buffer.m_sizeInBytes = size; - - m_allBuffers.add(buffer); - return buffer; -} - -CPUMemoryBinding::Buffer CPUMemoryBinding::_allocateBuffer(size_t size, const void* initialData, size_t initialSize) -{ - SLANG_ASSERT(initialSize <= size); - Buffer buffer = _allocateBuffer(size); - - if (initialData) - { - memcpy(buffer.m_data, initialData, initialSize); - } - - return buffer; -} - -SlangResult CPUMemoryBinding::init(slang::ShaderReflection* reflection, int entryPointIndex) -{ - m_reflection = reflection; - m_rootBuffer = Buffer(); - m_entryPointBuffer = Buffer(); - - m_allBuffers.clear(); - m_arena.deallocateAll(); - - { - size_t globalConstantBuffer = reflection->getGlobalConstantBufferSize(); - - size_t rootSizeInBytes = 0; - const int parameterCount = reflection->getParameterCount(); - for (int i = 0; i < parameterCount; ++i) - { - auto parameter = reflection->getParameterByIndex(i); - - auto offset = parameter->getOffset(); - - auto typeLayout = parameter->getTypeLayout(); - auto sizeInBytes = typeLayout->getSize(); - - size_t endOffset = offset + sizeInBytes; - - rootSizeInBytes = (endOffset > rootSizeInBytes) ? endOffset : rootSizeInBytes; - } - SLANG_ASSERT(rootSizeInBytes == globalConstantBuffer); - - if (rootSizeInBytes) - { - // Allocate the 'root' buffer - m_rootBuffer = _allocateBuffer(rootSizeInBytes); - - // Create default empty constant buffers - uint8_t*const buffer = m_rootBuffer.m_data; - for (int i = 0; i < parameterCount; ++i) - { - auto parameter = reflection->getParameterByIndex(i); - auto offset = parameter->getOffset(); - - auto typeLayout = parameter->getTypeLayout(); - Buffer paramBuffer; - SLANG_RETURN_ON_FAIL(_add(parameter, typeLayout, buffer + offset, paramBuffer)); - } - } - } - - { - auto entryPointCount = int(reflection->getEntryPointCount()); - if (entryPointIndex < 0 || entryPointIndex >= entryPointCount) - { - SLANG_ASSERT(!"Entry point index out of range"); - return SLANG_FAIL; - } - - m_entryPoint = reflection->getEntryPointByIndex(entryPointIndex); - size_t entryPointParamsSizeInBytes = 0; - - const int parameterCount = int(m_entryPoint->getParameterCount()); - for (int i = 0 ; i < parameterCount; i++) - { - slang::VariableLayoutReflection* parameter = m_entryPoint->getParameterByIndex(i); - - // If has a semantic, then isn't uniform parameter - if (auto semanticName = parameter->getSemanticName()) - { - continue; - } - - auto offset = parameter->getOffset(); - - auto typeLayout = parameter->getTypeLayout(); - auto sizeInBytes = typeLayout->getSize(); - - size_t endOffset = offset + sizeInBytes; - entryPointParamsSizeInBytes = (endOffset > entryPointParamsSizeInBytes) ? endOffset : entryPointParamsSizeInBytes; - } - - if (entryPointParamsSizeInBytes) - { - m_entryPointBuffer = _allocateBuffer(entryPointParamsSizeInBytes); - - uint8_t*const buffer = m_entryPointBuffer.m_data; - for (int i = 0; i < parameterCount; ++i) - { - auto parameter = m_entryPoint->getParameterByIndex(i); - // If has a semantic, then isn't uniform parameter - if (auto semanticName = parameter->getSemanticName()) - { - continue; - } - - auto offset = parameter->getOffset(); - - auto typeLayout = parameter->getTypeLayout(); - Buffer paramBuffer; - SLANG_RETURN_ON_FAIL(_add(parameter, typeLayout, buffer + offset, paramBuffer)); - } - } - } - - return SLANG_OK; -} - -SlangResult CPUMemoryBinding::_add(slang::VariableLayoutReflection* varLayout, slang::TypeLayoutReflection* typeLayout, void* dst, Buffer& outBuffer) -{ - const auto kind = typeLayout->getKind(); - switch (kind) - { - case slang::TypeReflection::Kind::Array: - { - auto elementTypeLayout = typeLayout->getElementTypeLayout(); - auto elementCount = int(typeLayout->getElementCount()); - - if (elementCount == 0) - { - // We don't currently know the size that this array will be. So let's initially size it to 0. - - CPPPrelude::Array<uint8_t>& dstBuf = *(CPPPrelude::Array<uint8_t>*)dst; - dstBuf.data = nullptr; - dstBuf.count = 0; - } - else - { - const size_t stride = elementTypeLayout->getSize(); - uint8_t* cur = (uint8_t*)dst; - for (int i = 0; i < elementCount; ++i) - { - Buffer elementBuffer; - _add(nullptr, elementTypeLayout, cur, elementBuffer); - cur += stride; - } - } - break; - } - case slang::TypeReflection::Kind::Struct: - { - auto structTypeLayout = typeLayout; - //auto name = structTypeLayout->getName(); - //SLANG_UNUSED(name); - - auto fieldCount = structTypeLayout->getFieldCount(); - for (uint32_t ff = 0; ff < fieldCount; ++ff) - { - auto field = structTypeLayout->getFieldByIndex(ff); - auto offset = field->getOffset(); - - Buffer fieldBuffer; - SLANG_RETURN_ON_FAIL(_add(nullptr, field->getTypeLayout(), ((uint8_t*)dst) + offset, fieldBuffer)); - } - break; - } - case slang::TypeReflection::Kind::ParameterBlock: - case slang::TypeReflection::Kind::ConstantBuffer: - { - SLANG_ASSERT(typeLayout->getSize() == sizeof(void*)); - - auto elementTypeLayout = typeLayout->getElementTypeLayout(); - const size_t elementSize = elementTypeLayout->getSize(); - - outBuffer = _allocateBuffer(elementSize); - - // Constant buffers map to a pointer - *(void**)dst = outBuffer.m_data; - - // On CPU constant buffers can contain pointers to other resources (including constant buffers) - Buffer innerBuffer; - SLANG_RETURN_ON_FAIL(_add(nullptr, elementTypeLayout, outBuffer.m_data, innerBuffer)); - break; - } - default: break; - } - return SLANG_OK; -} - -slang::VariableLayoutReflection* CPUMemoryBinding::getParameterByName(const char* name) -{ - const int parameterCount = m_reflection->getParameterCount(); - for (int i = 0; i < parameterCount; ++i) - { - auto parameter = m_reflection->getParameterByIndex(i); - const char* paramName = parameter->getName(); - if (strcmp(name, paramName) == 0) - { - return parameter; - } - } - - return nullptr; -} - -slang::VariableLayoutReflection* CPUMemoryBinding::getEntryPointParameterByName(const char* name) -{ - const int parameterCount = int(m_entryPoint->getParameterCount()); - for (int i = 0; i < parameterCount; ++i) - { - auto parameter = m_entryPoint->getParameterByIndex(i); - // If has a semantic we will ignore - if (parameter->getSemanticName()) - { - continue; - } - if (strcmp(parameter->getName(), name) == 0) - { - return parameter; - } - } - return nullptr; -} - -CPUMemoryBinding::Location CPUMemoryBinding::find(const char* name) -{ - auto varLayout = getParameterByName(name); - if (varLayout) - { - return Location(varLayout->getTypeLayout(), m_rootBuffer.m_data + varLayout->getOffset()); - } - - varLayout = getEntryPointParameterByName(name); - if (varLayout) - { - return Location(varLayout->getTypeLayout(), m_entryPointBuffer.m_data + varLayout->getOffset()); - } - return Location(); -} - -CPUMemoryBinding::Location CPUMemoryBinding::Location::toField(const char* name) const -{ - if (!isValid()) - { - return *this; - } - - auto typeLayout = m_typeLayout; - uint8_t* cur = m_cur; - - // Strip constantBuffer wrapping - { - const auto kind = typeLayout->getKind(); - if (kind == slang::TypeReflection::Kind::ConstantBuffer || - kind == slang::TypeReflection::Kind::ParameterBlock) - { - // Follow the pointer - cur = *(uint8_t**)cur; - typeLayout = typeLayout->getElementTypeLayout(); - } - } - - { - const auto kind = typeLayout->getKind(); - if (kind == slang::TypeReflection::Kind::Struct) - { - slang::VariableLayoutReflection* varLayout = nullptr; - auto fieldCount = typeLayout->getFieldCount(); - for (uint32_t ff = 0; ff < fieldCount; ++ff) - { - auto field = typeLayout->getFieldByIndex(ff); - if (strcmp(field->getName(), name) == 0) - { - return Location(field->getTypeLayout(), cur + field->getOffset()); - } - } - } - } - - return Location(); -} - -CPUMemoryBinding::Location CPUMemoryBinding::Location::toIndex(int index) const -{ - if (!isValid()) - { - return *this; - } - SLANG_ASSERT(index >= 0); - if (index < 0) - { - return Location(); - } - - auto typeLayout = m_typeLayout; - uint8_t* cur = m_cur; - - const auto kind = typeLayout->getKind(); - switch (kind) - { - case slang::TypeReflection::Kind::Array: - { - auto elementTypeLayout = typeLayout->getElementTypeLayout(); - const auto elementCount = int(typeLayout->getElementCount()); - const auto elementStride = typeLayout->getElementStride(SLANG_PARAMETER_CATEGORY_UNIFORM); - - if (elementCount == 0) - { - CPPPrelude::Array<uint8_t>& array = *(CPPPrelude::Array<uint8_t>*)cur; - if (index < int(array.count)) - { - return Location(elementTypeLayout, array.data + elementStride * index); - } - return Location(); - } - else - { - if (index >= elementCount) - { - SLANG_ASSERT(index < elementCount); - return Location(); - } - return Location(elementTypeLayout, cur + elementStride * index); - } - } - default: break; - } - - return Location(); -} - -SlangResult CPUMemoryBinding::initValue(slang::TypeLayoutReflection* typeLayout, void* dst) -{ - auto size = typeLayout->getSize(); - // Zeroing works for built in types, and object types - memset(dst, 0, size); - return SLANG_OK; -} - -SlangResult CPUMemoryBinding::setBufferContents(const Location& location, const void* initialData, size_t sizeInBytes) -{ - if (!location.isValid()) - { - return SLANG_FAIL; - } - - auto typeLayout = location.getTypeLayout(); - uint8_t* cur = location.getPtr(); - - const auto kind = typeLayout->getKind(); - switch (kind) - { - case slang::TypeReflection::Kind::ParameterBlock: - case slang::TypeReflection::Kind::ConstantBuffer: - { - typeLayout = typeLayout->getElementTypeLayout(); - - size_t bufferSize = typeLayout->getSize(); - sizeInBytes = (sizeInBytes > bufferSize) ? bufferSize : sizeInBytes; - - void* buffer = *(void**)cur; - memcpy(buffer, initialData, sizeInBytes); - return SLANG_OK; - } - default: break; - } - return SLANG_FAIL; -} - -SlangResult CPUMemoryBinding::setNewBuffer(const Location& location, const void* initialData, size_t sizeInBytes, Buffer& outBuffer) -{ - if (!location.isValid()) - { - return SLANG_FAIL; - } - - auto typeLayout = location.getTypeLayout(); - uint8_t* cur = location.getPtr(); - - const auto kind = typeLayout->getKind(); - switch (kind) - { - case slang::TypeReflection::Kind::ParameterBlock: - case slang::TypeReflection::Kind::ConstantBuffer: - { - // All should be allocated (!) - SLANG_ASSERT(typeLayout->getSize() == sizeof(void*)); - SLANG_ASSERT(*(void**)cur); - return SLANG_FAIL; - } - case slang::TypeReflection::Kind::Resource: - { - auto type = typeLayout->getType(); - auto shape = type->getResourceShape(); - - switch (shape & SLANG_RESOURCE_BASE_SHAPE_MASK) - { - case SLANG_STRUCTURED_BUFFER: - { - auto elementTypeLayout = typeLayout->getElementTypeLayout(); - size_t elementSize = elementTypeLayout->getSize(SLANG_PARAMETER_CATEGORY_UNIFORM); - - // We don't know the size of the buffer, but we can work it out, based on what is initialized - const int numElements = int((sizeInBytes + elementSize - 1) / elementSize); - - const size_t bufferSize = numElements * elementSize; - SLANG_ASSERT(bufferSize >= sizeInBytes); - - outBuffer = _allocateBuffer(bufferSize, initialData, sizeInBytes); - - CPPPrelude::StructuredBuffer<uint8_t>& dstBuf = *(CPPPrelude::StructuredBuffer<uint8_t>*)cur; - dstBuf.data = (uint8_t*)outBuffer.m_data; - dstBuf.count = numElements; - return SLANG_OK; - } - case SLANG_BYTE_ADDRESS_BUFFER: - { - const size_t bufferSize = (sizeInBytes + 3) & ~size_t(3); - - outBuffer = _allocateBuffer(bufferSize, initialData, sizeInBytes); - - CPPPrelude::ByteAddressBuffer& dstBuf = *(CPPPrelude::ByteAddressBuffer*)cur; - dstBuf.data = (uint32_t*)outBuffer.m_data; - dstBuf.sizeInBytes = bufferSize; - return SLANG_OK; - } - } - break; - } - default: break; - } - - return SLANG_FAIL; -} - -SlangResult CPUMemoryBinding::setObject(const Location& location, void* object) -{ - if (!location.isValid()) - { - return SLANG_FAIL; - } - - auto typeLayout = location.getTypeLayout(); - uint8_t* cur = location.getPtr(); - - const auto kind = typeLayout->getKind(); - switch (kind) - { - default: - break; - - case slang::TypeReflection::Kind::TextureBuffer: - { - auto elementTypeLayout = typeLayout->getElementTypeLayout(); - SLANG_UNUSED(elementTypeLayout); - break; - } - case slang::TypeReflection::Kind::ShaderStorageBuffer: - { - auto elementTypeLayout = typeLayout->getElementTypeLayout(); - SLANG_UNUSED(elementTypeLayout); - break; - } - case slang::TypeReflection::Kind::GenericTypeParameter: - { - const char* name = typeLayout->getName(); - SLANG_UNUSED(name); - break; - } - case slang::TypeReflection::Kind::Interface: - { - const char* name = typeLayout->getName(); - SLANG_UNUSED(name); - break; - } - case slang::TypeReflection::Kind::Resource: - { - auto type = typeLayout->getType(); - auto shape = type->getResourceShape(); - - //auto access = type->getResourceAccess(); - - switch (shape & SLANG_RESOURCE_BASE_SHAPE_MASK) - { - default: - assert(!"unhandled case"); - break; - case SLANG_TEXTURE_1D: - case SLANG_TEXTURE_2D: - case SLANG_TEXTURE_3D: - case SLANG_TEXTURE_CUBE: - case SLANG_TEXTURE_BUFFER: - { - *(void**)cur = object; - return SLANG_OK; - } - } - if (shape & SLANG_TEXTURE_ARRAY_FLAG) - { - - } - if (shape & SLANG_TEXTURE_MULTISAMPLE_FLAG) - { - - } - - break; - } - } - - return SLANG_FAIL; -} - -SlangResult CPUMemoryBinding::setInplace(const Location& location, const void* data, size_t sizeInBytes) -{ - if (!location.isValid()) - { - return SLANG_FAIL; - } - - size_t dstSize = location.getTypeLayout()->getSize(); - sizeInBytes = (sizeInBytes > dstSize) ? dstSize : sizeInBytes; - memcpy(location.getPtr(), data, sizeInBytes); - return SLANG_OK; -} - -Index CPUMemoryBinding::findBufferIndex(const void* ptr) const -{ - const Index count = m_allBuffers.getCount(); - - for (Index i = 0; i < count; ++i) - { - if (m_allBuffers[i].m_data == ptr) - { - return i; - } - } - return -1; -} - -SlangResult CPUMemoryBinding::setArrayCount(const Location& location, int count, Buffer& outBuffer) -{ - if (!location.isValid()) - { - return SLANG_FAIL; - } - - auto typeLayout = location.getTypeLayout(); - uint8_t* cur = location.getPtr(); - - const auto kind = typeLayout->getKind(); - - if (!(typeLayout->getKind() == slang::TypeReflection::Kind::Array && typeLayout->getElementCount() == 0)) - { - return SLANG_FAIL; - } - - CPPPrelude::Array<uint8_t>& array = *(CPPPrelude::Array<uint8_t>*)cur; - uint8_t* elements = array.data; - - // Making smaller, just reduce the count. - // NOTE! Nothing is done here about deallocating resources which are perhaps no longer reachable. - // This isn't a leakage problem tho, as all buffers are released automatically when scope is left. - if (count <= int(array.count)) - { - array.count = count; - return SLANG_OK; - } - - const size_t elementStride = typeLayout->getElementStride(SLANG_PARAMETER_CATEGORY_UNIFORM); - - const Index bufferIndex = elements ? findBufferIndex(elements) : -1; - if (bufferIndex > 0) - { - int maxCount = int(m_allBuffers[bufferIndex].m_sizeInBytes / elementStride); - if (count <= maxCount) - { - // Just initialize the space - memset(elements + elementStride * array.count, 0, (count - array.count) * elementStride); - array.count = count; - return SLANG_OK; - } - } - - // Ok allocate a buffer that can hold all the elements - - const size_t newBufferSize = count * elementStride; - Buffer newBuffer = _allocateBuffer(newBufferSize); - - // Copy over the data from the old buffer if there is any - if (elements && array.count) - { - memcpy(newBuffer.m_data, elements, array.count * elementStride); - } - - // Remove the old buffer as no longer needed - if (bufferIndex >= 0) - { - m_allBuffers.removeAt(bufferIndex); - } - - // Set data - array.count = count; - array.data = newBuffer.m_data; - - outBuffer = newBuffer; - return SLANG_OK; -} - -} // renderer_test diff --git a/tools/render-test/cpu-memory-binding.h b/tools/render-test/cpu-memory-binding.h deleted file mode 100644 index 8ca8e0681..000000000 --- a/tools/render-test/cpu-memory-binding.h +++ /dev/null @@ -1,88 +0,0 @@ -#ifndef CPU_MEMORY_BINDING_H -#define CPU_MEMORY_BINDING_H - -#include "core/slang-basic.h" - -#include "core/slang-memory-arena.h" - -namespace renderer_test { - -struct CPUMemoryBinding -{ - struct Buffer - { - Buffer() : m_data(nullptr), m_sizeInBytes(0) {} - uint8_t* m_data; - size_t m_sizeInBytes; - }; - - struct Location - { - bool isValid() const { return m_cur != nullptr; } - bool isInvalid() const { return m_cur == nullptr; } - - Location toField(const char* name) const; - Location toIndex(int index) const; - - slang::TypeLayoutReflection* getTypeLayout() const { return m_typeLayout; } - uint8_t* getPtr() const { return m_cur; } - - SLANG_FORCE_INLINE Location():m_typeLayout(nullptr), m_cur(nullptr) {} - - SLANG_FORCE_INLINE Location(slang::TypeLayoutReflection* typeLayout, uint8_t* ptr): - m_typeLayout(typeLayout), - m_cur(ptr) - { - } - - protected: - slang::TypeLayoutReflection* m_typeLayout; - uint8_t* m_cur; - }; - - slang::VariableLayoutReflection* getParameterByName(const char* name); - slang::VariableLayoutReflection* getEntryPointParameterByName(const char* name); - - /// Finds which buffer starts at the ptr index - Slang::Index findBufferIndex(const void* ptr) const; - - Location find(const char* name); - - SlangResult setBufferContents(const Location& location, const void* initialData, size_t sizeInBytes); - SlangResult setNewBuffer(const Location& location, const void* initialData, size_t sizeInBytes, Buffer& outBuffer); - SlangResult setObject(const Location& location, void* object); - SlangResult setInplace(const Location& location, const void* data, size_t sizeInBytes); - /// Initialize memory with a 'sensible' value based on type. Pointer types become null. - SlangResult initValue(slang::TypeLayoutReflection* typeLayout, void* dst); - SlangResult initValue(const Location& location) { return initValue(location.getTypeLayout(), location.getPtr()); } - - /// Set the size of a *non fixed size* array at location. - /// A non fixed size array is reflected as having a count of 0 elements. - /// Only returns a buffer in outBuffer if a new buffer is created. - SlangResult setArrayCount(const Location& location, int count, Buffer& outBuffer); - - SlangResult init(slang::ShaderReflection* reflection, int entryPointIndex); - CPUMemoryBinding(); - - Buffer _allocateBuffer(size_t size); - Buffer _allocateBuffer(size_t size, const void* initialData, size_t initialSize); - - SlangResult _add(slang::VariableLayoutReflection* var, slang::TypeLayoutReflection* type, void* dst, Buffer& outBuffer); - - Slang::MemoryArena m_arena; ///< Storage for buffers - - Buffer m_rootBuffer; - Buffer m_entryPointBuffer; - - slang::ShaderReflection* m_reflection; - - // All buffers - Slang::List<Buffer> m_allBuffers; - - slang::EntryPointReflection* m_entryPoint; - int m_entryPointIndex; -}; - -} // renderer_test - -#endif //CPU_MEMORY_BINDING_H diff --git a/tools/render-test/render-test-main.cpp b/tools/render-test/render-test-main.cpp index a110b6ca9..d91592ccf 100644 --- a/tools/render-test/render-test-main.cpp +++ b/tools/render-test/render-test-main.cpp @@ -545,6 +545,11 @@ SLANG_TEST_TOOL_API SlangResult innerMain(Slang::StdWriters* stdWriters, SlangSe ComPtr<ISlangSharedLibrary> sharedLibrary; SLANG_RETURN_ON_FAIL(spGetEntryPointHostCallable(compilationAndLayout.output.request, 0, 0, sharedLibrary.writeRef())); + // This is a hack to work around, reflection when compiling straight C/C++ code. In that case the code is just passed + // straight through to the C++ compiler so no reflection. In these tests though we should have conditional code + // (performance-profile.slang for example), such that there is both a slang and C++ code, and it is the job + // of the test implementer to *ensure* that the straight C++ code has the same layout as the slang C++ backend. + // // If we are running c/c++ we still need binding information, so compile again as slang source if (gOptions.sourceLanguage == SLANG_SOURCE_LANGUAGE_C || input.sourceLanguage == SLANG_SOURCE_LANGUAGE_CPP) { @@ -578,7 +583,7 @@ SLANG_TEST_TOOL_API SlangResult innerMain(Slang::StdWriters* stdWriters, SlangSe if (gOptions.outputPath) { // Dump everything out that was written - SLANG_RETURN_ON_FAIL(CPUComputeUtil::writeBindings(compilationAndLayout.layout, context.buffers, gOptions.outputPath)); + SLANG_RETURN_ON_FAIL(CPUComputeUtil::writeBindings(compilationAndLayout.layout, context.m_buffers, gOptions.outputPath)); // Check all execution styles produce the same result SLANG_RETURN_ON_FAIL(CPUComputeUtil::checkStyleConsistency(sharedLibrary, gOptions.computeDispatchSize, compilationAndLayout)); diff --git a/tools/render-test/render-test-tool.vcxproj b/tools/render-test/render-test-tool.vcxproj index 3cc813300..cd05a53a2 100644 --- a/tools/render-test/render-test-tool.vcxproj +++ b/tools/render-test/render-test-tool.vcxproj @@ -179,8 +179,8 @@ </PostBuildEvent> </ItemDefinitionGroup> <ItemGroup> + <ClInclude Include="bind-location.h" /> <ClInclude Include="cpu-compute-util.h" /> - <ClInclude Include="cpu-memory-binding.h" /> <ClInclude Include="options.h" /> <ClInclude Include="png-serialize-util.h" /> <ClInclude Include="shader-input-layout.h" /> @@ -189,8 +189,8 @@ <ClInclude Include="window.h" /> </ItemGroup> <ItemGroup> + <ClCompile Include="bind-location.cpp" /> <ClCompile Include="cpu-compute-util.cpp" /> - <ClCompile Include="cpu-memory-binding.cpp" /> <ClCompile Include="options.cpp" /> <ClCompile Include="png-serialize-util.cpp" /> <ClCompile Include="render-test-main.cpp" /> diff --git a/tools/render-test/render-test-tool.vcxproj.filters b/tools/render-test/render-test-tool.vcxproj.filters index 7091fce6b..36b384bb0 100644 --- a/tools/render-test/render-test-tool.vcxproj.filters +++ b/tools/render-test/render-test-tool.vcxproj.filters @@ -9,10 +9,10 @@ </Filter> </ItemGroup> <ItemGroup> - <ClInclude Include="cpu-compute-util.h"> + <ClInclude Include="bind-location.h"> <Filter>Header Files</Filter> </ClInclude> - <ClInclude Include="cpu-memory-binding.h"> + <ClInclude Include="cpu-compute-util.h"> <Filter>Header Files</Filter> </ClInclude> <ClInclude Include="options.h"> @@ -35,10 +35,10 @@ </ClInclude> </ItemGroup> <ItemGroup> - <ClCompile Include="cpu-compute-util.cpp"> + <ClCompile Include="bind-location.cpp"> <Filter>Source Files</Filter> </ClCompile> - <ClCompile Include="cpu-memory-binding.cpp"> + <ClCompile Include="cpu-compute-util.cpp"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="options.cpp"> diff --git a/tools/render-test/shader-input-layout.cpp b/tools/render-test/shader-input-layout.cpp index 7a9001c9e..5ae35b90d 100644 --- a/tools/render-test/shader-input-layout.cpp +++ b/tools/render-test/shader-input-layout.cpp @@ -541,6 +541,141 @@ namespace renderer_test } } + /* static */SlangResult ShaderInputLayout::addBindSetValues(const Slang::List<ShaderInputLayoutEntry>& entries, const String& sourcePath, WriterHelper outStream, BindRoot& bindRoot) + { + BindSet* bindSet = bindRoot.getBindSet(); + SLANG_ASSERT(bindSet); + + for (Index entryIndex = 0; entryIndex < entries.getCount(); ++entryIndex) + { + auto& entry = entries[entryIndex]; + + if (entry.name.getLength() == 0) + { + outStream.print("No 'name' specified for value in '%s'\n", sourcePath.getBuffer()); + return SLANG_FAIL; + } + + BindLocation location = BindLocation::Invalid; + SLANG_RETURN_ON_FAIL(bindRoot.parse(entry.name, sourcePath, outStream, location)); + + auto& srcEntry = entries[entryIndex]; + + auto typeLayout = location.getTypeLayout(); + const auto kind = typeLayout->getKind(); + switch (kind) + { + case slang::TypeReflection::Kind::Array: + { + auto elementCount = int(typeLayout->getElementCount()); + if (elementCount == 0) + { + if (srcEntry.type == ShaderInputType::Array) + { + // Set the size + SLANG_RETURN_ON_FAIL(bindRoot.setArrayCount(location, srcEntry.arrayDesc.size)); + } + break; + } + break; + } + case slang::TypeReflection::Kind::Vector: + case slang::TypeReflection::Kind::Matrix: + case slang::TypeReflection::Kind::Scalar: + case slang::TypeReflection::Kind::Struct: + { + SLANG_RETURN_ON_FAIL(location.setUniform(srcEntry.bufferData.getBuffer(), srcEntry.bufferData.getCount() * sizeof(unsigned int))); + break; + } + default: + break; + case slang::TypeReflection::Kind::ConstantBuffer: + { + SLANG_RETURN_ON_FAIL(bindSet->setBufferContents(location, srcEntry.bufferData.getBuffer(), srcEntry.bufferData.getCount() * sizeof(unsigned int))); + break; + } + case slang::TypeReflection::Kind::ParameterBlock: + { + auto elementTypeLayout = typeLayout->getElementTypeLayout(); + SLANG_UNUSED(elementTypeLayout); + break; + } + case slang::TypeReflection::Kind::TextureBuffer: + { + auto elementTypeLayout = typeLayout->getElementTypeLayout(); + SLANG_UNUSED(elementTypeLayout); + break; + } + case slang::TypeReflection::Kind::ShaderStorageBuffer: + { + auto elementTypeLayout = typeLayout->getElementTypeLayout(); + SLANG_UNUSED(elementTypeLayout); + break; + } + case slang::TypeReflection::Kind::GenericTypeParameter: + { + const char* name = typeLayout->getName(); + SLANG_UNUSED(name); + break; + } + case slang::TypeReflection::Kind::Interface: + { + const char* name = typeLayout->getName(); + SLANG_UNUSED(name); + break; + } + case slang::TypeReflection::Kind::Resource: + { + if (BindSet::isTextureType(typeLayout)) + { + // We don't bother setting any data + BindSet::Value* value = bindSet->createTextureValue(typeLayout); + value->m_userIndex = entryIndex; + bindSet->setAt(location, value); + break; + } + + auto type = typeLayout->getType(); + auto shape = type->getResourceShape(); + + //auto access = type->getResourceAccess(); + + switch (shape & SLANG_RESOURCE_BASE_SHAPE_MASK) + { + default: + assert(!"unhandled case"); + break; + case SLANG_BYTE_ADDRESS_BUFFER: + case SLANG_STRUCTURED_BUFFER: + { + size_t bufferSize = srcEntry.bufferData.getCount() * sizeof(unsigned int); + + BindSet::Value* value = bindSet->createBufferValue(typeLayout, bufferSize, srcEntry.bufferData.getBuffer()); + SLANG_ASSERT(value); + + value->m_userIndex = entryIndex; + + bindSet->setAt(location, value); + break; + } + } + if (shape & SLANG_TEXTURE_ARRAY_FLAG) + { + + } + if (shape & SLANG_TEXTURE_MULTISAMPLE_FLAG) + { + + } + + break; + } + } + } + + return SLANG_OK; + } + void generateTextureData(TextureData& output, const InputTextureDesc& desc) { switch (desc.format) diff --git a/tools/render-test/shader-input-layout.h b/tools/render-test/shader-input-layout.h index d2f67a61e..3e33f876e 100644 --- a/tools/render-test/shader-input-layout.h +++ b/tools/render-test/shader-input-layout.h @@ -4,6 +4,10 @@ #include "core/slang-basic.h" #include "core/slang-random-generator.h" +#include "core/slang-writer.h" + +#include "bind-location.h" + #include "render.h" namespace renderer_test { @@ -92,6 +96,12 @@ public: void updateForTarget(SlangCompileTarget target); void parse(Slang::RandomGenerator* rand, const char* source); + + /// Adds to bind set resources as defined in entries. + /// Note: No actual resources are created on a device, these are just the 'Resource' structures that are held on the BindSet + /// For buffers, the Resources will be setup with the contents of the entry. + /// That if a resource is created that maps to an entry, the m_userData member of Resource will be set to it's index + static SlangResult addBindSetValues(const Slang::List<ShaderInputLayoutEntry>& entries, const Slang::String& sourcePath, Slang::WriterHelper outError, BindRoot& bindRoot); }; void generateTextureDataRGB8(TextureData& output, const InputTextureDesc& desc); diff --git a/tools/slang-test/options.h b/tools/slang-test/options.h index a210a7558..12869a945 100644 --- a/tools/slang-test/options.h +++ b/tools/slang-test/options.h @@ -84,7 +84,9 @@ struct Options // By default we potentially synthesize test for all // TODO: Vulkan is disabled by default for now as the majority as vulkan synthesized tests // OpenGL is disabled for now - Slang::RenderApiFlags synthesizedTestApis = Slang::RenderApiFlag::AllOf & ~(Slang::RenderApiFlag::Vulkan | Slang::RenderApiFlag::OpenGl | Slang::RenderApiFlag::CPU); + // CPU is disabled by default + // CUDA is disabled by default + Slang::RenderApiFlags synthesizedTestApis = Slang::RenderApiFlag::AllOf & ~(Slang::RenderApiFlag::Vulkan | Slang::RenderApiFlag::OpenGl | Slang::RenderApiFlag::CPU | Slang::RenderApiFlag::CUDA); // The adapter to use. If empty will match first found adapter. Slang::String adapter; diff --git a/tools/slang-test/slang-test-main.cpp b/tools/slang-test/slang-test-main.cpp index 9ccf6840f..ac073bcee 100644 --- a/tools/slang-test/slang-test-main.cpp +++ b/tools/slang-test/slang-test-main.cpp @@ -2841,8 +2841,10 @@ SlangResult innerMain(int argc, char** argv) /*auto computeTestCategory = */categorySet.add("compute", fullTestCategory); auto vulkanTestCategory = categorySet.add("vulkan", fullTestCategory); auto unitTestCatagory = categorySet.add("unit-test", fullTestCategory); + auto cudaTestCategory = categorySet.add("cuda", fullTestCategory); + auto compatibilityIssueCategory = categorySet.add("compatibility-issue", fullTestCategory); - + #if SLANG_WINDOWS_FAMILY auto windowsCategory = categorySet.add("windows", fullTestCategory); #endif @@ -2851,13 +2853,13 @@ SlangResult innerMain(int argc, char** argv) auto unixCatagory = categorySet.add("unix", fullTestCategory); #endif - // An un-categorized test will always belong to the `full` category categorySet.defaultCategory = fullTestCategory; TestCategory* fxcCategory = nullptr; TestCategory* dxcCategory = nullptr; TestCategory* glslangCategory = nullptr; + TestCategory* nvrtcCategory = nullptr; // Work out what backends/pass-thrus are available { @@ -2885,6 +2887,10 @@ SlangResult innerMain(int argc, char** argv) { dxcCategory = categorySet.add("dxc", fullTestCategory); } + if (context.availableBackendFlags & PassThroughFlag::NVRTC) + { + nvrtcCategory = categorySet.add("nvrtc", fullTestCategory); + } } // Working out what renderApis is worked on on demand through |
