diff options
Diffstat (limited to 'tools/render-test/bind-location.cpp')
| -rw-r--r-- | tools/render-test/bind-location.cpp | 1117 |
1 files changed, 1117 insertions, 0 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 |
