StructuredMemory.h#

Contains a utility class which allows flexible and easy access and setting of memory that would usually be accessed as raw data.

Includes#

  • PVRCore/strings/StringHash.h

  • PVRCore/types/FreeValue.h

  • PVRCore/types/GpuDataTypes.h

  • PVRCore/types/Types.h

  • iomanip

  • sstream

Included By#

Namespaces#

Classes#

Defines#

Source Code#

#pragma once
#include "PVRCore/types/Types.h"
#include "PVRCore/types/GpuDataTypes.h"
#include "PVRCore/types/FreeValue.h"
#include "PVRCore/strings/StringHash.h"
#include <sstream>
#include <iomanip>

namespace pvr {

namespace utils {
class StructuredMemoryEntry;
class StructuredBufferView;

class StructuredMemoryDescription
{
private:
    friend class StructuredMemoryEntry;
    std::string _name;
    std::vector<StructuredMemoryDescription> _children;
    GpuDatatypes _type;
    uint32_t _numArrayElements;

public:
    StructuredMemoryDescription(const StructuredMemoryDescription& rhs) : _name(rhs._name), _children(rhs._children), _type(rhs._type), _numArrayElements(rhs._numArrayElements) {}

    StructuredMemoryDescription(const std::string& name, uint32_t arraySize, std::initializer_list<StructuredMemoryDescription> children)
        : _name(name), _children(std::move(children)), _numArrayElements(arraySize), _type(GpuDatatypes::none)
    {}

    StructuredMemoryDescription(std::string&& name, uint32_t arraySize, std::initializer_list<StructuredMemoryDescription> children)
        : _name(std::move(name)), _children(std::move(children)), _numArrayElements(arraySize), _type(GpuDatatypes::none)
    {}

    StructuredMemoryDescription(std::string&& name, GpuDatatypes type) : _name(std::move(name)), _type(type), _numArrayElements(1) {}

    StructuredMemoryDescription(const std::string& str, uint32_t arraySize, pvr::GpuDatatypes type) : _name(std::move(str)), _type(type), _numArrayElements(arraySize) {}

    StructuredMemoryDescription() : _type(GpuDatatypes::none), _numArrayElements(1) {}

    StructuredMemoryDescription(StructuredMemoryDescription&& rhs)
        : _name(std::move(rhs._name)), _children(std::move(rhs._children)), _type(std::move(rhs._type)), _numArrayElements(std::move(rhs._numArrayElements))
    {}

    StructuredMemoryDescription& operator=(const StructuredMemoryDescription& rhs)
    {
        _name = rhs._name;
        _children = rhs._children;
        _type = rhs._type;
        _numArrayElements = rhs._numArrayElements;
        return *this;
    }

    StructuredMemoryDescription& setName(const std::string& name)
    {
        _name = name;
        return *this;
    }

    StructuredMemoryDescription& setName(std::string&& name)
    {
        _name = std::move(name);
        return *this;
    }

    StructuredMemoryDescription& setNumArrayElements(uint32_t numArrayElements)
    {
        _numArrayElements = numArrayElements;
        return *this;
    }

    StructuredMemoryDescription& setType(GpuDatatypes type)
    {
        _type = type;
        return *this;
    }

    StructuredMemoryDescription& addElement(const std::string& name, GpuDatatypes type, uint32_t numArrayElements = 1)
    {
        _children.emplace_back(name, numArrayElements, type);
        return *this;
    }
    StructuredMemoryDescription& addElement(std::string& name, GpuDatatypes type, uint32_t numArrayElements = 1)
    {
        _children.emplace_back(name, numArrayElements, type);
        return *this;
    }
    StructuredMemoryDescription& addElement(const StructuredMemoryDescription& smd)
    {
        _children.emplace_back(smd);
        return *this;
    }

    StructuredMemoryDescription getElement(const std::string& name)
    {
        for (uint32_t i = 0; i < _children.size(); i++)
        {
            if (_children[i].getName() == name) { return _children[i]; }
        }

        return StructuredMemoryDescription();
    }

    StructuredMemoryDescription getElement(uint32_t index)
    {
        if (index < _children.size()) { return _children[index]; }
        return StructuredMemoryDescription();
    }

    const std::string& getName() const { return _name; }

    const GpuDatatypes getType() { return _type; }

