Browse Source

review: multi component standard view

Michele Caini 7 years ago
parent
commit
ca0a1f8f8b
3 changed files with 63 additions and 94 deletions
  1. 3 3
      README.md
  2. 60 87
      src/entt/entity/view.hpp
  3. 0 4
      test/entt/entity/view.cpp

+ 3 - 3
README.md

@@ -1276,9 +1276,9 @@ entities available for each component and pick up a reference to the smallest
 set of candidates in order to speed up iterations.<br/>
 set of candidates in order to speed up iterations.<br/>
 They offer fewer functionalities than their companion views for single
 They offer fewer functionalities than their companion views for single
 component. In particular, a multi component standard view exposes utility
 component. In particular, a multi component standard view exposes utility
-functions to reset its internal state (optimization purposes) and to get the
-estimated number of entities it is going to return. It's also possible to ask a
-view if it contains a given entity.<br/>
+functions to get the estimated number of entities it is going to return and to
+know whether it's empty or not. It's also possible to ask a view if it contains
+a given entity.<br/>
 Refer to the [official documentation](https://skypjack.github.io/entt/) for all
 Refer to the [official documentation](https://skypjack.github.io/entt/) for all
 the details.
 the details.
 
 

+ 60 - 87
src/entt/entity/view.hpp

@@ -9,7 +9,6 @@
 #include <algorithm>
 #include <algorithm>
 #include <type_traits>
 #include <type_traits>
 #include "../config/config.h"
 #include "../config/config.h"
-#include "../core/ident.hpp"
 #include "entt_traits.hpp"
 #include "entt_traits.hpp"
 #include "sparse_set.hpp"
 #include "sparse_set.hpp"
 
 
@@ -432,16 +431,13 @@ class View final {
     class Iterator {
     class Iterator {
         using size_type = typename view_type::size_type;
         using size_type = typename view_type::size_type;
 
 
-        inline bool valid() const ENTT_NOEXCEPT {
+        bool valid() const ENTT_NOEXCEPT {
             const auto entity = *begin;
             const auto entity = *begin;
             const auto sz = size_type(entity & traits_type::entity_mask);
             const auto sz = size_type(entity & traits_type::entity_mask);
-            auto pos = unchecked.size();
 
 
-            if(sz < extent) {
-                for(; pos && unchecked[pos-1]->fast(entity); --pos);
-            }
-
-            return !pos;
+            return sz < extent && std::all_of(unchecked.cbegin(), unchecked.cend(), [entity](const view_type *view) {
+                return view->fast(entity);
+            });
         }
         }
 
 
     public:
     public:
@@ -499,41 +495,45 @@ class View final {
     };
     };
 
 
     View(pool_type<Component> &... pools) ENTT_NOEXCEPT
     View(pool_type<Component> &... pools) ENTT_NOEXCEPT
-        : pools{pools...}, view{nullptr}, unchecked{}, idx{}
-    {
-        reset();
-    }
-
-    template<typename Comp, typename Other, typename It>
-    inline std::enable_if_t<std::is_same<Comp, Other>::value, const Other &>
-    get(const It &it, Entity) const { return *it; }
+        : pools{pools...}
+    {}
 
 
-    template<typename Comp, typename Other, typename It>
-    inline std::enable_if_t<!std::is_same<Comp, Other>::value, const Other &>
-    get(const It &, Entity entity) const { return std::get<pool_type<Other> &>(pools).get(entity); }
+    template<typename Comp>
+    const pool_type<Comp> & pool() const ENTT_NOEXCEPT {
+        return std::get<pool_type<Comp> &>(pools);
+    }
 
 
-    template<typename Comp, typename Func>
-    void each(Func func) const {
-        const auto extent = std::min({ std::get<pool_type<Component> &>(pools).extent()... });
-        auto &pool = std::get<pool_type<Comp> &>(pools);
+    template<typename Comp>
+    inline pool_type<Comp> & pool() ENTT_NOEXCEPT {
+        return const_cast<pool_type<Comp> &>(const_cast<const View *>(this)->pool<Comp>());
+    }
 
 
-        std::for_each(pool.view_type::cbegin(), pool.view_type::cend(), [func = std::move(func), raw = pool.cbegin(), extent, this](const auto entity) mutable {
-            const auto sz = size_type(entity & traits_type::entity_mask);
+    const view_type * candidate() const ENTT_NOEXCEPT {
+        return std::min({ static_cast<const view_type *>(&pool<Component>())... }, [](const auto *lhs, const auto *rhs) {
+            return lhs->size() < rhs->size();
+        });
+    }
 
 
-            if(sz < extent) {
-                auto pos = unchecked.size();
+    unchecked_type unchecked(const view_type *view) const ENTT_NOEXCEPT {
+        unchecked_type other{};
+        std::size_t pos{};
+        using accumulator_type = const view_type *[];
+        accumulator_type accumulator = { (&pool<Component>() == view ? view : other[pos++] = &pool<Component>())... };
+        (void)accumulator;
+        return other;
+    }
 
 
-                for(; pos && unchecked[pos-1]->fast(entity); --pos);
+    typename view_type::size_type extent() const ENTT_NOEXCEPT {
+        return std::min({ pool<Component>().extent()... });
+    }
 
 
-                if(!pos) {
-                    // avoided indirections due to the sparse set for the pivot (this-> required because of GCC 6)
-                    func(entity, this->get<Comp, Component>(raw, entity)...);
-                }
-            }
+    template<typename Comp, typename Other>
+    inline std::enable_if_t<std::is_same<Comp, Other>::value, const Other &>
+    get(const typename pool_type<Comp>::const_iterator_type &it, Entity) const ENTT_NOEXCEPT { return *it; }
 
 
-            ++raw;
-        });
-    }
+    template<typename Comp, typename Other>
+    inline std::enable_if_t<!std::is_same<Comp, Other>::value, const Other &>
+    get(const typename pool_type<Comp>::const_iterator_type &, Entity entity) const ENTT_NOEXCEPT { return pool<Other>().get(entity); }
 
 
 public:
 public:
     /*! @brief Input iterator type. */
     /*! @brief Input iterator type. */
@@ -550,7 +550,7 @@ public:
      * @return Estimated number of entities that have the given components.
      * @return Estimated number of entities that have the given components.
      */
      */
     size_type size() const ENTT_NOEXCEPT {
     size_type size() const ENTT_NOEXCEPT {
-        return view->size();
+        return std::min({ pool<Component>().size()... });
     }
     }
 
 
     /**
     /**
@@ -558,7 +558,7 @@ public:
      * @return True if the view is definitely empty, false otherwise.
      * @return True if the view is definitely empty, false otherwise.
      */
      */
     bool empty() const ENTT_NOEXCEPT {
     bool empty() const ENTT_NOEXCEPT {
-        return view->empty();
+        return std::max({ pool<Component>().empty()... });
     }
     }
 
 
     /**
     /**
@@ -576,8 +576,8 @@ public:
      * @return An iterator to the first entity that has the given components.
      * @return An iterator to the first entity that has the given components.
      */
      */
     const_iterator_type cbegin() const ENTT_NOEXCEPT {
     const_iterator_type cbegin() const ENTT_NOEXCEPT {
-        const auto extent = std::min({ std::get<pool_type<Component> &>(pools).extent()... });
-        return iterator_type{ unchecked, extent, view->cbegin(), view->cend() };
+        const auto *view = candidate();
+        return iterator_type{ unchecked(view), extent(), view->cbegin(), view->cend() };
     }
     }
 
 
     /**
     /**
@@ -614,8 +614,8 @@ public:
      * given components.
      * given components.
      */
      */
     const_iterator_type cend() const ENTT_NOEXCEPT {
     const_iterator_type cend() const ENTT_NOEXCEPT {
-        const auto extent = std::min({ std::get<pool_type<Component> &>(pools).extent()... });
-        return iterator_type{ unchecked, extent, view->cend(), view->cend() };
+        const auto *view = candidate();
+        return iterator_type{ unchecked(view), extent(), view->cend(), view->cend() };
     }
     }
 
 
     /**
     /**
@@ -643,15 +643,8 @@ public:
      * @return True if the view contains the given entity, false otherwise.
      * @return True if the view contains the given entity, false otherwise.
      */
      */
     bool contains(entity_type entity) const ENTT_NOEXCEPT {
     bool contains(entity_type entity) const ENTT_NOEXCEPT {
-        const auto extent = std::min({ std::get<pool_type<Component> &>(pools).extent()... });
         const auto sz = size_type(entity & traits_type::entity_mask);
         const auto sz = size_type(entity & traits_type::entity_mask);
-        auto pos = unchecked.size();
-
-        if(sz < extent && view->has(entity) && (view->data()[view->get(entity)] == entity)) {
-            for(; pos && unchecked[pos-1]->fast(entity); --pos);
-        }
-
-        return !pos;
+        return sz < extent() && std::min({ (pool<Component>().has(entity) && (pool<Component>().data()[pool<Component>().view_type::get(entity)] == entity))... });
     }
     }
 
 
     /**
     /**
@@ -674,7 +667,7 @@ public:
     template<typename Comp>
     template<typename Comp>
     const Comp & get(entity_type entity) const ENTT_NOEXCEPT {
     const Comp & get(entity_type entity) const ENTT_NOEXCEPT {
         assert(contains(entity));
         assert(contains(entity));
-        return std::get<pool_type<Comp> &>(pools).get(entity);
+        return pool<Comp>().get(entity);
     }
     }
 
 
     /**
     /**
@@ -764,10 +757,23 @@ public:
      * @param func A valid function object.
      * @param func A valid function object.
      */
      */
     template<typename Func>
     template<typename Func>
-    inline void each(Func func) const {
-        constexpr auto indexes = ident<Component...>;
+    void each(Func func) const {
+        auto iterate = [&func, this](const auto &cpool) {
+            std::for_each(cpool.view_type::cbegin(), cpool.view_type::cend(), [&func, raw = cpool.cbegin(), unchecked = this->unchecked(&cpool), extent = this->extent(), this](const auto entity) mutable {
+                const auto sz = size_type(entity & traits_type::entity_mask);
+
+                if(sz < extent && std::all_of(unchecked.cbegin(), unchecked.cend(), [entity](const view_type *view) { return view->fast(entity); })) {
+                    // avoided indirections due to the sparse set for the pivot type
+                    func(entity, this->get<typename std::decay_t<decltype(cpool)>::object_type, Component>(raw, entity)...);
+                }
+
+                ++raw;
+            });
+        };
+
+        const auto *view = candidate();
         using accumulator_type = int[];
         using accumulator_type = int[];
-        accumulator_type accumulator = { (indexes.template get<Component>() == idx ? (each<Component>(std::move(func)), 0) : 0)... };
+        accumulator_type accumulator = { (&pool<Component>() == view ? (iterate(pool<Component>()), 0) : 0)... };
         (void)accumulator;
         (void)accumulator;
     }
     }
 
 
@@ -794,41 +800,8 @@ public:
         });
         });
     }
     }
 
 
