Преглед изворни кода

registry: disable optimizations based on is_iterator_type, free pools allow to implement them on the user side and more efficiently when needed

Michele Caini пре 4 година
родитељ
комит
a8be765aa8
3 измењених фајлова са 8 додато и 75 уклоњено
  1. 0 1
      TODO
  2. 0 41
      docs/md/entity.md
  3. 8 33
      src/entt/entity/registry.hpp

+ 0 - 1
TODO

@@ -4,7 +4,6 @@
 * add examples (and credits) from @alanjfs :)
 
 WIP:
-* Making the most of range-destroy: disable this optimization, free pools make it possible to implement it on user side when needed
 * investigate: meta as_type_t<T> for getters and the like
 * investigate: add the possibility of disabling entities without deleting components thanks to the new full check
 * fast-contains for sparse sets (low prio but nice-to-have)

+ 0 - 41
docs/md/entity.md

@@ -30,7 +30,6 @@
   * [Pointer stability](#pointer-stability)
     * [In-place delete](#in-place-delete)
     * [Hierarchies and the like](#hierarchies-and-the-like)
-  * [Making the most of range-destroy](#making-the-most-of-range-destroy)
   * [Meet the runtime](#meet-the-runtime)
     * [A base class to rule them all](#a-base-class-to-rule-them-all)
     * [Beam me up, registry](#beam-me-up-registry)
@@ -1035,46 +1034,6 @@ time and therefore fallback into adjacent positions, thus favoring locality even
 on random accesses. Locality that won't be sacrificed over time given the
 stability of storage positions, with undoubted performance advantages.
 
-## Making the most of range-destroy
-
-The range-destroy functionality offers an improved path under the hood. To
-understand it, let's try to describe what problem it tries to solve.<br/>
-This function accepts two iterators that point to the beginning and end of a
-range of entities. If the iterators are those returned from a view, this pair
-cannot be passed to the first storage asking to remove all entities and then to
-all other storage. This is because the range may be empty when passed to the
-second pool, as not all of those entities still own all the components iterated
-from the view itself.<br/>
-As a result, only one component is removed and no entities are destroyed.
-
-To avoid this, in many cases the registry doesn't pass the range to all pools.
-Instead, it iterates the range and passes an entity at a time to all pools.<br/>
-It goes without saying that the latter is slightly slower than the former.
-
-On the other side, the `destroy` function also uses `is_iterator_type` under the
-hood to detect _dangerous_ iterators. Whenever possible, it still chooses the
-fastest path.<br/>
-This means that performance will improve if, for example, two iterators returned
-from an `std::vector` are used or, more in general, with all iterators that are
-not part of `EnTT`.
-
-Unfortunately, this risks falling into the error described above in some corner
-cases. In particular, where an iterator is used that is not defined by `EnTT`
-but which uses one of the latter _within_ it.<br/>
-It's quite unlikely to happen even in large software. However, the library
-offers a solution also in this case, so as to allow for custom iterators and
-better performance at the same time.<br/>
-In particular, it's necessary to either expose the member type `iterator_type`
-and declare that an iterator from `EnTT` is used internally or specialize the
-`is_iterator_type` class to drive the choice of the `destroy` function.<br/>
-In both cases, the aim is to not choose the optimized route if it can cause
-problems.
-
-With a good chance, the last note can be ignored and there will never be a need
-to do the above even after writing millions of lines of code.<br/>
-However, it's good to know how to exploit the `destroy` function to get the best
-out of it.
-
 ## Meet the runtime
 
 `EnTT` takes advantage of what the language offers at compile-time. However,

+ 8 - 33
src/entt/entity/registry.hpp

@@ -718,18 +718,8 @@ public:
      */
     template<typename It>
     void destroy(It first, It last) {
-        ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); }), "Invalid entity");
-
-        if constexpr(is_iterator_type_v<typename base_type::iterator, It>) {
-            for(; first != last; ++first) {
-                destroy(*first, entity_traits::to_version(*first) + 1u);
-            }
-        } else {
-            for(auto &&curr: pools) {
-                curr.second->remove(first, last);
-            }
-
-            release(first, last);
+        for(; first != last; ++first) {
+            destroy(*first, entity_traits::to_version(*first) + 1u);
         }
     }
 
@@ -885,24 +875,17 @@ public:
      * @sa remove
      *
      * @tparam Component Types of components to remove.
-     * @tparam Other Other types of components to remove.
      * @tparam It Type of input iterator.
      * @param first An iterator to the first element of the range of entities.
      * @param last An iterator past the last element of the range of entities.
      * @return The number of components actually removed.
      */
-    template<typename Component, typename... Other, typename It>
+    template<typename... Component, typename It>
     size_type remove(It first, It last) {
-        ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); }), "Invalid entity");
         size_type count{};
 
-        if constexpr(is_iterator_type_v<typename base_type::iterator, It> && sizeof...(Other) != 0u) {
-            for(const auto cpools = std::forward_as_tuple(assure<Component>(), assure<Other>()...); first != last; ++first) {
-                const auto entity = *first;
-                count += (std::get<storage_type<Component> &>(cpools).remove(entity) + ... + std::get<storage_type<Other> &>(cpools).remove(entity));
-            }
-        } else {
-            count = (assure<Component>().remove(first, last) + ... + assure<Other>().remove(first, last));
+        for(; first != last; ++first) {
+            count += remove<Component...>(*first);
         }
 
         return count;
@@ -931,22 +914,14 @@ public:
      * @sa erase
      *
      * @tparam Component Types of components to erase.
-     * @tparam Other Other types of components to erase.
      * @tparam It Type of input iterator.
      * @param first An iterator to the first element of the range of entities.
      * @param last An iterator past the last element of the range of entities.
      */
-    template<typename Component, typename... Other, typename It>
+    template<typename... Component, typename It>
     void erase(It first, It last) {
-        ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); }), "Invalid entity");
-
-        if constexpr(is_iterator_type_v<typename base_type::iterator, It> && sizeof...(Other) != 0u) {
-            for(const auto cpools = std::forward_as_tuple(assure<Component>(), assure<Other>()...); first != last; ++first) {
-                const auto entity = *first;
-                (std::get<storage_type<Component> &>(cpools).erase(entity), (std::get<storage_type<Other> &>(cpools).erase(entity), ...));
-            }
-        } else {
-            (assure<Component>().erase(first, last), (assure<Other>().erase(first, last), ...));
+        for(; first != last; ++first) {
+            erase<Component...>(*first);
         }
     }