    const uint32_t getNumArrayElements() { return _numArrayElements; }

    const uint32_t getNumChildren() { return static_cast<uint32_t>(_children.size()); }
};

class StructuredMemoryEntry
{
private:
    friend class StructuredBufferView;
    friend class StructuredBufferViewElement;

    StringHash _name;
    StructuredMemoryEntry* _parent;
    std::vector<StructuredMemoryEntry> _childEntries; // These must never reallocate. Hence the list is non-copyable
    GpuDatatypes _type;
    uint32_t _baseAlignment; // The alignment requirements of this object as defined by std140. Takes into account array etc.
    uint32_t _numArrayElements; // The number of array elements.
    bool _variableArray; // This is the last 1st level element, so can be resized dynamically
    uint64_t _size; // The minimum size of this item, including self-padding IF an arrays or structures, NOT taking into account next item alignment.
    uint64_t _singleElementSize; // The minimum size of this item, including self-padding IF an arrays or structures, NOT taking into account next item alignment.
    uint32_t _arrayMemberSize; // The size of each slice of this item, ALWAYS including self-padding for array or structure.
    uint32_t _offset;
    uint64_t _minDynamicAlignment;
    void* _mappedMemory;
    uint32_t _mappedDynamicSlice;

    struct IsEqual
    {
        const StringHash& _hash;
        IsEqual(const StringHash& name) : _hash(name) {}
        bool operator()(const StructuredMemoryEntry& rhs) const { return _hash == rhs.getName(); }
    };

    void calcBaseAlignment()
    {
        if (isStructure())
        {
            _baseAlignment = 0;
            for (auto& child : _childEntries)
            {
                child.calcBaseAlignment();
                _baseAlignment = std::max(_baseAlignment, child._baseAlignment);
            }
            _baseAlignment = std::max(_baseAlignment, getAlignment(GpuDatatypes::vec4)); // STD140
        }
        else
        {
            _baseAlignment = getAlignment(_type);
            if (_numArrayElements > 1) { _baseAlignment = std::max(_baseAlignment, getAlignment(GpuDatatypes::vec4)); }
        }
    }

    void calcDynamicAlignment(BufferUsageFlags usage = BufferUsageFlags::UniformBuffer, const uint64_t minUboDynamicAlignment = 0, const uint64_t minSsboDynamicAlignment = 0)
    {
        _minDynamicAlignment = 0;

        uint64_t uboAlign = 0;
        uint64_t ssboAlign = 0;

        // if dynamic ubo OR dynamic ssbo take the relevant alignment
        if (static_cast<uint32_t>(usage & BufferUsageFlags::UniformBuffer) != 0 && minUboDynamicAlignment) { uboAlign = minUboDynamicAlignment; }
        if (static_cast<uint32_t>(usage & BufferUsageFlags::StorageBuffer) != 0 && minSsboDynamicAlignment) { ssboAlign = minSsboDynamicAlignment; }
        _minDynamicAlignment = std::max(uboAlign, ssboAlign);

        if (isStructure())
        {
            for (auto& child : _childEntries) { child.calcDynamicAlignment(); }
        }
    }

    // INTERNAL NOTE: CALL THIS ** AFTER ** CALC BASE ALIGNMENT, TO FIX THE _offset AND _size MEMBERS
    void calcSizeAndOffset(const uint32_t& offset)
    {
        _offset = align(offset, _baseAlignment);
        _offset = align(_offset, _minDynamicAlignment);
        if (isStructure())
        {
            uint32_t tmp_offset = 0;
            for (auto& child : _childEntries)
            {
                child.calcSizeAndOffset(tmp_offset);
                tmp_offset = child.getOffset() + static_cast<uint32_t>(child.getSize());
            }
            // STD140: Structures are padded to their alignment so that a[n] ==>   sizeof(a[0]) == sizeof[a] / n
            tmp_offset = align(tmp_offset, _baseAlignment);
            tmp_offset = align(tmp_offset, _minDynamicAlignment);
            _arrayMemberSize = tmp_offset;
            _singleElementSize = _arrayMemberSize;

            tmp_offset *= _numArrayElements;
            _size = tmp_offset;
        }
        else
        {
            _arrayMemberSize = getSelfAlignedArraySize(_type);
            _singleElementSize = pvr::getSize(_type);
            _size = _variableArray ? _arrayMemberSize * _numArrayElements : pvr::getSize(_type, _numArrayElements);
        }
    }