-    /**
-     * @brief Resets the view and reinitializes it.
-     *
-     * A multi component view keeps a reference to the smallest set of candidate
-     * entities to iterate. Resetting a view means querying the underlying data
-     * structures and reinitializing the view.<br/>
-     * Use it only if copies of views are stored around and there is a
-     * possibility that a component has become the best candidate in the
-     * meantime.
-     */
-    void reset() {
-        using accumulator_type = size_type[];
-        size_type sz = std::max({ std::get<pool_type<Component> &>(pools).size()... }) + std::size_t{1};
-        size_type next{};
-
-        auto probe = [this](auto sz, const auto &pool) {
-            return pool.size() < sz ? (view = &pool, pool.size()) : sz;
-        };
-
-        auto filter = [this](auto next, const auto &pool) {
-            return (view == &pool) ? (idx = next) : (unchecked[next++] = &pool, next);
-        };
-
-        accumulator_type probing = { (sz = probe(sz, std::get<pool_type<Component> &>(pools)))... };
-        accumulator_type filtering = { (next = filter(next, std::get<pool_type<Component> &>(pools)))... };
-
-        (void)filtering;
-        (void)probing;
-    }
-
 private:
 private:
     const pattern_type pools;
     const pattern_type pools;
-    const view_type *view;
-    unchecked_type unchecked;
-    size_type idx;
 };
 };
 
 
 
 

+ 0 - 4
test/entt/entity/view.cpp

@@ -175,10 +175,6 @@ TEST(View, MultipleComponent) {
 
 
     registry.remove<char>(e0);
     registry.remove<char>(e0);
     registry.remove<char>(e1);
     registry.remove<char>(e1);
-    view.reset();
-
-    ASSERT_EQ(view.begin(), view.end());
-    ASSERT_TRUE(view.empty());
 }
 }
 
 
 TEST(View, MultipleComponentBeginEnd) {
 TEST(View, MultipleComponentBeginEnd) {