summaryrefslogtreecommitdiffstats
path: root/tools/render-test/bind-location.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tools/render-test/bind-location.cpp')
-rw-r--r--tools/render-test/bind-location.cpp1117
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