    void privateInit(const StructuredMemoryDescription& desc, StructuredMemoryEntry* parent, bool firstLevel, bool isVariableArray)
    {
        _name = desc._name;
        _numArrayElements = desc._numArrayElements;
        _variableArray = isVariableArray;
        _parent = parent;
        this->_type = desc._type;
        if (!desc._children.empty()) // isStruct
        {
            _childEntries.resize(desc._children.size()); // Called once
            for (uint32_t i = 0; i < desc._children.size(); ++i)
            {
                // PASS "TRUE" TO "VARIABLEARRAY" OF THE LAST ENTRY OF THE FIRST LEVEL.
                // THIS IS TO A) ALLOW SSBO VARIABLE SIZED ARRAYS.
                // B) ALIGN THE SIZE OF THE WHOLE BLOCK TO CONTAIN ITS OWN PADDING
                _childEntries[i].privateInit(desc._children[i], this, false, firstLevel && (i + 1 == desc._children.size()));
            }
            _type = GpuDatatypes::none;
        }
    }

    void layout(BufferUsageFlags usage = BufferUsageFlags::UniformBuffer, const uint64_t minUboDynamicAlignment = 0, const uint64_t minSsboDynamicAlignment = 0)
    {
        calcBaseAlignment();
        calcDynamicAlignment(usage, minUboDynamicAlignment, minSsboDynamicAlignment);
        calcSizeAndOffset(0);
    }

    void setMappedMemory(void* mappedMemory, const uint32_t mappedDynamicSlice = 0)
    {
        _mappedMemory = mappedMemory;
        _mappedDynamicSlice = mappedDynamicSlice;
    }

    uint32_t getMappedDynamicSlice() const { return _mappedDynamicSlice; }

    void* getMappedMemory() const { return _mappedMemory; }

    void fixParentPointers(StructuredMemoryEntry* parent)
    {
        _parent = parent;
        for (auto&& child : _childEntries)
        { //
            child.fixParentPointers(this);
        }
    }
    friend void swap(StructuredBufferView& first, StructuredBufferView& second);

public:
    StructuredMemoryEntry()
        : _name(), _parent(0), _type(GpuDatatypes::none), _numArrayElements(0), _size(0), _arrayMemberSize(0), _singleElementSize(0), _offset(0), _mappedMemory(nullptr),
          _mappedDynamicSlice(0), _baseAlignment(0), _minDynamicAlignment(0), _variableArray(0)
    {}

    StructuredMemoryEntry(const StructuredMemoryEntry& other)
    {
        _name = other._name;
        _parent = other._parent;
        _childEntries.reserve(other._childEntries.size());
        std::copy(other._childEntries.begin(), other._childEntries.end(), back_inserter(_childEntries));
        _type = other._type;
        _baseAlignment = other._baseAlignment;
        _numArrayElements = other._numArrayElements;
        _variableArray = other._variableArray;
        _size = other._size;
        _singleElementSize = other._singleElementSize;
        _arrayMemberSize = other._arrayMemberSize;
        _offset = other._offset;
        _minDynamicAlignment = other._minDynamicAlignment;
        _mappedMemory = other._mappedMemory;
        _mappedDynamicSlice = other._mappedDynamicSlice;
    }

    virtual ~StructuredMemoryEntry() {}

    friend void swap(StructuredMemoryEntry& first, StructuredMemoryEntry& second)
    {
        std::swap(first._name, second._name);
        std::swap(first._parent, second._parent);
        std::swap(first._childEntries, second._childEntries);
        std::swap(first._type, second._type);
        std::swap(first._baseAlignment, second._baseAlignment);
        std::swap(first._numArrayElements, second._numArrayElements);
        std::swap(first._variableArray, second._variableArray);
        std::swap(first._size, second._size);
        std::swap(first._singleElementSize, second._singleElementSize);
        std::swap(first._arrayMemberSize, second._arrayMemberSize);
        std::swap(first._offset, second._offset);
        std::swap(first._minDynamicAlignment, second._minDynamicAlignment);
        std::swap(first._mappedMemory, second._mappedMemory);
        std::swap(first._mappedDynamicSlice, second._mappedDynamicSlice);
    }

