From ca0a1f8f8be5bb2ace6cc0e186cb0ffcee8cb31a Mon Sep 17 00:00:00 2001 From: Michele Caini Date: Tue, 15 May 2018 17:30:22 +0200 Subject: [PATCH] review: multi component standard view --- README.md | 6 +- src/entt/entity/view.hpp | 151 ++++++++++++++++---------------------- test/entt/entity/view.cpp | 4 - 3 files changed, 65 insertions(+), 96 deletions(-) diff --git a/README.md b/README.md index e5d076856..b355fbefa 100644 --- a/README.md +++ b/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.
They offer fewer functionalities than their companion views for single 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.
+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.
Refer to the [official documentation](https://skypjack.github.io/entt/) for all the details. diff --git a/src/entt/entity/view.hpp b/src/entt/entity/view.hpp index b58cf0bb5..9e724ec72 100644 --- a/src/entt/entity/view.hpp +++ b/src/entt/entity/view.hpp @@ -9,7 +9,6 @@ #include #include #include "../config/config.h" -#include "../core/ident.hpp" #include "entt_traits.hpp" #include "sparse_set.hpp" @@ -432,16 +431,13 @@ class View final { class Iterator { 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 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: @@ -499,42 +495,46 @@ class View final { }; View(pool_type &... pools) ENTT_NOEXCEPT - : pools{pools...}, view{nullptr}, unchecked{}, idx{} - { - reset(); + : pools{pools...} + {} + + template + const pool_type & pool() const ENTT_NOEXCEPT { + return std::get &>(pools); } - template - inline std::enable_if_t::value, const Other &> - get(const It &it, Entity) const { return *it; } + template + inline pool_type & pool() ENTT_NOEXCEPT { + return const_cast &>(const_cast(this)->pool()); + } - template - inline std::enable_if_t::value, const Other &> - get(const It &, Entity entity) const { return std::get &>(pools).get(entity); } - - template - void each(Func func) const { - const auto extent = std::min({ std::get &>(pools).extent()... }); - auto &pool = std::get &>(pools); - - 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); - - if(sz < extent) { - auto pos = unchecked.size(); - - for(; pos && unchecked[pos-1]->fast(entity); --pos); - - if(!pos) { - // avoided indirections due to the sparse set for the pivot (this-> required because of GCC 6) - func(entity, this->get(raw, entity)...); - } - } - - ++raw; + const view_type * candidate() const ENTT_NOEXCEPT { + return std::min({ static_cast(&pool())... }, [](const auto *lhs, const auto *rhs) { + return lhs->size() < rhs->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() == view ? view : other[pos++] = &pool())... }; + (void)accumulator; + return other; + } + + typename view_type::size_type extent() const ENTT_NOEXCEPT { + return std::min({ pool().extent()... }); + } + + template + inline std::enable_if_t::value, const Other &> + get(const typename pool_type::const_iterator_type &it, Entity) const ENTT_NOEXCEPT { return *it; } + + template + inline std::enable_if_t::value, const Other &> + get(const typename pool_type::const_iterator_type &, Entity entity) const ENTT_NOEXCEPT { return pool().get(entity); } + public: /*! @brief Input iterator type. */ using iterator_type = Iterator; @@ -550,7 +550,7 @@ public: * @return Estimated number of entities that have the given components. */ size_type size() const ENTT_NOEXCEPT { - return view->size(); + return std::min({ pool().size()... }); } /** @@ -558,7 +558,7 @@ public: * @return True if the view is definitely empty, false otherwise. */ bool empty() const ENTT_NOEXCEPT { - return view->empty(); + return std::max({ pool().empty()... }); } /** @@ -576,8 +576,8 @@ public: * @return An iterator to the first entity that has the given components. */ const_iterator_type cbegin() const ENTT_NOEXCEPT { - const auto extent = std::min({ std::get &>(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. */ const_iterator_type cend() const ENTT_NOEXCEPT { - const auto extent = std::min({ std::get &>(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. */ bool contains(entity_type entity) const ENTT_NOEXCEPT { - const auto extent = std::min({ std::get &>(pools).extent()... }); 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().has(entity) && (pool().data()[pool().view_type::get(entity)] == entity))... }); } /** @@ -674,7 +667,7 @@ public: template const Comp & get(entity_type entity) const ENTT_NOEXCEPT { assert(contains(entity)); - return std::get &>(pools).get(entity); + return pool().get(entity); } /** @@ -764,10 +757,23 @@ public: * @param func A valid function object. */ template - inline void each(Func func) const { - constexpr auto indexes = ident; + 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::object_type, Component>(raw, entity)...); + } + + ++raw; + }); + }; + + const auto *view = candidate(); using accumulator_type = int[]; - accumulator_type accumulator = { (indexes.template get() == idx ? (each(std::move(func)), 0) : 0)... }; + accumulator_type accumulator = { (&pool() == view ? (iterate(pool()), 0) : 0)... }; (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.
- * 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 &>(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 &>(pools)))... }; - accumulator_type filtering = { (next = filter(next, std::get &>(pools)))... }; - - (void)filtering; - (void)probing; - } - private: const pattern_type pools; - const view_type *view; - unchecked_type unchecked; - size_type idx; }; diff --git a/test/entt/entity/view.cpp b/test/entt/entity/view.cpp index 230503a39..c335820af 100644 --- a/test/entt/entity/view.cpp +++ b/test/entt/entity/view.cpp @@ -175,10 +175,6 @@ TEST(View, MultipleComponent) { registry.remove(e0); registry.remove(e1); - view.reset(); - - ASSERT_EQ(view.begin(), view.end()); - ASSERT_TRUE(view.empty()); } TEST(View, MultipleComponentBeginEnd) {