[Bf-blender-cvs] [488fc4eb50c] virtual-array-attributes: more virtual arrays

Jacques Lucke noreply at git.blender.org
Mon Apr 12 18:27:52 CEST 2021


Commit: 488fc4eb50c860d6fc51bd628bce2ae23afb1775
Author: Jacques Lucke
Date:   Sat Apr 10 13:55:11 2021 +0200
Branches: virtual-array-attributes
https://developer.blender.org/rB488fc4eb50c860d6fc51bd628bce2ae23afb1775

more virtual arrays

===================================================================

M	source/blender/blenlib/BLI_virtual_array.hh
M	source/blender/blenlib/tests/BLI_virtual_array_test.cc

===================================================================

diff --git a/source/blender/blenlib/BLI_virtual_array.hh b/source/blender/blenlib/BLI_virtual_array.hh
index 371703d4038..71d1699d20d 100644
--- a/source/blender/blenlib/BLI_virtual_array.hh
+++ b/source/blender/blenlib/BLI_virtual_array.hh
@@ -37,6 +37,7 @@
  * see of the increased compile time and binary size is worth it.
  */
 
+#include "BLI_array.hh"
 #include "BLI_span.hh"
 
 namespace blender {
@@ -116,6 +117,18 @@ template<typename T> class VArray {
     return this->get(index);
   }
 
+  void materialize(MutableSpan<T> r_span) const
+  {
+    BLI_assert(size_ == r_span.size());
+    this->materialize_impl(r_span);
+  }
+
+  void materialize_to_uninitialized(MutableSpan<T> r_span) const
+  {
+    BLI_assert(size_ == r_span.size());
+    this->materialize_to_uninitialized_impl(r_span);
+  }
+
  protected:
   virtual T get_impl(const int64_t index) const = 0;
 
@@ -142,6 +155,43 @@ template<typename T> class VArray {
     BLI_assert_unreachable();
     return T();
   }
+
+  virtual void materialize_impl(MutableSpan<T> r_span) const
+  {
+    if (this->is_span()) {
+      const Span<T> span = this->get_span();
+      initialized_copy_n(span.data(), size_, r_span.data());
+    }
+    else if (this->is_single()) {
+      const T single = this->get_single();
+      initialized_fill_n(r_span.data(), size_, single);
+    }
+    else {
+      const int64_t size = size_;
+      for (int64_t i = 0; i < size; i++) {
+        r_span[i] = this->get(i);
+      }
+    }
+  }
+
+  virtual void materialize_to_uninitialized_impl(MutableSpan<T> r_span) const
+  {
+    if (this->is_span()) {
+      const Span<T> span = this->get_span();
+      uninitialized_copy_n(span.data(), size_, r_span.data());
+    }
+    else if (this->is_single()) {
+      const T single = this->get_single();
+      uninitialized_fill_n(r_span.data(), size_, single);
+    }
+    else {
+      const int64_t size = size_;
+      T *dst = r_span.data();
+      for (int64_t i = 0; i < size; i++) {
+        new (dst + i) T(this->get(i));
+      }
+    }
+  }
 };
 
 /**
@@ -157,14 +207,14 @@ template<typename T> class VArrayForSpan : public VArray<T> {
   {
   }
 
-  /* When this constructor is used, the #set_data method has to be used as well. */
+  /* When this constructor is used, the #set_span_start method has to be used as well. */
   VArrayForSpan(const int64_t size) : VArray<T>(size)
   {
   }
 
  protected:
   /* Can be used when the data pointer is not ready when the constructor is called. */
-  void set_data(const T *data)
+  void set_span_start(const T *data)
   {
     data_ = data;
   }
@@ -200,7 +250,7 @@ class VArrayForArrayContainer : public VArrayForSpan<T> {
   VArrayForArrayContainer(Container container)
       : VArrayForSpan<T>((int64_t)container.size()), container_(std::move(container))
   {
-    this->set_data(container_.data());
+    this->set_span_start(container_.data());
   }
 };
 