    StructuredMemoryEntry& operator=(StructuredMemoryEntry other)
    {
        swap(*this, other);
        return *this;
    }

    const uint32_t getNumChildren() const { return static_cast<uint32_t>(_childEntries.size()); }

    const StructuredMemoryEntry& getChild(uint32_t index) const { return _childEntries[index]; }

    const StructuredMemoryEntry* getParent() const { return _parent; }

    uint32_t getNumArrayElements() const { return _numArrayElements; }

    bool isStructure() const { return _childEntries.size() != 0; }

    bool isPrimitive() const { return !isStructure(); }

    const StringHash& getName() const { return _name; }

    GpuDatatypes getPrimitiveType() const { return _type; }

    uint32_t getOffset() const { return _offset; }

    uint32_t getArrayElementOffset(uint32_t arrayElement) const { return _offset + _arrayMemberSize * arrayElement; }

    uint64_t getSize() const { return _size; }

    uint64_t getSingleItemSize() const { return _singleElementSize; }

    void setLastElementArraySize(uint32_t arraySize)
    {
        auto& child = _childEntries.back();

        uint64_t oldSize = child._size;
        uint64_t newSize = child._arrayMemberSize * arraySize;
        debug_assertion(child._size == (child._arrayMemberSize * child._numArrayElements), "1");
        child._numArrayElements = arraySize;

        int64_t sizeDiff = static_cast<int64_t>(newSize) - static_cast<int64_t>(oldSize);
        child._size = newSize;
        _size += sizeDiff;
    }

    uint32_t getIndex(const StringHash& name) const
    {
        auto entry = std::find_if(_childEntries.begin(), _childEntries.end(), IsEqual(name));
        if (entry == _childEntries.end()) { return static_cast<uint32_t>(-1); }
        return static_cast<uint32_t>(entry - _childEntries.begin());
    }

    void init(const StructuredMemoryDescription& desc)
    {
        privateInit(desc, nullptr, true, false);
        layout();
    }

    void initDynamic(const StructuredMemoryDescription& desc, BufferUsageFlags usage = BufferUsageFlags::UniformBuffer, const uint64_t minUboDynamicAlignment = 0,
        const uint64_t minSsboDynamicAlignment = 0)
    {
        privateInit(desc, nullptr, true, false);
        layout(usage, minUboDynamicAlignment, minSsboDynamicAlignment);
    }

    inline static void printPreamble(std::stringstream& str, uint32_t level)
    {
        for (uint32_t i = 0; i < level; ++i) { str << " "; }
    }

    void printIntoStringStream(std::stringstream& str, uint32_t level) const
    {
        str << "\n" << std::setw(3) << _offset << ": ";
        printPreamble(str, level * 2);
        str << (isStructure() ? "struct" : toString(_type)) << " " << _name.str();
        if (_numArrayElements > 1) { str << "[" << _numArrayElements << "]"; }
        str << ";";
        if (!isStructure()) { str << "\t"; }
        str << "\t baseSz:" << _size / _numArrayElements << "\t size:" << getSize() << "\t baseAlign:" << _baseAlignment << "\t nextOffset:" << _offset + getSize()
            << "\t arrayMemberSize:" << _arrayMemberSize;
        if (isStructure())
        {
            str << "\n";
            printPreamble(str, level * 2 + 5);
            str << "{";
            for (auto& child : _childEntries) { child.printIntoStringStream(str, level + 1); }
            str << "\n";
            printPreamble(str, level * 2 + 5);
            str << "}";
        }
    }
};

class StructuredBufferView;

class StructuredBufferViewElement
{
private:
    friend class StructuredBufferView;
    uint32_t _offset;
    void* _mappedMemory;
    uint32_t _level;
    uint32_t _indices[5]; // This, is the array index of each ancestor item up the chain. Is carried to children elements to enable offset calcs.
    const StructuredMemoryEntry& _prototype;
    StructuredBufferViewElement(const StructuredMemoryEntry& entry, uint32_t level, uint32_t elementArrayIndex, const uint32_t* parentIndices, uint32_t dynamicSlice = 0)
        : _mappedMemory(nullptr), _level(level), _prototype(entry)
    {
        _indices[0] = elementArrayIndex;
        if (parentIndices) { memcpy(_indices + 1, parentIndices, sizeof(uint32_t) * (level)); }
        init(dynamicSlice);
    }
    void init(uint32_t dynamicSlice = 0)
    {
        // Get the offset INSIDE CURRENT LEVEL.
        _offset = _prototype.getArrayElementOffset(_indices[0]);
        const StructuredMemoryEntry* parent = _prototype.getParent();
        size_t level = 1; // How many levels up we have gone
        uint64_t dynamicSliceSize = 0;
        uint32_t mappedDynamicSlice = 0;
        while (parent) // Until we reach the root element
        {
            debug_assertion(parent->getNumArrayElements() > _indices[level], "StructuredBufferViewElement: Attempted out-of-bounds access in getOffset");
            _offset += parent->getArrayElementOffset(_indices[level++]);
            dynamicSliceSize = parent->getSize();
            mappedDynamicSlice = parent->getMappedDynamicSlice();
            // store the mapped memory so we only have to do this lookup once rather than each time setValue is called
            _mappedMemory = parent->getMappedMemory();
            parent = parent->getParent();
        }

        // at this point dynamicSliceSize matches the root size
        debug_assertion(dynamicSlice >= mappedDynamicSlice, "StructuredBufferViewElement: Mapped dynamic slice must be greater than or equal to the current dynamic slice");
        uint32_t sliceOffset = (dynamicSlice * static_cast<uint32_t>(dynamicSliceSize)) - (mappedDynamicSlice * static_cast<uint32_t>(dynamicSliceSize));
        _offset += sliceOffset;
    }

