diff --git a/libs/utils/include/utils/StructureOfArrays.h b/libs/utils/include/utils/StructureOfArrays.h index e5e8328b8d..99854b998e 100644 --- a/libs/utils/include/utils/StructureOfArrays.h +++ b/libs/utils/include/utils/StructureOfArrays.h @@ -55,10 +55,19 @@ public: static constexpr size_t getArrayCount() noexcept { return kArrayCount; } // Size needed to store "size" array elements - static size_t getNeededSize(size_t size) noexcept { + static size_t getNeededSize(size_t const size) noexcept { return getOffset(kArrayCount - 1, size) + sizeof(TypeAt) * size; } + template + void copyRange(size_t destOffset, + const StructureOfArraysBase& src, size_t srcOffset, size_t count) { + assert(destOffset + count <= mCapacity); + assert(srcOffset + count <= src.mSize); + Copier copier{this, src, destOffset, srcOffset, count}; + const_cast(this)->for_each_index(mArrays, copier); + } + // -------------------------------------------------------------------------------------------- class IteratorValue; @@ -77,21 +86,28 @@ public: friend class IteratorValue; friend iterator; friend const_iterator; + friend class StructureOfArraysBase; StructureOfArraysBase* const UTILS_RESTRICT soa; size_t const index; - IteratorValueRef(StructureOfArraysBase* soa, size_t index) : soa(soa), index(index) { } + IteratorValueRef(StructureOfArraysBase* soa, size_t const index) : soa(soa), index(index) { } // assigns a value_type to a reference (i.e. assigns to what's pointed to by the reference) template IteratorValueRef& assign(IteratorValue const& rhs, std::index_sequence); + template + IteratorValueRef& assign(Structure const& rhs, std::index_sequence); + + template + IteratorValueRef& assign(Structure&& rhs, std::index_sequence) noexcept; + // assigns a value_type to a reference (i.e. assigns to what's pointed to by the reference) template IteratorValueRef& assign(IteratorValue&& rhs, std::index_sequence) noexcept; // objects pointed to by reference can be swapped, so provide the special swap() function. - friend void swap(IteratorValueRef lhs, IteratorValueRef rhs) { + friend void swap(IteratorValueRef const lhs, IteratorValueRef const rhs) { lhs.soa->swap(lhs.index, rhs.index); } @@ -99,6 +115,9 @@ public: // references can be created by copy-assignment only IteratorValueRef(IteratorValueRef const& rhs) noexcept : soa(rhs.soa), index(rhs.index) { } + IteratorValueRef& operator=(Structure const& rhs); + IteratorValueRef& operator=(Structure&& rhs) noexcept; + // copy the content of a reference to the content of this one IteratorValueRef& operator=(IteratorValueRef const& rhs); @@ -120,6 +139,10 @@ public: template TypeAt& get() { return soa->elementAt(index); } }; + IteratorValueRef operator[](size_t index) noexcept { + return {this, index}; + } + /* * The value_type of iterator. This is basically the "structure" of the SoA. @@ -175,7 +198,7 @@ public: CVQualifiedSOAPointer soa; // don't use restrict, can have aliases if multiple iterators are created size_t index; - Iterator(CVQualifiedSOAPointer soa, size_t index) : soa(soa), index(index) {} + Iterator(CVQualifiedSOAPointer soa, size_t const index) : soa(soa), index(index) {} public: using value_type = IteratorValue; @@ -196,10 +219,10 @@ public: Iterator& operator++() { ++index; return *this; } Iterator& operator--() { --index; return *this; } - Iterator& operator+=(size_t n) { index += n; return *this; } - Iterator& operator-=(size_t n) { index -= n; return *this; } - Iterator operator+(size_t n) const { return { soa, index + n }; } - Iterator operator-(size_t n) const { return { soa, index - n }; } + Iterator& operator+=(size_t const n) { index += n; return *this; } + Iterator& operator-=(size_t const n) { index -= n; return *this; } + Iterator operator+(size_t const n) const { return { soa, index + n }; } + Iterator operator-(size_t const n) const { return { soa, index - n }; } difference_type operator-(Iterator const& rhs) const { return index - rhs.index; } bool operator==(Iterator const& rhs) const { return (index == rhs.index); } bool operator!=(Iterator const& rhs) const { return (index != rhs.index); } @@ -222,7 +245,7 @@ public: StructureOfArraysBase() = default; - explicit StructureOfArraysBase(size_t capacity) { + explicit StructureOfArraysBase(size_t const capacity) { setCapacity(capacity); } @@ -270,7 +293,7 @@ public: // set the capacity of the array. the capacity cannot be smaller than the current size, // the call is a no-op in that case. UTILS_NOINLINE - void setCapacity(size_t capacity) { + void setCapacity(size_t const capacity) { // allocate enough space for "capacity" elements of each array // capacity cannot change when optional storage is specified if (capacity >= mSize) { @@ -292,7 +315,7 @@ public: } } - void ensureCapacity(size_t needed) { + void ensureCapacity(size_t const needed) { if (UTILS_UNLIKELY(needed > mCapacity)) { // not enough space, increase the capacity const size_t capacity = (needed > SIZE_MAX / 3) ? needed : (needed * 3 + 1) / 2; @@ -305,7 +328,7 @@ public: // If the arrays don't have enough capacity, the capacity is increased accordingly // (the capacity is set to 3/2 of the asked size). UTILS_NOINLINE - void resize(size_t needed) { + void resize(size_t const needed) { ensureCapacity(needed); resizeNoCheck(needed); if (needed <= mCapacity) { @@ -318,7 +341,7 @@ public: } - inline void swap(size_t i, size_t j) noexcept { + void swap(size_t i, size_t j) noexcept { forEach([i, j](auto p) { using std::swap; swap(p[i], p[j]); @@ -326,7 +349,7 @@ public: } // remove and destroy the last element of each array - inline void pop_back() noexcept { + void pop_back() noexcept { if (mSize) { destroy_each(mSize - 1, mSize); mSize--; @@ -483,80 +506,97 @@ public: using Type = typename SoA::template TypeAt; UTILS_ALWAYS_INLINE Field& operator = (Field&& rhs) noexcept { - soa.elementAt(i) = soa.elementAt(rhs.i); + soa.template elementAt(i) = soa.template elementAt(rhs.i); return *this; } // auto-conversion to the field's type UTILS_ALWAYS_INLINE operator Type&() noexcept { - return soa.elementAt(i); + return soa.template elementAt(i); } UTILS_ALWAYS_INLINE operator Type const&() const noexcept { - return soa.elementAt(i); + return soa.template elementAt(i); } // dereferencing the selected field UTILS_ALWAYS_INLINE Type& operator ->() noexcept { - return soa.elementAt(i); + return soa.template elementAt(i); } UTILS_ALWAYS_INLINE Type const& operator ->() const noexcept { - return soa.elementAt(i); + return soa.template elementAt(i); } // address-of the selected field UTILS_ALWAYS_INLINE Type* operator &() noexcept { - return &soa.elementAt(i); + return &soa.template elementAt(i); } UTILS_ALWAYS_INLINE Type const* operator &() const noexcept { - return &soa.elementAt(i); + return &soa.template elementAt(i); } // assignment to the field UTILS_ALWAYS_INLINE Type const& operator = (Type const& other) noexcept { - return (soa.elementAt(i) = other); + return (soa.template elementAt(i) = other); } UTILS_ALWAYS_INLINE Type const& operator = (Type&& other) noexcept { - return (soa.elementAt(i) = std::forward(other)); + return (soa.template elementAt(i) = std::forward(other)); } // comparisons UTILS_ALWAYS_INLINE bool operator==(Type const& other) const { - return (soa.elementAt(i) == other); + return (soa.template elementAt(i) == other); } UTILS_ALWAYS_INLINE bool operator!=(Type const& other) const { - return (soa.elementAt(i) != other); + return (soa.template elementAt(i) != other); } // calling the field template UTILS_ALWAYS_INLINE decltype(auto) operator()(ARGS&& ... args) noexcept { - return soa.elementAt(i)(std::forward(args)...); + return soa.template elementAt(i)(std::forward(args)...); } template UTILS_ALWAYS_INLINE decltype(auto) operator()(ARGS&& ... args) const noexcept { - return soa.elementAt(i)(std::forward(args)...); + return soa.template elementAt(i)(std::forward(args)...); } }; private: + template + struct Copier { + StructureOfArraysBase* dest; + const StructureOfArraysBase& src; + size_t destOffset; + size_t srcOffset; + size_t count; + + template + void operator()(TypeAt* destPtr) const { + using ElementType = TypeAt; + ElementType const* const s = std::get(src.mArrays) + srcOffset; + ElementType* const d = destPtr + destOffset; + std::copy_n(s, count, d); + } + }; + template - inline std::enable_if_t + std::enable_if_t for_each(std::tuple&, FuncT) {} template - inline std::enable_if_t + std::enable_if_t for_each(std::tuple& t, FuncT f) { f(I, std::get(t)); for_each(t, f); } template - inline std::enable_if_t + std::enable_if_t for_each_index(std::tuple&, FuncT) {} template - inline std::enable_if_t + std::enable_if_t for_each_index(std::tuple& t, FuncT f) { f.template operator()(std::get(t)); for_each_index(t, f); } - inline void resizeNoCheck(size_t needed) noexcept { + void resizeNoCheck(size_t const needed) noexcept { assert(mCapacity >= needed); if (needed < mSize) { // we shrink the arrays @@ -570,12 +610,12 @@ private: } // this calculates the offset adjusted for all data alignment of a given array - static inline size_t getOffset(size_t index, size_t capacity) noexcept { + static size_t getOffset(size_t index, size_t const capacity) noexcept { auto offsets = getOffsets(capacity); return offsets[index]; } - static inline std::array getOffsets(size_t capacity) noexcept { + static std::array getOffsets(size_t capacity) noexcept { // compute the required size of each array const size_t sizes[] = { (sizeof(Elements) * capacity)... }; @@ -618,7 +658,7 @@ private: }); } - void move_each(void* buffer, size_t capacity) noexcept { + void move_each(void* buffer, size_t const capacity) noexcept { auto offsets = getOffsets(capacity); size_t index = 0; if (mSize) { @@ -653,7 +693,7 @@ private: // update the pointers for_each(mArrays, [buffer, &offsets](size_t i, auto&& p) { using Type = std::remove_reference_t; - p = Type((char*)buffer + offsets[i]); + p = Type(static_cast(buffer) + offsets[i]); }); } @@ -668,7 +708,6 @@ private: template -inline typename StructureOfArraysBase::IteratorValueRef& StructureOfArraysBase::IteratorValueRef::operator=( IteratorValueRef const& rhs) { @@ -676,16 +715,28 @@ StructureOfArraysBase::IteratorValueRef::operator=( } template -inline typename StructureOfArraysBase::IteratorValueRef& StructureOfArraysBase::IteratorValueRef::operator=( IteratorValueRef&& rhs) noexcept { return operator=(IteratorValue(rhs)); } +template +typename StructureOfArraysBase::IteratorValueRef& +StructureOfArraysBase::IteratorValueRef::operator=( + Structure const& rhs) { + return assign(rhs, std::make_index_sequence()); +} + +template +typename StructureOfArraysBase::IteratorValueRef& +StructureOfArraysBase::IteratorValueRef::operator=( + Structure&& rhs) noexcept { + return assign(std::move(rhs), std::make_index_sequence()); +} + template template -inline typename StructureOfArraysBase::IteratorValueRef& StructureOfArraysBase::IteratorValueRef::assign( IteratorValue const& rhs, std::index_sequence) { @@ -696,7 +747,24 @@ StructureOfArraysBase::IteratorValueRef::assign( template template -inline +typename StructureOfArraysBase::IteratorValueRef& +StructureOfArraysBase::IteratorValueRef::assign( + Structure const& rhs, std::index_sequence) { + auto UTILS_UNUSED l = {(soa->template elementAt(index) = std::get(rhs), 0)...}; + return *this; +} + +template +template +typename StructureOfArraysBase::IteratorValueRef& +StructureOfArraysBase::IteratorValueRef::assign( + Structure&& rhs, std::index_sequence) noexcept { + auto UTILS_UNUSED l = {(soa->template elementAt(index) = std::move(std::get(rhs)), 0)...}; + return *this; +} + +template +template typename StructureOfArraysBase::IteratorValueRef& StructureOfArraysBase::IteratorValueRef::assign( IteratorValue&& rhs, std::index_sequence) noexcept { diff --git a/libs/utils/test/test_StructureOfArrays.cpp b/libs/utils/test/test_StructureOfArrays.cpp index 652d9d37fe..5cd0223e4f 100644 --- a/libs/utils/test/test_StructureOfArrays.cpp +++ b/libs/utils/test/test_StructureOfArrays.cpp @@ -19,6 +19,11 @@ #include #include +#include +#include +#include +#include + using namespace filament::math; using namespace utils; @@ -71,7 +76,7 @@ TEST(StructureOfArraysTest, Iterator) { EXPECT_EQ(soa.elementAt<0>(2), 3.0f); for (size_t i = 0; i < 8; i++) { - soa.elementAt<0>(i) = (float)std::rand(); + soa.elementAt<0>(i) = float(std::rand()); soa.elementAt<1>(i) = soa.elementAt<0>(i) * 2; soa.elementAt<2>(i) = soa.elementAt<0>(i) * 4; } @@ -103,7 +108,7 @@ TEST(StructureOfArraysTest, Simple) { soa.elementAt<2>(i) = i * 4; } - size_t capacity = soa.capacity(); + size_t const capacity = soa.capacity(); // check that each array doesn't overlap the previous one EXPECT_TRUE((void*)soa.data<1>() >= (void*)(soa.data<0>() + capacity)); @@ -183,3 +188,42 @@ TEST(StructureOfArraysTest, MoveOnly) { EXPECT_EQ(*soa.elementAt<1>(1).get(), 2); } +TEST(StructureOfArraysTest, CopyRange) { + StructureOfArrays soa1; + StructureOfArrays soa2; + + soa1.setCapacity(10); + soa1.resize(5); + for (size_t i = 0; i < 5; i++) { + soa1.elementAt<0>(i) = i; + soa1.elementAt<1>(i) = i * 2; + soa1.elementAt<2>(i) = i * 4; + } + + soa2.setCapacity(10); + soa2.resize(5); + + soa2.copyRange(1, soa1, 1, 3); + + EXPECT_EQ(soa2.elementAt<0>(1), 1.0f); + EXPECT_EQ(soa2.elementAt<1>(1), 2.0); + EXPECT_EQ(soa2.elementAt<2>(1), TestFloat4{ 4.0f }); + + EXPECT_EQ(soa2.elementAt<0>(3), 3.0f); + EXPECT_EQ(soa2.elementAt<1>(3), 6.0); + EXPECT_EQ(soa2.elementAt<2>(3), TestFloat4{ 12.0f }); +} + +TEST(StructureOfArraysTest, AssignTuple) { + StructureOfArrays soa; + soa.setCapacity(10); + soa.resize(5); + + auto tuple = std::make_tuple(1.0f, 2.0, TestFloat4{4.0f}); + soa[2] = tuple; + + EXPECT_EQ(soa.elementAt<0>(2), 1.0f); + EXPECT_EQ(soa.elementAt<1>(2), 2.0); + EXPECT_EQ(soa.elementAt<2>(2), TestFloat4{ 4.0f }); +} +