@@ -245,6 +295,68 @@ template<typename T> class VArrayForSingle final : public VArray<T> {
   }
 };
 
+/**
+ * In many cases a virtual array is a span internally. In those cases, access to individual could
+ * be much more efficient than calling a virtual method. When the underlying virtual array is not a
+ * span, this class allocates a new array and copies the values over.
+ *
+ * This should be used in those cases:
+ *  - All elements in the virtual array are accessed multiple times.
+ *  - In most cases, the underlying virtual array is a span, so no copy is necessary to benefit
+ *    from faster access.
+ *  - An API is called, that does not accept virtual arrays, but only spans.
+ */
+template<typename T> class VArrayAsSpan final : public VArrayForSpan<T> {
+ private:
+  const VArray<T> &varray_;
+  Array<T> owned_data_;
+
+ public:
+  VArrayAsSpan(const VArray<T> &varray) : VArrayForSpan<T>(varray.size()), varray_(varray)
+  {
+    if (varray_.is_span()) {
+      this->set_span_start(varray_.get_span().data());
+    }
+    else {
+      owned_data_.~Array();
+      new (&owned_data_) Array<T>(varray_.size(), NoInitialization{});
+      varray_.materialize_to_uninitialized(owned_data_);
+      this->set_span_start(owned_data_.data());
+    }
+  }
+
+  Span<T> as_span() const
+  {
+    return this->get_span();
+  }
+
+  operator Span<T>() const
+  {
+    return this->get_span();
+  }
+};
+
+/**
+ * This class makes it easy to create a virtual array for an existing function or lambda. The
+ * `GetFunc` should take a single `index` argument and return the value at that index.
+ */
+template<typename T, typename GetFunc> class VArrayForFunc final : public VArray<T> {
+ private:
+  GetFunc get_func_;
+
+ public:
+  VArrayForFunc(const int64_t size, GetFunc get_func)
+      : VArray<T>(size), get_func_(std::move(get_func))
+  {
+  }
+
+ private:
+  virtual T get_impl(const int64_t index) const
+  {
+    return get_func_(index);
+  }
+};
+
 /**
  * Generate multiple versions of the given function optimized for different virtual arrays.
  * One has to be careful with nesting multiple devirtualizations, because that results in an
diff --git a/source/blender/blenlib/tests/BLI_virtual_array_test.cc b/source/blender/blenlib/tests/BLI_virtual_array_test.cc
index 0f568f63d79..c438ff90040 100644
--- a/source/blender/blenlib/tests/BLI_virtual_array_test.cc
+++ b/source/blender/blenlib/tests/BLI_virtual_array_test.cc
@@ -95,4 +95,27 @@ TEST(virtual_array, ForVectorSet)
   EXPECT_EQ(varray[3], 1);
 }
 
+TEST(virtual_array, ForFunc)
+{
+  auto func = [](int64_t index) { return (int)(index * index); };
+  VArrayForFunc<int, decltype(func)> varray{10, func};
+  EXPECT_EQ(varray.size(), 10);
+  EXPECT_EQ(varray[0], 0);
+  EXPECT_EQ(varray[3], 9);
+  EXPECT_EQ(varray[9], 81);
+}
+
+TEST(virtual_array, AsSpan)
+{
+  auto func = [](int64_t index) { return (int)(10 * index); };
+  VArrayForFunc<int, decltype(func)> func_varray{10, func};
+  VArrayAsSpan span_varray{func_varray};
+  EXPECT_EQ(span_varray.size(), 10);
+  Span<int> span = span_varray;
+  EXPECT_EQ(span.size(), 10);
+  EXPECT_EQ(span[0], 0);
+  EXPECT_EQ(span[3], 30);
+  EXPECT_EQ(span[6], 60);
+}
+
 }  // namespace blender::tests



More information about the Bf-blender-cvs mailing list