    void* getMappedMemory()
    {
        debug_assertion(_mappedMemory != nullptr, "StructuredBufferViewElement: Before getting mapped memory the memory must be set.");
        return _mappedMemory;
    }

public:
    StructuredBufferViewElement getElementByName(const StringHash& str, uint32_t elementArrayIndex = 0, uint32_t dynamicSlice = 0)
    {
        return getElement(_prototype.getIndex(str), elementArrayIndex, dynamicSlice);
    }

    const StructuredBufferViewElement getElementByName(const StringHash& str, uint32_t elementArrayIndex = 0, uint32_t dynamicSlice = 0) const
    {
        return getElement(_prototype.getIndex(str), elementArrayIndex, dynamicSlice);
    }

    StructuredBufferViewElement getElementByName(const std::string& str, uint32_t elementArrayIndex = 0, uint32_t dynamicSlice = 0)
    {
        return getElement(_prototype.getIndex(str), elementArrayIndex, dynamicSlice);
    }

    const StructuredBufferViewElement getElementByName(const std::string& str, uint32_t elementArrayIndex = 0, uint32_t dynamicSlice = 0) const
    {
        return getElement(_prototype.getIndex(str), elementArrayIndex, dynamicSlice);
    }

    StructuredBufferViewElement getElementByName(const char* str, uint32_t elementArrayIndex = 0, uint32_t dynamicSlice = 0)
    {
        return getElement(_prototype.getIndex(str), elementArrayIndex, dynamicSlice);
    }

    const StructuredBufferViewElement getElementByName(const char* str, uint32_t elementArrayIndex = 0, uint32_t dynamicSlice = 0) const
    {
        return getElement(_prototype.getIndex(str), elementArrayIndex, dynamicSlice);
    }

    StructuredBufferViewElement getElement(uint32_t elementIndex, uint32_t elementArrayIndex = 0, uint32_t dynamicSlice = 0)
    {
        return StructuredBufferViewElement(_prototype.getChild(elementIndex), _level + 1, elementArrayIndex, _indices, dynamicSlice);
    }

    const StructuredBufferViewElement getElement(uint32_t elementIndex, uint32_t elementArrayIndex = 0, uint32_t dynamicSlice = 0) const
    {
        return StructuredBufferViewElement(_prototype.getChild(elementIndex), _level + 1, elementArrayIndex, _indices, dynamicSlice);
    }

    const std::string& getElementNameByIndex(uint32_t elementIndex) const { return _prototype.getChild(elementIndex).getName(); }

    uint32_t getIndex(const StringHash& str) { return _prototype.getIndex(str); }

    uint32_t getIndex(const std::string& str) { return _prototype.getIndex(str); }

    uint32_t getIndex(const char* str) { return _prototype.getIndex(str); }

    uint32_t getOffset() const { return _offset; }

    uint64_t getValueSize() const { return _prototype.getSingleItemSize(); }

