summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjsmall-nvidia <jsmall@nvidia.com>2020-01-15 14:58:45 -0500
committerGitHub <noreply@github.com>2020-01-15 14:58:45 -0500
commit662721ba4ab0e38924701df4c876a86eb8390968 (patch)
treedeef68220d0aebbdfff370918a3d42fcf12fd72c
parentef41dfc605f7868c0ccc7dde05982232b7d49589 (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.
-rw-r--r--source/slang/slang.vcxproj2
-rw-r--r--source/slang/slang.vcxproj.filters16
-rw-r--r--tools/render-test/bind-location.cpp1117
-rw-r--r--tools/render-test/bind-location.h428
-rw-r--r--tools/render-test/cpu-compute-util.cpp344
-rw-r--r--tools/render-test/cpu-compute-util.h15
-rw-r--r--tools/render-test/cpu-memory-binding.cpp641
-rw-r--r--tools/render-test/cpu-memory-binding.h88
-rw-r--r--tools/render-test/render-test-main.cpp7
-rw-r--r--tools/render-test/render-test-tool.vcxproj4
-rw-r--r--tools/render-test/render-test-tool.vcxproj.filters8
-rw-r--r--tools/render-test/shader-input-layout.cpp135
-rw-r--r--tools/render-test/shader-input-layout.h10
-rw-r--r--tools/slang-test/options.h4
-rw-r--r--tools/slang-test/slang-test-main.cpp10
15 files changed, 1893 insertions, 936 deletions
diff --git a/source/slang/slang.vcxproj b/source/slang/slang.vcxproj
index 017f99a1c..de1108a1c 100644
--- a/source/slang/slang.vcxproj
+++ b/source/slang/slang.vcxproj
@@ -379,4 +379,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
-</Project> \ No newline at end of file
+</Project> \ No newline at end of file
diff --git a/source/slang/slang.vcxproj.filters b/source/slang/slang.vcxproj.filters
index 350f12b6b..d5abb0289 100644
--- a/source/slang/slang.vcxproj.filters
+++ b/source/slang/slang.vcxproj.filters
@@ -463,19 +463,19 @@
</ClCompile>
</ItemGroup>
<ItemGroup>
- <CustomBuild Include="core.meta.slang">
+ <None Include="..\core\core.natvis">
<Filter>Source Files</Filter>
- </CustomBuild>
- <CustomBuild Include="hlsl.meta.slang">
+ </None>
+ <None Include="slang.natvis">
<Filter>Source Files</Filter>
- </CustomBuild>
+ </None>
</ItemGroup>
<ItemGroup>
- <Natvis Include="..\core\core.natvis">
+ <CustomBuild Include="core.meta.slang">
<Filter>Source Files</Filter>
- </Natvis>
- <Natvis Include="slang.natvis">
+ </CustomBuild>
+ <CustomBuild Include="hlsl.meta.slang">
<Filter>Source Files</Filter>
- </Natvis>
+ </CustomBuild>
</ItemGroup>
</Project> \ No newline at end of file
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