utils: Improve StructureOfArrays (#9936)

- Added `copyRange()` to `StructureOfArrays` to efficiently copy a range
  of elements from another SoA of the same type. It uses `std::copy_n`
  which leverages `memcpy` for trivially copyable types.
- Added `operator[]` to `StructureOfArrays` returning `IteratorValueRef`.
- Added `operator=` for `Structure` (tuple) to `IteratorValueRef` to
  allow direct assignment: `soa[i] = tuple`.
- Added unit tests for these new methods in `test_StructureOfArrays.cpp`.
This commit is contained in:
Mathias Agopian
2026-04-29 09:06:26 -07:00
committed by GitHub
parent 25a12b47c9
commit 9c27bfa3e1
2 changed files with 154 additions and 42 deletions

View File

@@ -19,6 +19,11 @@
#include <utils/StructureOfArrays.h>
#include <math/vec4.h>
#include <cstdint>
#include <cstdlib>
#include <memory>
#include <utility>
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<float, double, TestFloat4> soa1;
StructureOfArrays<float, double, TestFloat4> 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<float, double, TestFloat4> 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 });
}