    uint64_t getArrayPaddedSize() const { return _prototype._arrayMemberSize; }

// clang-format off
#define DEFINE_SETVALUE_FOR_TYPE(ParamType)\
  void setValue(const ParamType& value)\
  {\
  memcpy(static_cast<char*>(getMappedMemory()) + getOffset(), &value, (size_t)getValueSize()); \
  }\
  \
  void setValue(const ParamType* value)\
  {\
    memcpy(static_cast<char*>(getMappedMemory()) + getOffset(), value, (size_t)getValueSize()); \
  }
    DEFINE_SETVALUE_FOR_TYPE(float)
    DEFINE_SETVALUE_FOR_TYPE(uint32_t)
    DEFINE_SETVALUE_FOR_TYPE(uint64_t)
    DEFINE_SETVALUE_FOR_TYPE(int32_t)
    DEFINE_SETVALUE_FOR_TYPE(int64_t)
    DEFINE_SETVALUE_FOR_TYPE(glm::vec2)
    DEFINE_SETVALUE_FOR_TYPE(glm::vec4)
    DEFINE_SETVALUE_FOR_TYPE(glm::ivec2)
    DEFINE_SETVALUE_FOR_TYPE(glm::ivec4)
    DEFINE_SETVALUE_FOR_TYPE(glm::uvec2)
    DEFINE_SETVALUE_FOR_TYPE(glm::uvec4)
    DEFINE_SETVALUE_FOR_TYPE(glm::mat2x2)
    DEFINE_SETVALUE_FOR_TYPE(glm::mat2x4)
    DEFINE_SETVALUE_FOR_TYPE(glm::mat3x2)
    DEFINE_SETVALUE_FOR_TYPE(glm::mat3x4)
    DEFINE_SETVALUE_FOR_TYPE(glm::mat4x2)
    DEFINE_SETVALUE_FOR_TYPE(glm::mat4x4)

#undef DEFINE_SETVALUE_FOR_TYPE
    // clang-format on

    void setValue(const glm::vec3& value) { memcpy(static_cast<char*>(getMappedMemory()) + getOffset(), &value, sizeof(glm::vec3)); }

    void setValue(const glm::ivec3& value) { memcpy(static_cast<char*>(getMappedMemory()) + getOffset(), &value, sizeof(glm::ivec3)); }

    void setValue(const glm::uvec3& value) { memcpy(static_cast<char*>(getMappedMemory()) + getOffset(), &value, sizeof(glm::uvec3)); }

    void setValue(const glm::vec3* value)
    {
        for (uint32_t i = 0; i < this->_prototype.getNumArrayElements(); ++i)
        { memcpy(static_cast<char*>(getMappedMemory()) + getOffset() + sizeof(glm::vec4) * i, value + i, sizeof(glm::vec3)); }
    }

    void setValue(const glm::ivec3* value)
    {
        for (uint32_t i = 0; i < this->_prototype.getNumArrayElements(); ++i)
        { memcpy(static_cast<char*>(getMappedMemory()) + getOffset() + sizeof(glm::ivec4) * i, value + i, sizeof(glm::ivec3)); }
    }

    void setValue(const glm::uvec3* value)
    {
        for (uint32_t i = 0; i < this->_prototype.getNumArrayElements(); ++i)
        { memcpy(static_cast<char*>(getMappedMemory()) + getOffset() + sizeof(glm::uvec4) * i, value + i, sizeof(glm::uvec3)); }
    }

    void setValue(const glm::mat2x3& value)
    {
        glm::mat2x4 newvalue(value);
        memcpy(static_cast<char*>(getMappedMemory()) + getOffset(), (const void*)&newvalue, (size_t)getValueSize());
    }

    void setValue(const glm::mat3x3& value)
    {
        glm::mat3x4 newvalue(value);
        memcpy(static_cast<char*>(getMappedMemory()) + getOffset(), (const void*)&newvalue, (size_t)getValueSize());
    }

    void setValue(const glm::mat4x3& value)
    {
        glm::mat4x4 newvalue(value);
        memcpy(static_cast<char*>(getMappedMemory()) + getOffset(), (const void*)&newvalue, (size_t)getValueSize());
    }

    void setValue(const glm::mat2x3* value)
    {
        glm::mat2x4 newvalue(*value);
        memcpy(static_cast<char*>(getMappedMemory()) + getOffset(), (const void*)&newvalue, (size_t)getValueSize());
    }

    void setValue(const glm::mat3x3* value)
    {
        glm::mat3x4 newvalue(*value);
        memcpy(static_cast<char*>(getMappedMemory()) + getOffset(), (const void*)&newvalue, (size_t)getValueSize());
    }

    void setValue(const glm::mat4x3* value)
    {
        glm::mat4x4 newvalue(*value);
        memcpy(static_cast<char*>(getMappedMemory()) + getOffset(), (const void*)&newvalue, (size_t)getValueSize());
    }

    void setValue(const FreeValue& value)
    {
        if (this->_prototype.getPrimitiveType() != value.dataType() && value.dataType() != GpuDatatypes::mat3x3)
        { throw std::runtime_error("StructuredBufferView: Mismatched FreeValue datatype"); }
        if (value.dataType() == GpuDatatypes::mat3x3)
        {
            glm::mat3x4 tmp(value.interpretValueAs<glm::mat3x3>());
            memcpy((char*)getMappedMemory() + getOffset(), &tmp[0][0], (size_t)getValueSize());
        }
        else
        {
            memcpy((char*)getMappedMemory() + getOffset(), value.raw(), (size_t)getValueSize());
        }
    }

    void setValue(const TypedMem& value)
    {
        debug_assertion(value.arrayElements() == 1, "Calling set value would have updated multiple elements here");
        setArrayValuesStartingFromThis(value);
    }

    void setArrayValuesStartingFromThis(const TypedMem& value)
    {
        if (this->_prototype.getPrimitiveType() != value.dataType() && value.dataType() != GpuDatatypes::mat3x3)
        { throw std::runtime_error("StructuredBufferView: Mismatched FreeValue datatype"); }
        if (value.arrayElements() != _prototype.getNumArrayElements()) { throw std::runtime_error("StructuredBufferView: Mismatched number of array elements"); }
        if (value.dataType() == GpuDatatypes::mat3x3)
        {
            size_t startoff = getOffset();
            for (uint32_t i = 0; i < value.arrayElements(); ++i)
            {
                glm::mat3x4 tmp(value.interpretValueAs<glm::mat3x3>(i));

                uint64_t myoff = startoff + _prototype._arrayMemberSize * i;
                uint64_t myvaluesize = getSize(value.dataType());
                memcpy((char*)getMappedMemory() + (size_t)myoff, &tmp[0][0], (size_t)myvaluesize);
            }
        }
        else
        {
            uint64_t myoff = getOffset();
            uint64_t myvaluesize = value.dataSize();
            memcpy((char*)getMappedMemory() + (size_t)myoff, value.raw(), (size_t)myvaluesize);
        }
    }

    uint32_t getNumElements()
    {
        if (_prototype.isStructure()) { return _prototype.getNumChildren(); }
        return 1;
    }
};

class StructuredBufferView
{
private:
    StructuredMemoryEntry _root;
    uint32_t _numDynamicSlices;

public:
    StructuredBufferView() : _numDynamicSlices(1) {}

    StructuredBufferView(const StructuredBufferView& other) : _numDynamicSlices(other._numDynamicSlices), _root(other._root)
    { //
        _root.fixParentPointers(0);
    }
    StructuredBufferView(const StructuredBufferView&& other) : _numDynamicSlices(other._numDynamicSlices), _root(std::move(other._root))
    { //
        _root.fixParentPointers(0);
    }

    StructuredBufferView& operator=(StructuredBufferView other)
    {
        swap(*this, other);
        _root.fixParentPointers(0);
        return *this;
    }

    friend void swap(StructuredBufferView& first, StructuredBufferView& second)
    {
        swap(first._root, second._root);
        std::swap(first._numDynamicSlices, second._numDynamicSlices);
        first._root.fixParentPointers(0);
        second._root.fixParentPointers(0);
    }

    void pointToMappedMemory(void* mappedMemory, const uint32_t mappedDynamicSlice = 0) { _root.setMappedMemory(mappedMemory, mappedDynamicSlice); }

    uint64_t getSize() const { return getDynamicSliceSize() * _numDynamicSlices; }

    uint32_t getMappedDynamicSlice() const { return _root.getMappedDynamicSlice(); }

    const void* getMappedMemory() const { return _root.getMappedMemory(); }

    uint64_t getDynamicSliceSize() const { return _root.getSize(); }

    uint32_t getNumDynamicSlices() const { return _numDynamicSlices; }

    const std::string& getName() const { return _root.getName(); }

    uint32_t getDynamicSliceOffset(uint32_t dynamicSliceIndex) const { return dynamicSliceIndex * static_cast<uint32_t>(getDynamicSliceSize()); }

    void init(const StructuredMemoryDescription& desc) { _root.init(desc); }

    void initDynamic(const StructuredMemoryDescription& desc, uint32_t numDynamicSlices = 1, BufferUsageFlags usage = BufferUsageFlags::UniformBuffer,
        const uint64_t minUboDynamicAlignment = 0, const uint64_t minSsboDynamicAlignment = 0)
    {
        _root.initDynamic(desc, usage, minUboDynamicAlignment, minSsboDynamicAlignment);
        _numDynamicSlices = numDynamicSlices;
    }

    const StructuredBufferViewElement getElementByName(const StringHash& str, uint32_t elementArrayIndex = 0, uint32_t dynamicSlice = 0) const
    {
        return StructuredBufferViewElement(_root, 0, 0, nullptr).getElementByName(str, elementArrayIndex, dynamicSlice);
    }

    StructuredBufferViewElement getElementByName(const StringHash& str, uint32_t elementArrayIndex = 0, uint32_t dynamicSlice = 0)
    {
        return StructuredBufferViewElement(_root, 0, 0, nullptr).getElementByName(str, elementArrayIndex, dynamicSlice);
    }

    const StructuredBufferViewElement getElementByName(const std::string& str, uint32_t elementArrayIndex = 0, uint32_t dynamicSlice = 0) const
    {
        return StructuredBufferViewElement(_root, 0, 0, nullptr).getElementByName(str, elementArrayIndex, dynamicSlice);
    }

    StructuredBufferViewElement getElementByName(const std::string& str, uint32_t elementArrayIndex = 0, uint32_t dynamicSlice = 0)
    {
        return StructuredBufferViewElement(_root, 0, 0, nullptr).getElementByName(str, elementArrayIndex, dynamicSlice);
    }

    const StructuredBufferViewElement getElementByName(const char* str, uint32_t elementArrayIndex = 0, uint32_t dynamicSlice = 0) const
    {
        return StructuredBufferViewElement(_root, 0, 0, nullptr).getElementByName(str, elementArrayIndex, dynamicSlice);
    }

    StructuredBufferViewElement getElementByName(const char* str, uint32_t elementArrayIndex = 0, uint32_t dynamicSlice = 0)
    {
        return StructuredBufferViewElement(_root, 0, 0, nullptr).getElementByName(str, elementArrayIndex, dynamicSlice);
    }

    const StructuredBufferViewElement getElement(uint32_t elementIndex, uint32_t elementArrayIndex = 0, uint32_t dynamicSlice = 0) const
    {
        return StructuredBufferViewElement(_root, 0, 0, nullptr).getElement(elementIndex, elementArrayIndex, dynamicSlice);
    }

    StructuredBufferViewElement getElement(uint32_t elementIndex, uint32_t elementArrayIndex = 0, uint32_t dynamicSlice = 0)
    {
        return StructuredBufferViewElement(_root, 0, 0, nullptr).getElement(elementIndex, elementArrayIndex, dynamicSlice);
    }

    std::string getElementNameByIndex(uint32_t elementIndex) { return StructuredBufferViewElement(_root, 0, 0, nullptr).getElementNameByIndex(elementIndex); }

    uint32_t getNumElements() { return StructuredBufferViewElement(_root, 0, 0, nullptr).getNumElements(); }

    void setLastElementArraySize(uint32_t arraySize) { return _root.setLastElementArraySize(arraySize); }

    uint32_t getIndex(const StringHash& name) const { return _root.getIndex(name); }

    uint32_t getIndex(const char* name) const { return _root.getIndex(name); }

    uint32_t getIndex(const std::string& name) const { return _root.getIndex(name); }

    std::string toString()
    {
        std::stringstream ss;
        ss << "\n";
        _root.printIntoStringStream(ss, 0);
        return ss.str();
    }
};
} // namespace utils
} // namespace pvr