runtime view
This commit is contained in:
138
README.md
138
README.md
@@ -36,6 +36,7 @@
|
||||
* [Multi component standard view](#multi-component-standard-view)
|
||||
* [Persistent View](#persistent-view)
|
||||
* [Raw View](#raw-view)
|
||||
* [Runtime View](#runtime-view)
|
||||
* [Give me everything](#give-me-everything)
|
||||
* [Iterations: what is allowed and what is not](#iterations-what-is-allowed-and-what-is-not)
|
||||
* [Multithreading](#multithreading)
|
||||
@@ -211,18 +212,14 @@ Dell XPS 13 out of the mid 2014):
|
||||
|-----------|-------------|-------------|
|
||||
| Create 1M entities | 0.0147s | **0.0046s** |
|
||||
| Destroy 1M entities | 0.0053s | **0.0045s** |
|
||||
| Standard view, 1M entities, one component | 0.0012s | **1.9e-07s** |
|
||||
| Standard view, 1M entities, two components | 0.0012s | **3.8e-07s** |
|
||||
| Standard view, 1M entities, two components<br/>Half of the entities have all the components | 0.0009s | **3.8e-07s** |
|
||||
| Standard view, 1M entities, two components<br/>One of the entities has all the components | 0.0008s | **1.0e-06s** |
|
||||
| Persistent view, 1M entities, two components | 0.0012s | **2.8e-07s** |
|
||||
| Standard view, 1M entities, five components | 0.0010s | **7.0e-07s** |
|
||||
| Persistent view, 1M entities, five components | 0.0010s | **2.8e-07s** |
|
||||
| Standard view, 1M entities, ten components | 0.0011s | **1.2e-06s** |
|
||||
| Standard view, 1M entities, ten components<br/>Half of the entities have all the components | 0.0010s | **1.2e-06s** |
|
||||
| Standard view, 1M entities, ten components<br/>One of the entities has all the components | 0.0008s | **1.2e-06s** |
|
||||
| Persistent view, 1M entities, ten components | 0.0011s | **3.0e-07s** |
|
||||
| Raw view, 1M entities | - | **2.2e-07s** |
|
||||
| 1M entities, one component | 0.0012s | **1.9e-07s** |
|
||||
| 1M entities, two components | 0.0012s | **3.8e-07s** |
|
||||
| 1M entities, two components<br/>Half of the entities have all the components | 0.0009s | **3.8e-07s** |
|
||||
| 1M entities, two components<br/>One of the entities has all the components | 0.0008s | **1.0e-06s** |
|
||||
| 1M entities, five components | 0.0010s | **7.0e-07s** |
|
||||
| 1M entities, ten components | 0.0011s | **1.2e-06s** |
|
||||
| 1M entities, ten components<br/>Half of the entities have all the components | 0.0010s | **1.2e-06s** |
|
||||
| 1M entities, ten components<br/>One of the entities has all the components | 0.0008s | **1.2e-06s** |
|
||||
| Sort 150k entities, one component<br/>Arrays are in reverse order | - | **0.0036s** |
|
||||
| Sort 150k entities, enforce permutation<br/>Arrays are in reverse order | - | **0.0005s** |
|
||||
| Sort 150k entities, one component<br/>Arrays are almost sorted, std::sort | - | **0.0035s** |
|
||||
@@ -715,9 +712,9 @@ created or destroyed.
|
||||
|
||||
### Runtime components
|
||||
|
||||
Defining components at runtime is useful to support plugins and mods in general.
|
||||
However, it seems impossible with a tool designed around a bunch of templates.
|
||||
Indeed it's not that difficult.<br/>
|
||||
Defining components at runtime is useful to support plugin systems and mods in
|
||||
general. However, it seems impossible with a tool designed around a bunch of
|
||||
templates. Indeed it's not that difficult.<br/>
|
||||
Of course, some features cannot be easily exported into a runtime
|
||||
environment. As an example, sorting a group of components defined at runtime
|
||||
isn't for free if compared to most of the other operations. However, the basic
|
||||
@@ -1175,9 +1172,9 @@ view can only iterate entities and their components, then read or update the
|
||||
data members of the latter.<br/>
|
||||
It is a subtle difference that can help designing a better software sometimes.
|
||||
|
||||
There are mainly three kinds of views: standard (also known as `View`),
|
||||
persistent (also known as `PersistentView`) and raw (also known as
|
||||
`RawView`).<br/>
|
||||
There are mainly four kinds of views: standard (also known as `View`),
|
||||
persistent (also known as `PersistentView`), raw (also known as `RawView`) and
|
||||
runtime (also known as `RuntimeView`).<br/>
|
||||
All of them have pros and cons to take in consideration. In particular:
|
||||
|
||||
* Standard views:
|
||||
@@ -1232,6 +1229,21 @@ All of them have pros and cons to take in consideration. In particular:
|
||||
* They can be used to iterate only one type of component at a time.
|
||||
* They don't return the entity to which a component belongs to the caller.
|
||||
|
||||
* Runtime views:
|
||||
|
||||
Pros:
|
||||
|
||||
* Their lists of components are defined at runtime and not at compile-time.
|
||||
* Creating and destroying them isn't expensive at all because they don't have
|
||||
any type of initialization.
|
||||
* They are the best tool for things like plugin systems and mods in general.
|
||||
* They don't affect any other operations of the registry.
|
||||
|
||||
Cons:
|
||||
|
||||
* Their performances are definitely lower than those of all the other views,
|
||||
although they are still usable and sufficient for most of the purposes.
|
||||
|
||||
To sum up and as a rule of thumb:
|
||||
|
||||
* Use a raw view to iterate components only (no entities) for a given type.
|
||||
@@ -1250,6 +1262,8 @@ To sum up and as a rule of thumb:
|
||||
entities but the intersection between the sets of entities is small.
|
||||
* Prepare and use a persistent view in all the cases where a standard view
|
||||
wouldn't fit well otherwise.
|
||||
* Finally, in case you don't know at compile-time what are the components to
|
||||
use, choose a runtime view and set them during execution.
|
||||
|
||||
To easily iterate entities and components, all the views offer the common
|
||||
`begin` and `end` member functions that allow users to use a view in a typical
|
||||
@@ -1288,7 +1302,7 @@ There is no need to store views around for they are extremely cheap to
|
||||
construct, even though they can be copied without problems and reused freely. In
|
||||
fact, they return newly created and correctly initialized iterators whenever
|
||||
`begin` or `end` are invoked.<br/>
|
||||
To iterate a single component standard view, either use it in range-for loop:
|
||||
To iterate a single component standard view, either use it in a range-for loop:
|
||||
|
||||
```cpp
|
||||
auto view = registry.view<Renderable>();
|
||||
@@ -1309,8 +1323,8 @@ registry.view<Renderable>().each([](auto entity, auto &renderable) {
|
||||
});
|
||||
```
|
||||
|
||||
Performance are more or less the same. The best approach depends mainly on
|
||||
whether all the components have to be accessed or not.
|
||||
The `each` member function is highly optimized. Unless users want to iterate
|
||||
only entities, using `each` should be the preferred approach.
|
||||
|
||||
**Note**: prefer the `get` member function of a view instead of the `get` member
|
||||
function template of a registry during iterations, if possible. However, keep in
|
||||
@@ -1333,7 +1347,7 @@ There is no need to store views around for they are extremely cheap to
|
||||
construct, even though they can be copied without problems and reused freely. In
|
||||
fact, they return newly created and correctly initialized iterators whenever
|
||||
`begin` or `end` are invoked.<br/>
|
||||
To iterate a multi component standard view, either use it in range-for loop:
|
||||
To iterate a multi component standard view, either use it in a range-for loop:
|
||||
|
||||
```cpp
|
||||
auto view = registry.view<Position, Velocity>();
|
||||
@@ -1359,8 +1373,9 @@ registry.view<Position, Velocity>().each([](auto entity, auto &position, auto &v
|
||||
});
|
||||
```
|
||||
|
||||
Performance are more or less the same. The best approach depends mainly on
|
||||
whether all the components have to be accessed or not.
|
||||
The `each` member function is highly optimized. Unless users want to iterate
|
||||
only entities or get only some of the components, using `each` should be the
|
||||
preferred approach.
|
||||
|
||||
**Note**: prefer the `get` member function of a view instead of the `get` member
|
||||
function template of a registry during iterations, if possible. However, keep in
|
||||
@@ -1406,7 +1421,7 @@ of the components for which it has been constructed. It's also possible to ask a
|
||||
view if it contains a given entity.<br/>
|
||||
Refer to the inline documentation for all the details.
|
||||
|
||||
To iterate a persistent view, either use it in range-for loop:
|
||||
To iterate a persistent view, either use it in a range-for loop:
|
||||
|
||||
```cpp
|
||||
auto view = registry.view<Position, Velocity>(entt::persistent_t{});
|
||||
@@ -1461,7 +1476,7 @@ There is no need to store views around for they are extremely cheap to
|
||||
construct, even though they can be copied without problems and reused freely. In
|
||||
fact, they return newly created and correctly initialized iterators whenever
|
||||
`begin` or `end` are invoked.<br/>
|
||||
To iterate a raw view, use it in range-for loop:
|
||||
To iterate a raw view, use it in a range-for loop:
|
||||
|
||||
```cpp
|
||||
auto view = registry.view<Renderable>(entt::raw_t{});
|
||||
@@ -1471,9 +1486,74 @@ for(auto &&component: raw) {
|
||||
}
|
||||
```
|
||||
|
||||
**Note**: raw views don't have the `each` nor the `get` member function for
|
||||
obvious reasons. The former would only return the components and therefore it
|
||||
would be redundant, the latter isn't required at all.
|
||||
Or rely on the `each` member function:
|
||||
|
||||
```cpp
|
||||
registry.view<Renderable>(entt::raw_t{}).each([](auto &renderable) {
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
Performance are exactly the same in both cases.
|
||||
|
||||
**Note**: raw views don't have a `get` member function for obvious reasons.
|
||||
|
||||
### Runtime View
|
||||
|
||||
Runtime views iterate entities that have at least all the given components in
|
||||
their bags. During construction, these views look at the number of entities
|
||||
available for each component and pick up a reference to the smallest
|
||||
set of candidates in order to speed up iterations.<br/>
|
||||
They offer more or less the same functionalities of a multi component standard
|
||||
view. However, they don't expose a `get` member function and users should refer
|
||||
to the registry that generated the view to access components. In particular, a
|
||||
runtime view exposes utility 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 inline documentation for all the details.
|
||||
|
||||
Runtime view are extremely cheap to construct and should not be stored around in
|
||||
any case. They should be used immediately after creation and then they should be
|
||||
thrown away. The reasons for this go far beyond the scope of this document.<br/>
|
||||
To iterate a runtime view, either use it in a range-for loop:
|
||||
|
||||
```cpp
|
||||
using component_type = typename decltype(registry)::component_type;
|
||||
component_type types[] = { registry.type<Position>(), registry.type<Velocity>() };
|
||||
|
||||
auto view = registry.view(std::cbegin(types), std::cend(types));
|
||||
|
||||
for(auto entity: view) {
|
||||
// a component at a time ...
|
||||
Position &position = registry.get<Position>(entity);
|
||||
Velocity &velocity = registry.get<Velocity>(entity);
|
||||
|
||||
// ... or multiple components at once
|
||||
std::tuple<Position &, Velocity &> tup = view.get<Position, Velocity>(entity);
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Or rely on the `each` member function to iterate entities:
|
||||
|
||||
```cpp
|
||||
using component_type = typename decltype(registry)::component_type;
|
||||
component_type types[] = { registry.type<Position>(), registry.type<Velocity>() };
|
||||
|
||||
auto view = registry.view(std::cbegin(types), std::cend(types)).each([](auto entity) {
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
Performance are exactly the same in both cases.
|
||||
|
||||
**Note**: runtime views are meant for all those cases where users don't know at
|
||||
compile-time what components to use to iterate entities. This is particularly
|
||||
well suited to plugin systems and mods in general. Where possible, don't use
|
||||
runtime views, as their performance are slightly inferior to those of the other
|
||||
views.
|
||||
|
||||
|
||||
### Give me everything
|
||||
|
||||
|
||||
3
TODO
3
TODO
@@ -2,14 +2,11 @@
|
||||
* scene management (I prefer the concept of spaces, that is a kind of scene anyway)
|
||||
* review doc: separate it in multiple md/dox files, reduce the readme to a minimum and provide users with links to the online documentation on gh-pages
|
||||
* debugging tools (#60): the issue online already contains interesting tips on this, look at it
|
||||
* dynamic view, useful for runtime ecs, can be filled with the desired pool at runtime and are not constrained to a compile-time list of components
|
||||
* define basic reactive systems (track entities to which component is attached, track entities from which component is removed, and so on)
|
||||
* define systems as composable mixins (initializazion, reactive, update, whatever) with flexible auto-detected arguments (registry, views, etc)
|
||||
* create dedicated flat map based on types implementation (sort of "type map") for types to use within the registry and so on...
|
||||
* ease the assignment of tags as string (type-less assign member function + user defined literal for hashed strings)
|
||||
* break snapshot <-> registry dependency (and get rid of utility.hpp)
|
||||
* family -> std::uint32_t (or some other fixed and known size type)
|
||||
* work stealing job system (see #100)
|
||||
* more on runtime stuff
|
||||
* C++17. That's all.
|
||||
* AOB
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cassert>
|
||||
#include <iterator>
|
||||
#include <algorithm>
|
||||
#include <type_traits>
|
||||
#include "../config/config.h"
|
||||
@@ -74,13 +75,13 @@ class Registry {
|
||||
};
|
||||
|
||||
template<typename Component>
|
||||
bool managed() const ENTT_NOEXCEPT {
|
||||
inline bool managed() const ENTT_NOEXCEPT {
|
||||
const auto ctype = component_family::type<Component>();
|
||||
return ctype < pools.size() && std::get<0>(pools[ctype]);
|
||||
}
|
||||
|
||||
template<typename Component>
|
||||
const SparseSet<Entity, Component> & pool() const ENTT_NOEXCEPT {
|
||||
inline const SparseSet<Entity, Component> & pool() const ENTT_NOEXCEPT {
|
||||
assert(managed<Component>());
|
||||
const auto ctype = component_family::type<Component>();
|
||||
return static_cast<SparseSet<Entity, Component> &>(*std::get<0>(pools[ctype]));
|
||||
@@ -1298,6 +1299,7 @@ public:
|
||||
* @see View<Entity, Component>
|
||||
* @see PersistentView
|
||||
* @see RawView
|
||||
* @see RuntimeView
|
||||
*
|
||||
* @tparam Component Type of components used to construct the view.
|
||||
* @return A newly created standard view.
|
||||
@@ -1432,6 +1434,7 @@ public:
|
||||
* @see View<Entity, Component>
|
||||
* @see PersistentView
|
||||
* @see RawView
|
||||
* @see RuntimeView
|
||||
*
|
||||
* @tparam Component Types of components used to construct the view.
|
||||
* @return A newly created persistent view.
|
||||
@@ -1461,6 +1464,7 @@ public:
|
||||
* @see View<Entity, Component>
|
||||
* @see PersistentView
|
||||
* @see RawView
|
||||
* @see RuntimeView
|
||||
*
|
||||
* @tparam Component Type of component used to construct the view.
|
||||
* @return A newly created raw view.
|
||||
@@ -1471,6 +1475,44 @@ public:
|
||||
return RawView<Entity, Component>{pool<Component>()};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a runtime view for the given components.
|
||||
*
|
||||
* This kind of views are created on the fly and share with the registry its
|
||||
* internal data structures.<br/>
|
||||
* Users should throw away the view after use. Fortunately, creating and
|
||||
* destroying a view is an incredibly cheap operation because they do not
|
||||
* require any type of initialization.<br/>
|
||||
* As a rule of thumb, storing a view should never be an option.
|
||||
*
|
||||
* Runtime views are well suited when users want to construct a view from
|
||||
* some external inputs and don't know at compile-time what are the required
|
||||
* components.<br/>
|
||||
* This is particularly well suited to plugin systems and mods in general.
|
||||
*
|
||||
* @see View
|
||||
* @see View<Entity, Component>
|
||||
* @see PersistentView
|
||||
* @see RawView
|
||||
* @see RuntimeView
|
||||
*
|
||||
* @tparam It Type of forward iterator.
|
||||
* @param first An iterator to the first element of the range of components.
|
||||
* @param last An iterator past the last element of the range of components.
|
||||
* @return A newly created runtime view.
|
||||
*/
|
||||
template<typename It>
|
||||
RuntimeView<Entity> view(It first, It last) {
|
||||
static_assert(std::is_convertible<typename std::iterator_traits<It>::value_type, component_type>::value, "!");
|
||||
std::vector<const SparseSet<Entity> *> set(last - first);
|
||||
|
||||
std::transform(first, last, set.begin(), [this](const component_type ctype) {
|
||||
return ctype < pools.size() ? std::get<0>(pools[ctype]).get() : nullptr;
|
||||
});
|
||||
|
||||
return RuntimeView<Entity>{std::move(set)};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a temporary object to use to create snapshots.
|
||||
*
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <cassert>
|
||||
#include <array>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
#include <type_traits>
|
||||
@@ -61,6 +62,7 @@ class Registry;
|
||||
* @sa View
|
||||
* @sa View<Entity, Component>
|
||||
* @sa RawView
|
||||
* @sa RuntimeView
|
||||
*
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
* @tparam Component Types of components iterated by the view.
|
||||
@@ -455,6 +457,7 @@ private:
|
||||
* @sa View<Entity, Component>
|
||||
* @sa PersistentView
|
||||
* @sa RawView
|
||||
* @sa RuntimeView
|
||||
*
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
* @tparam Component Types of components iterated by the view.
|
||||
@@ -481,7 +484,7 @@ class View final {
|
||||
class Iterator {
|
||||
friend class View<Entity, Component...>;
|
||||
|
||||
using size_type = typename view_type::size_type;
|
||||
using extent_type = typename view_type::size_type;
|
||||
|
||||
Iterator(unchecked_type unchecked, underlying_iterator_type begin, underlying_iterator_type end) ENTT_NOEXCEPT
|
||||
: unchecked{unchecked},
|
||||
@@ -495,7 +498,7 @@ class View final {
|
||||
}
|
||||
|
||||
template<std::size_t... Indexes>
|
||||
size_type min(std::index_sequence<Indexes...>) const ENTT_NOEXCEPT {
|
||||
extent_type min(std::index_sequence<Indexes...>) const ENTT_NOEXCEPT {
|
||||
return std::min({ std::get<Indexes>(unchecked)->extent()... });
|
||||
}
|
||||
|
||||
@@ -549,7 +552,7 @@ class View final {
|
||||
unchecked_type unchecked;
|
||||
underlying_iterator_type begin;
|
||||
underlying_iterator_type end;
|
||||
size_type extent;
|
||||
extent_type extent;
|
||||
};
|
||||
|
||||
View(pool_type<Component> &... pools) ENTT_NOEXCEPT
|
||||
@@ -658,7 +661,7 @@ public:
|
||||
*/
|
||||
const_iterator_type cbegin() const ENTT_NOEXCEPT {
|
||||
const auto *view = candidate();
|
||||
return iterator_type{ unchecked(view), view->cbegin(), view->cend() };
|
||||
return const_iterator_type{unchecked(view), view->cbegin(), view->cend()};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -714,7 +717,7 @@ public:
|
||||
*/
|
||||
const_iterator_type cend() const ENTT_NOEXCEPT {
|
||||
const auto *view = candidate();
|
||||
return iterator_type{ unchecked(view), view->cend(), view->cend() };
|
||||
return const_iterator_type{unchecked(view), view->cend(), view->cend()};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -944,6 +947,7 @@ private:
|
||||
* @sa View
|
||||
* @sa PersistentView
|
||||
* @sa RawView
|
||||
* @sa RuntimeView
|
||||
*
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
* @tparam Component Type of component iterated by the view.
|
||||
@@ -1287,6 +1291,7 @@ private:
|
||||
* @sa View
|
||||
* @sa View<Entity, Component>
|
||||
* @sa PersistentView
|
||||
* @sa RuntimeView
|
||||
*
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
* @tparam Component Type of component iterated by the view.
|
||||
@@ -1523,12 +1528,12 @@ public:
|
||||
/**
|
||||
* @brief Iterates components and applies the given function object to them.
|
||||
*
|
||||
* The function object is provided with a const reference to each component
|
||||
* of the view.<br/>
|
||||
* The function object is provided with a reference to each component of the
|
||||
* view.<br/>
|
||||
* The signature of the function should be equivalent to the following:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(const Component &);
|
||||
* void(Component &);
|
||||
* @endcode
|
||||
*
|
||||
* @tparam Func Type of the function object to invoke.
|
||||
@@ -1544,6 +1549,345 @@ private:
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Runtime view.
|
||||
*
|
||||
* Runtime views iterate over those entities that have at least all the given
|
||||
* components in their bags. During initialization, a runtime view looks at the
|
||||
* number of entities available for each component and picks up a reference to
|
||||
* the smallest set of candidate entities in order to get a performance boost
|
||||
* when iterate.<br/>
|
||||
* Order of elements during iterations are highly dependent on the order of the
|
||||
* underlying data structures. See SparseSet and its specializations for more
|
||||
* details.
|
||||
*
|
||||
* @b Important
|
||||
*
|
||||
* Iterators aren't invalidated if:
|
||||
*
|
||||
* * New instances of the given components are created and assigned to entities.
|
||||
* * The entity currently pointed is modified (as an example, if one of the
|
||||
* given components is removed from the entity to which the iterator points).
|
||||
*
|
||||
* In all the other cases, modifying the pools of the given components in any
|
||||
* way invalidates all the iterators and using them results in undefined
|
||||
* behavior.
|
||||
*
|
||||
* @note
|
||||
* Views share references to the underlying data structures with the Registry
|
||||
* that generated them. Therefore any change to the entities and to the
|
||||
* components made by means of the registry are immediately reflected by views,
|
||||
* unless a pool wasn't missing when the view was built (in this case, the view
|
||||
* won't have a valid reference and won't be updated accordingly).
|
||||
*
|
||||
* @warning
|
||||
* Lifetime of a view must overcome the one of the registry that generated it.
|
||||
* In any other case, attempting to use a view results in undefined behavior.
|
||||
*
|
||||
* @sa View
|
||||
* @sa View<Entity, Component>
|
||||
* @sa PersistentView
|
||||
* @sa RawView
|
||||
*
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
*/
|
||||
template<typename Entity>
|
||||
class RuntimeView {
|
||||
/*! @brief A registry is allowed to create views. */
|
||||
friend class Registry<Entity>;
|
||||
|
||||
using view_type = SparseSet<Entity>;
|
||||
using underlying_iterator_type = typename view_type::const_iterator_type;
|
||||
using pattern_type = std::vector<const view_type *>;
|
||||
using extent_type = typename view_type::size_type;
|
||||
using traits_type = entt_traits<Entity>;
|
||||
|
||||
class Iterator {
|
||||
friend class RuntimeView<Entity>;
|
||||
|
||||
Iterator(underlying_iterator_type begin, underlying_iterator_type end, const view_type * const *first, const view_type * const *last, extent_type extent) ENTT_NOEXCEPT
|
||||
: begin{begin},
|
||||
end{end},
|
||||
first{first},
|
||||
last{last},
|
||||
extent{extent}
|
||||
{
|
||||
if(begin != end && !valid()) {
|
||||
++(*this);
|
||||
}
|
||||
}
|
||||
|
||||
bool valid() const ENTT_NOEXCEPT {
|
||||
const auto entity = *begin;
|
||||
const auto sz = size_type(entity & traits_type::entity_mask);
|
||||
|
||||
return sz < extent && std::all_of(first, last, [entity](const auto *view) {
|
||||
return view->fast(entity);
|
||||
});
|
||||
}
|
||||
|
||||
public:
|
||||
using difference_type = typename underlying_iterator_type::difference_type;
|
||||
using value_type = typename underlying_iterator_type::value_type;
|
||||
using pointer = typename underlying_iterator_type::pointer;
|
||||
using reference = typename underlying_iterator_type::reference;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
|
||||
Iterator() ENTT_NOEXCEPT = default;
|
||||
|
||||
Iterator(const Iterator &) ENTT_NOEXCEPT = default;
|
||||
Iterator & operator=(const Iterator &) ENTT_NOEXCEPT = default;
|
||||
|
||||
Iterator & operator++() ENTT_NOEXCEPT {
|
||||
return (++begin != end && !valid()) ? ++(*this) : *this;
|
||||
}
|
||||
|
||||
Iterator operator++(int) ENTT_NOEXCEPT {
|
||||
Iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
bool operator==(const Iterator &other) const ENTT_NOEXCEPT {
|
||||
return other.begin == begin;
|
||||
}
|
||||
|
||||
inline bool operator!=(const Iterator &other) const ENTT_NOEXCEPT {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
pointer operator->() const ENTT_NOEXCEPT {
|
||||
return begin.operator->();
|
||||
}
|
||||
|
||||
inline reference operator*() const ENTT_NOEXCEPT {
|
||||
return *operator->();
|
||||
}
|
||||
|
||||
private:
|
||||
underlying_iterator_type begin;
|
||||
underlying_iterator_type end;
|
||||
const view_type * const *first;
|
||||
const view_type * const *last;
|
||||
extent_type extent;
|
||||
};
|
||||
|
||||
RuntimeView(pattern_type others) ENTT_NOEXCEPT
|
||||
: pools{std::move(others)}
|
||||
{
|
||||
const auto it = std::min_element(pools.begin(), pools.end(), [](const auto *lhs, const auto *rhs) {
|
||||
return !lhs || (rhs && (lhs->size() < rhs->size()));
|
||||
});
|
||||
|
||||
// brings the best candidate (if any) on front of the vector
|
||||
std::rotate(pools.begin(), it, pools.end());
|
||||
}
|
||||
|
||||
extent_type min() const ENTT_NOEXCEPT {
|
||||
extent_type extent{};
|
||||
|
||||
if(valid()) {
|
||||
const auto it = std::min_element(pools.cbegin(), pools.cend(), [](const auto *lhs, const auto *rhs) {
|
||||
return lhs->extent() < rhs->extent();
|
||||
});
|
||||
|
||||
extent = (*it)->extent();
|
||||
}
|
||||
|
||||
return extent;
|
||||
}
|
||||
|
||||
inline bool valid() const ENTT_NOEXCEPT {
|
||||
return !pools.empty() && pools.front();
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = typename view_type::entity_type;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = typename view_type::size_type;
|
||||
/*! @brief Input iterator type. */
|
||||
using iterator_type = Iterator;
|
||||
/*! @brief Constant input iterator type. */
|
||||
using const_iterator_type = Iterator;
|
||||
|
||||
/**
|
||||
* @brief Estimates the number of entities that have the given components.
|
||||
* @return Estimated number of entities that have the given components.
|
||||
*/
|
||||
size_type size() const ENTT_NOEXCEPT {
|
||||
return valid() ? pools.front()->size() : size_type{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if the view is definitely empty.
|
||||
* @return True if the view is definitely empty, false otherwise.
|
||||
*/
|
||||
bool empty() const ENTT_NOEXCEPT {
|
||||
return !valid() || pools.front()->empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the first entity that has the given
|
||||
* components.
|
||||
*
|
||||
* The returned iterator points to the first entity that has the given
|
||||
* components. If the view is empty, the returned iterator will be equal to
|
||||
* `end()`.
|
||||
*
|
||||
* @note
|
||||
* Input iterators stay true to the order imposed to the underlying data
|
||||
* structures.
|
||||
*
|
||||
* @return An iterator to the first entity that has the given components.
|
||||
*/
|
||||
const_iterator_type cbegin() const ENTT_NOEXCEPT {
|
||||
const_iterator_type it{};
|
||||
|
||||
if(valid()) {
|
||||
const auto &pool = *pools.front();
|
||||
const auto * const *data = pools.data();
|
||||
it = { pool.cbegin(), pool.cend(), data + 1, data + pools.size(), min() };
|
||||
}
|
||||
|
||||
return it;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the first entity that has the given
|
||||
* components.
|
||||
*
|
||||
* The returned iterator points to the first entity that has the given
|
||||
* components. If the view is empty, the returned iterator will be equal to
|
||||
* `end()`.
|
||||
*
|
||||
* @note
|
||||
* Input iterators stay true to the order imposed to the underlying data
|
||||
* structures.
|
||||
*
|
||||
* @return An iterator to the first entity that has the given components.
|
||||
*/
|
||||
inline const_iterator_type begin() const ENTT_NOEXCEPT {
|
||||
return cbegin();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the first entity that has the given
|
||||
* components.
|
||||
*
|
||||
* The returned iterator points to the first entity that has the given
|
||||
* components. If the view is empty, the returned iterator will be equal to
|
||||
* `end()`.
|
||||
*
|
||||
* @note
|
||||
* Input iterators stay true to the order imposed to the underlying data
|
||||
* structures.
|
||||
*
|
||||
* @return An iterator to the first entity that has the given components.
|
||||
*/
|
||||
inline iterator_type begin() ENTT_NOEXCEPT {
|
||||
return cbegin();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator that is past the last entity that has the
|
||||
* given components.
|
||||
*
|
||||
* The returned iterator points to the entity following the last entity that
|
||||
* has the given components. Attempting to dereference the returned iterator
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @note
|
||||
* Input iterators stay true to the order imposed to the underlying data
|
||||
* structures.
|
||||
*
|
||||
* @return An iterator to the entity following the last entity that has the
|
||||
* given components.
|
||||
*/
|
||||
const_iterator_type cend() const ENTT_NOEXCEPT {
|
||||
const_iterator_type it{};
|
||||
|
||||
if(valid()) {
|
||||
const auto &pool = *pools.front();
|
||||
it = { pool.cend(), pool.cend(), nullptr, nullptr, min() };
|
||||
}
|
||||
|
||||
return it;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator that is past the last entity that has the
|
||||
* given components.
|
||||
*
|
||||
* The returned iterator points to the entity following the last entity that
|
||||
* has the given components. Attempting to dereference the returned iterator
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @note
|
||||
* Input iterators stay true to the order imposed to the underlying data
|
||||
* structures.
|
||||
*
|
||||
* @return An iterator to the entity following the last entity that has the
|
||||
* given components.
|
||||
*/
|
||||
inline const_iterator_type end() const ENTT_NOEXCEPT {
|
||||
return cend();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator that is past the last entity that has the
|
||||
* given components.
|
||||
*
|
||||
* The returned iterator points to the entity following the last entity that
|
||||
* has the given components. Attempting to dereference the returned iterator
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @note
|
||||
* Input iterators stay true to the order imposed to the underlying data
|
||||
* structures.
|
||||
*
|
||||
* @return An iterator to the entity following the last entity that has the
|
||||
* given components.
|
||||
*/
|
||||
inline iterator_type end() ENTT_NOEXCEPT {
|
||||
return cend();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a view contains an entity.
|
||||
* @param entity A valid entity identifier.
|
||||
* @return True if the view contains the given entity, false otherwise.
|
||||
*/
|
||||
bool contains(const entity_type entity) const ENTT_NOEXCEPT {
|
||||
return valid() && std::all_of(pools.cbegin(), pools.cend(), [entity](const auto *view) {
|
||||
return view->has(entity) && view->data()[view->get(entity)] == entity;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Iterates entities and applies the given function object to them.
|
||||
*
|
||||
* The function object is invoked for each entity. It is provided only with
|
||||
* the entity itself. To get the components, users can use the registry with
|
||||
* which the view was built.<br/>
|
||||
* The signature of the function should be equivalent to the following:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(const entity_type);
|
||||
* @endcode
|
||||
*
|
||||
* @tparam Func Type of the function object to invoke.
|
||||
* @param func A valid function object.
|
||||
*/
|
||||
template<typename Func>
|
||||
void each(Func func) const {
|
||||
std::for_each(cbegin(), cend(), func);
|
||||
}
|
||||
|
||||
private:
|
||||
pattern_type pools;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <chrono>
|
||||
#include <iterator>
|
||||
#include <gtest/gtest.h>
|
||||
#include <entt/entity/registry.hpp>
|
||||
|
||||
@@ -135,6 +136,31 @@ TEST(Benchmark, IterateSingleComponentRaw1M) {
|
||||
});
|
||||
}
|
||||
|
||||
TEST(Benchmark, IterateSingleComponentRuntime1M) {
|
||||
entt::DefaultRegistry registry;
|
||||
|
||||
std::cout << "Iterating over 1000000 entities, one component, runtime view" << std::endl;
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<Position>(entity);
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
using component_type = typename entt::DefaultRegistry::component_type;
|
||||
component_type types[] = { registry.type<Position>() };
|
||||
|
||||
Timer timer;
|
||||
registry.view(std::begin(types), std::end(types)).each(func);
|
||||
timer.elapsed();
|
||||
};
|
||||
|
||||
test([](auto) {});
|
||||
test([®istry](auto entity) {
|
||||
registry.get<Position>(entity).x = {};
|
||||
});
|
||||
}
|
||||
|
||||
TEST(Benchmark, IterateTwoComponents1M) {
|
||||
entt::DefaultRegistry registry;
|
||||
|
||||
@@ -242,6 +268,94 @@ TEST(Benchmark, IterateTwoComponentsPersistent1M) {
|
||||
});
|
||||
}
|
||||
|
||||
TEST(Benchmark, IterateTwoComponentsRuntime1M) {
|
||||
entt::DefaultRegistry registry;
|
||||
registry.prepare<Position, Velocity>();
|
||||
|
||||
std::cout << "Iterating over 1000000 entities, two components, runtime view" << std::endl;
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<Position>(entity);
|
||||
registry.assign<Velocity>(entity);
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
using component_type = typename entt::DefaultRegistry::component_type;
|
||||
component_type types[] = { registry.type<Position>(), registry.type<Velocity>() };
|
||||
|
||||
Timer timer;
|
||||
registry.view(std::begin(types), std::end(types)).each(func);
|
||||
timer.elapsed();
|
||||
};
|
||||
|
||||
test([](auto) {});
|
||||
test([®istry](auto entity) {
|
||||
registry.get<Position>(entity).x = {};
|
||||
registry.get<Velocity>(entity).x = {};
|
||||
});
|
||||
}
|
||||
|
||||
TEST(Benchmark, IterateTwoComponentsRuntime1MHalf) {
|
||||
entt::DefaultRegistry registry;
|
||||
|
||||
std::cout << "Iterating over 1000000 entities, two components, half of the entities have all the components, runtime view" << std::endl;
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<Velocity>(entity);
|
||||
|
||||
if(i % 2) {
|
||||
registry.assign<Position>(entity);
|
||||
}
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
using component_type = typename entt::DefaultRegistry::component_type;
|
||||
component_type types[] = { registry.type<Position>(), registry.type<Velocity>() };
|
||||
|
||||
Timer timer;
|
||||
registry.view(std::begin(types), std::end(types)).each(func);
|
||||
timer.elapsed();
|
||||
};
|
||||
|
||||
test([](auto) {});
|
||||
test([®istry](auto entity) {
|
||||
registry.get<Position>(entity).x = {};
|
||||
registry.get<Velocity>(entity).x = {};
|
||||
});
|
||||
}
|
||||
|
||||
TEST(Benchmark, IterateTwoComponentsRuntime1MOne) {
|
||||
entt::DefaultRegistry registry;
|
||||
|
||||
std::cout << "Iterating over 1000000 entities, two components, only one entity has all the components, runtime view" << std::endl;
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<Velocity>(entity);
|
||||
|
||||
if(i == 5000000L) {
|
||||
registry.assign<Position>(entity);
|
||||
}
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
using component_type = typename entt::DefaultRegistry::component_type;
|
||||
component_type types[] = { registry.type<Position>(), registry.type<Velocity>() };
|
||||
|
||||
Timer timer;
|
||||
registry.view(std::begin(types), std::end(types)).each(func);
|
||||
timer.elapsed();
|
||||
};
|
||||
|
||||
test([](auto) {});
|
||||
test([®istry](auto entity) {
|
||||
registry.get<Position>(entity).x = {};
|
||||
registry.get<Velocity>(entity).x = {};
|
||||
});
|
||||
}
|
||||
|
||||
TEST(Benchmark, IterateFiveComponents1M) {
|
||||
entt::DefaultRegistry registry;
|
||||
|
||||
@@ -361,6 +475,130 @@ TEST(Benchmark, IterateFiveComponentsPersistent1M) {
|
||||
});
|
||||
}
|
||||
|
||||
TEST(Benchmark, IterateFiveComponentsRuntime1M) {
|
||||
entt::DefaultRegistry registry;
|
||||
registry.prepare<Position, Velocity, Comp<1>, Comp<2>, Comp<3>>();
|
||||
|
||||
std::cout << "Iterating over 1000000 entities, five components, runtime view" << std::endl;
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<Position>(entity);
|
||||
registry.assign<Velocity>(entity);
|
||||
registry.assign<Comp<1>>(entity);
|
||||
registry.assign<Comp<2>>(entity);
|
||||
registry.assign<Comp<3>>(entity);
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
using component_type = typename entt::DefaultRegistry::component_type;
|
||||
component_type types[] = {
|
||||
registry.type<Position>(),
|
||||
registry.type<Velocity>(),
|
||||
registry.type<Comp<1>>(),
|
||||
registry.type<Comp<2>>(),
|
||||
registry.type<Comp<3>>()
|
||||
};
|
||||
|
||||
Timer timer;
|
||||
registry.view(std::begin(types), std::end(types)).each(func);
|
||||
timer.elapsed();
|
||||
};
|
||||
|
||||
test([](auto) {});
|
||||
test([®istry](auto entity) {
|
||||
registry.get<Position>(entity).x = {};
|
||||
registry.get<Velocity>(entity).x = {};
|
||||
registry.get<Comp<1>>(entity).x = {};
|
||||
registry.get<Comp<2>>(entity).x = {};
|
||||
registry.get<Comp<3>>(entity).x = {};
|
||||
});
|
||||
}
|
||||
|
||||
TEST(Benchmark, IterateFiveComponentsRuntime1MHalf) {
|
||||
entt::DefaultRegistry registry;
|
||||
|
||||
std::cout << "Iterating over 1000000 entities, five components, half of the entities have all the components, runtime view" << std::endl;
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<Velocity>(entity);
|
||||
registry.assign<Comp<1>>(entity);
|
||||
registry.assign<Comp<2>>(entity);
|
||||
registry.assign<Comp<3>>(entity);
|
||||
|
||||
if(i % 2) {
|
||||
registry.assign<Position>(entity);
|
||||
}
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
using component_type = typename entt::DefaultRegistry::component_type;
|
||||
component_type types[] = {
|
||||
registry.type<Position>(),
|
||||
registry.type<Velocity>(),
|
||||
registry.type<Comp<1>>(),
|
||||
registry.type<Comp<2>>(),
|
||||
registry.type<Comp<3>>()
|
||||
};
|
||||
|
||||
Timer timer;
|
||||
registry.view(std::begin(types), std::end(types)).each(func);
|
||||
timer.elapsed();
|
||||
};
|
||||
|
||||
test([](auto) {});
|
||||
test([®istry](auto entity) {
|
||||
registry.get<Position>(entity).x = {};
|
||||
registry.get<Velocity>(entity).x = {};
|
||||
registry.get<Comp<1>>(entity).x = {};
|
||||
registry.get<Comp<2>>(entity).x = {};
|
||||
registry.get<Comp<3>>(entity).x = {};
|
||||
});
|
||||
}
|
||||
|
||||
TEST(Benchmark, IterateFiveComponentsRuntime1MOne) {
|
||||
entt::DefaultRegistry registry;
|
||||
|
||||
std::cout << "Iterating over 1000000 entities, five components, only one entity has all the components, runtime view" << std::endl;
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<Velocity>(entity);
|
||||
registry.assign<Comp<1>>(entity);
|
||||
registry.assign<Comp<2>>(entity);
|
||||
registry.assign<Comp<3>>(entity);
|
||||
|
||||
if(i == 5000000L) {
|
||||
registry.assign<Position>(entity);
|
||||
}
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
using component_type = typename entt::DefaultRegistry::component_type;
|
||||
component_type types[] = {
|
||||
registry.type<Position>(),
|
||||
registry.type<Velocity>(),
|
||||
registry.type<Comp<1>>(),
|
||||
registry.type<Comp<2>>(),
|
||||
registry.type<Comp<3>>()
|
||||
};
|
||||
|
||||
Timer timer;
|
||||
registry.view(std::begin(types), std::end(types)).each(func);
|
||||
timer.elapsed();
|
||||
};
|
||||
|
||||
test([](auto) {});
|
||||
test([®istry](auto entity) {
|
||||
registry.get<Position>(entity).x = {};
|
||||
registry.get<Velocity>(entity).x = {};
|
||||
registry.get<Comp<1>>(entity).x = {};
|
||||
registry.get<Comp<2>>(entity).x = {};
|
||||
registry.get<Comp<3>>(entity).x = {};
|
||||
});
|
||||
}
|
||||
|
||||
TEST(Benchmark, IterateTenComponents1M) {
|
||||
entt::DefaultRegistry registry;
|
||||
|
||||
@@ -500,6 +738,175 @@ TEST(Benchmark, IterateTenComponentsPersistent1M) {
|
||||
});
|
||||
}
|
||||
|
||||
TEST(Benchmark, IterateTenComponentsRuntime1M) {
|
||||
entt::DefaultRegistry registry;
|
||||
registry.prepare<Position, Velocity, Comp<1>, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>();
|
||||
|
||||
std::cout << "Iterating over 1000000 entities, ten components, runtime view" << std::endl;
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<Position>(entity);
|
||||
registry.assign<Velocity>(entity);
|
||||
registry.assign<Comp<1>>(entity);
|
||||
registry.assign<Comp<2>>(entity);
|
||||
registry.assign<Comp<3>>(entity);
|
||||
registry.assign<Comp<4>>(entity);
|
||||
registry.assign<Comp<5>>(entity);
|
||||
registry.assign<Comp<6>>(entity);
|
||||
registry.assign<Comp<7>>(entity);
|
||||
registry.assign<Comp<8>>(entity);
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
using component_type = typename entt::DefaultRegistry::component_type;
|
||||
component_type types[] = {
|
||||
registry.type<Position>(),
|
||||
registry.type<Velocity>(),
|
||||
registry.type<Comp<1>>(),
|
||||
registry.type<Comp<2>>(),
|
||||
registry.type<Comp<3>>(),
|
||||
registry.type<Comp<4>>(),
|
||||
registry.type<Comp<5>>(),
|
||||
registry.type<Comp<6>>(),
|
||||
registry.type<Comp<7>>(),
|
||||
registry.type<Comp<8>>()
|
||||
};
|
||||
|
||||
Timer timer;
|
||||
registry.view(std::begin(types), std::end(types)).each(func);
|
||||
timer.elapsed();
|
||||
};
|
||||
|
||||
test([](auto) {});
|
||||
test([®istry](auto entity) {
|
||||
registry.get<Position>(entity).x = {};
|
||||
registry.get<Velocity>(entity).x = {};
|
||||
registry.get<Comp<1>>(entity).x = {};
|
||||
registry.get<Comp<2>>(entity).x = {};
|
||||
registry.get<Comp<3>>(entity).x = {};
|
||||
registry.get<Comp<4>>(entity).x = {};
|
||||
registry.get<Comp<5>>(entity).x = {};
|
||||
registry.get<Comp<6>>(entity).x = {};
|
||||
registry.get<Comp<7>>(entity).x = {};
|
||||
registry.get<Comp<8>>(entity).x = {};
|
||||
});
|
||||
}
|
||||
|
||||
TEST(Benchmark, IterateTenComponentsRuntime1MHalf) {
|
||||
entt::DefaultRegistry registry;
|
||||
|
||||
std::cout << "Iterating over 1000000 entities, ten components, half of the entities have all the components, runtime view" << std::endl;
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<Velocity>(entity);
|
||||
registry.assign<Comp<1>>(entity);
|
||||
registry.assign<Comp<2>>(entity);
|
||||
registry.assign<Comp<3>>(entity);
|
||||
registry.assign<Comp<4>>(entity);
|
||||
registry.assign<Comp<5>>(entity);
|
||||
registry.assign<Comp<6>>(entity);
|
||||
registry.assign<Comp<7>>(entity);
|
||||
registry.assign<Comp<8>>(entity);
|
||||
|
||||
if(i % 2) {
|
||||
registry.assign<Position>(entity);
|
||||
}
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
using component_type = typename entt::DefaultRegistry::component_type;
|
||||
component_type types[] = {
|
||||
registry.type<Position>(),
|
||||
registry.type<Velocity>(),
|
||||
registry.type<Comp<1>>(),
|
||||
registry.type<Comp<2>>(),
|
||||
registry.type<Comp<3>>(),
|
||||
registry.type<Comp<4>>(),
|
||||
registry.type<Comp<5>>(),
|
||||
registry.type<Comp<6>>(),
|
||||
registry.type<Comp<7>>(),
|
||||
registry.type<Comp<8>>()
|
||||
};
|
||||
|
||||
Timer timer;
|
||||
registry.view(std::begin(types), std::end(types)).each(func);
|
||||
timer.elapsed();
|
||||
};
|
||||
|
||||
test([](auto) {});
|
||||
test([®istry](auto entity) {
|
||||
registry.get<Position>(entity).x = {};
|
||||
registry.get<Velocity>(entity).x = {};
|
||||
registry.get<Comp<1>>(entity).x = {};
|
||||
registry.get<Comp<2>>(entity).x = {};
|
||||
registry.get<Comp<3>>(entity).x = {};
|
||||
registry.get<Comp<4>>(entity).x = {};
|
||||
registry.get<Comp<5>>(entity).x = {};
|
||||
registry.get<Comp<6>>(entity).x = {};
|
||||
registry.get<Comp<7>>(entity).x = {};
|
||||
registry.get<Comp<8>>(entity).x = {};
|
||||
});
|
||||
}
|
||||
|
||||
TEST(Benchmark, IterateTenComponentsRuntime1MOne) {
|
||||
entt::DefaultRegistry registry;
|
||||
|
||||
std::cout << "Iterating over 1000000 entities, ten components, only one entity has all the components, runtime view" << std::endl;
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<Velocity>(entity);
|
||||
registry.assign<Comp<1>>(entity);
|
||||
registry.assign<Comp<2>>(entity);
|
||||
registry.assign<Comp<3>>(entity);
|
||||
registry.assign<Comp<4>>(entity);
|
||||
registry.assign<Comp<5>>(entity);
|
||||
registry.assign<Comp<6>>(entity);
|
||||
registry.assign<Comp<7>>(entity);
|
||||
registry.assign<Comp<8>>(entity);
|
||||
|
||||
if(i == 5000000L) {
|
||||
registry.assign<Position>(entity);
|
||||
}
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
using component_type = typename entt::DefaultRegistry::component_type;
|
||||
component_type types[] = {
|
||||
registry.type<Position>(),
|
||||
registry.type<Velocity>(),
|
||||
registry.type<Comp<1>>(),
|
||||
registry.type<Comp<2>>(),
|
||||
registry.type<Comp<3>>(),
|
||||
registry.type<Comp<4>>(),
|
||||
registry.type<Comp<5>>(),
|
||||
registry.type<Comp<6>>(),
|
||||
registry.type<Comp<7>>(),
|
||||
registry.type<Comp<8>>()
|
||||
};
|
||||
|
||||
Timer timer;
|
||||
registry.view(std::begin(types), std::end(types)).each(func);
|
||||
timer.elapsed();
|
||||
};
|
||||
|
||||
test([](auto) {});
|
||||
test([®istry](auto entity) {
|
||||
registry.get<Position>(entity).x = {};
|
||||
registry.get<Velocity>(entity).x = {};
|
||||
registry.get<Comp<1>>(entity).x = {};
|
||||
registry.get<Comp<2>>(entity).x = {};
|
||||
registry.get<Comp<3>>(entity).x = {};
|
||||
registry.get<Comp<4>>(entity).x = {};
|
||||
registry.get<Comp<5>>(entity).x = {};
|
||||
registry.get<Comp<6>>(entity).x = {};
|
||||
registry.get<Comp<7>>(entity).x = {};
|
||||
registry.get<Comp<8>>(entity).x = {};
|
||||
});
|
||||
}
|
||||
|
||||
TEST(Benchmark, SortSingle) {
|
||||
entt::DefaultRegistry registry;
|
||||
|
||||
|
||||
@@ -1,307 +1,9 @@
|
||||
#include <utility>
|
||||
#include <iterator>
|
||||
#include <gtest/gtest.h>
|
||||
#include <entt/entity/registry.hpp>
|
||||
#include <entt/entity/view.hpp>
|
||||
|
||||
TEST(SingleComponentView, Functionalities) {
|
||||
entt::Registry<std::uint64_t> registry;
|
||||
auto view = registry.view<char>();
|
||||
const auto &cview = view;
|
||||
|
||||
const auto e0 = registry.create();
|
||||
const auto e1 = registry.create();
|
||||
|
||||
ASSERT_TRUE(view.empty());
|
||||
|
||||
registry.assign<int>(e1);
|
||||
registry.assign<char>(e1);
|
||||
|
||||
ASSERT_NO_THROW(registry.view<char>().begin()++);
|
||||
ASSERT_NO_THROW(++registry.view<char>().begin());
|
||||
|
||||
ASSERT_NE(view.begin(), view.end());
|
||||
ASSERT_NE(cview.begin(), cview.end());
|
||||
ASSERT_EQ(view.size(), typename decltype(view)::size_type{1});
|
||||
ASSERT_FALSE(view.empty());
|
||||
|
||||
registry.assign<char>(e0);
|
||||
|
||||
ASSERT_EQ(view.size(), typename decltype(view)::size_type{2});
|
||||
|
||||
view.get(e0) = '1';
|
||||
view.get(e1) = '2';
|
||||
|
||||
for(auto entity: view) {
|
||||
const auto &cview = static_cast<const decltype(view) &>(view);
|
||||
ASSERT_TRUE(cview.get(entity) == '1' || cview.get(entity) == '2');
|
||||
}
|
||||
|
||||
ASSERT_EQ(*(view.data() + 0), e1);
|
||||
ASSERT_EQ(*(view.data() + 1), e0);
|
||||
|
||||
ASSERT_EQ(*(view.raw() + 0), '2');
|
||||
ASSERT_EQ(*(static_cast<const decltype(view) &>(view).raw() + 1), '1');
|
||||
|
||||
registry.remove<char>(e0);
|
||||
registry.remove<char>(e1);
|
||||
|
||||
ASSERT_EQ(view.begin(), view.end());
|
||||
ASSERT_EQ(view.cbegin(), view.cend());
|
||||
ASSERT_TRUE(view.empty());
|
||||
}
|
||||
|
||||
TEST(SingleComponentView, ElementAccess) {
|
||||
entt::Registry<std::uint64_t> registry;
|
||||
auto view = registry.view<int>();
|
||||
const auto &cview = view;
|
||||
|
||||
const auto e0 = registry.create();
|
||||
registry.assign<int>(e0);
|
||||
|
||||
const auto e1 = registry.create();
|
||||
registry.assign<int>(e1);
|
||||
|
||||
for(typename decltype(view)::size_type i{}; i < view.size(); ++i) {
|
||||
//ASSERT_EQ(view[i], i ? e0 : e1);
|
||||
ASSERT_EQ(cview[i], i ? e0 : e1);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(SingleComponentView, Contains) {
|
||||
entt::Registry<std::uint64_t> registry;
|
||||
|
||||
const auto e0 = registry.create();
|
||||
registry.assign<int>(e0);
|
||||
|
||||
const auto e1 = registry.create();
|
||||
registry.assign<int>(e1);
|
||||
|
||||
registry.destroy(e0);
|
||||
|
||||
auto view = registry.view<int>();
|
||||
|
||||
ASSERT_FALSE(view.contains(e0));
|
||||
ASSERT_TRUE(view.contains(e1));
|
||||
}
|
||||
|
||||
TEST(SingleComponentView, Empty) {
|
||||
entt::Registry<std::uint64_t> registry;
|
||||
|
||||
const auto e0 = registry.create();
|
||||
registry.assign<char>(e0);
|
||||
registry.assign<double>(e0);
|
||||
|
||||
const auto e1 = registry.create();
|
||||
registry.assign<char>(e1);
|
||||
|
||||
auto view = registry.view<int>();
|
||||
|
||||
ASSERT_EQ(view.size(), entt::Registry<std::uint64_t>::size_type{0});
|
||||
|
||||
for(auto entity: view) {
|
||||
(void)entity;
|
||||
FAIL();
|
||||
}
|
||||
}
|
||||
|
||||
TEST(SingleComponentView, Each) {
|
||||
entt::Registry<std::uint64_t> registry;
|
||||
|
||||
registry.assign<int>(registry.create());
|
||||
registry.assign<int>(registry.create());
|
||||
|
||||
auto view = registry.view<int>();
|
||||
const auto &cview = static_cast<const decltype(view) &>(view);
|
||||
std::size_t cnt = 0;
|
||||
|
||||
view.each([&cnt](auto, int &) { ++cnt; });
|
||||
|
||||
ASSERT_EQ(cnt, std::size_t{2});
|
||||
|
||||
cview.each([&cnt](auto, const int &) { --cnt; });
|
||||
|
||||
ASSERT_EQ(cnt, std::size_t{0});
|
||||
}
|
||||
|
||||
TEST(MultipleComponentView, Functionalities) {
|
||||
entt::Registry<std::uint64_t> registry;
|
||||
auto view = registry.view<int, char>();
|
||||
const auto &cview = view;
|
||||
|
||||
ASSERT_TRUE(view.empty());
|
||||
|
||||
const auto e0 = registry.create();
|
||||
registry.assign<char>(e0);
|
||||
|
||||
const auto e1 = registry.create();
|
||||
registry.assign<int>(e1);
|
||||
|
||||
ASSERT_FALSE(view.empty());
|
||||
|
||||
registry.assign<char>(e1);
|
||||
|
||||
auto it = registry.view<char>().begin();
|
||||
|
||||
ASSERT_EQ(*it, e1);
|
||||
ASSERT_EQ(*(it+1), e0);
|
||||
ASSERT_EQ(it += 2, registry.view<char>().end());
|
||||
|
||||
ASSERT_NO_THROW((registry.view<int, char>().begin()++));
|
||||
ASSERT_NO_THROW((++registry.view<int, char>().begin()));
|
||||
|
||||
ASSERT_NE(view.begin(), view.end());
|
||||
ASSERT_NE(cview.begin(), cview.end());
|
||||
ASSERT_EQ(view.size(), decltype(view.size()){1});
|
||||
|
||||
registry.get<char>(e0) = '1';
|
||||
registry.get<char>(e1) = '2';
|
||||
registry.get<int>(e1) = 42;
|
||||
|
||||
for(auto entity: view) {
|
||||
const auto &cview = static_cast<const decltype(view) &>(view);
|
||||
ASSERT_EQ(std::get<0>(cview.get<int, char>(entity)), 42);
|
||||
ASSERT_EQ(std::get<1>(view.get<int, char>(entity)), '2');
|
||||
ASSERT_EQ(cview.get<char>(entity), '2');
|
||||
}
|
||||
|
||||
registry.remove<char>(e0);
|
||||
registry.remove<char>(e1);
|
||||
}
|
||||
|
||||
TEST(MultipleComponentView, Iterator) {
|
||||
using iterator_type = typename decltype(std::declval<entt::Registry<std::uint64_t>>().view<int, char>())::iterator_type;
|
||||
|
||||
entt::Registry<std::uint64_t> registry;
|
||||
const auto entity = registry.create();
|
||||
registry.assign<int>(entity);
|
||||
registry.assign<char>(entity);
|
||||
|
||||
const auto view = registry.view<int, char>();
|
||||
|
||||
iterator_type end{view.begin()};
|
||||
iterator_type begin{};
|
||||
begin = view.end();
|
||||
std::swap(begin, end);
|
||||
|
||||
ASSERT_EQ(begin, view.begin());
|
||||
ASSERT_EQ(end, view.end());
|
||||
ASSERT_NE(begin, end);
|
||||
|
||||
ASSERT_EQ(view.begin()++, view.begin());
|
||||
ASSERT_EQ(++view.begin(), view.end());
|
||||
}
|
||||
|
||||
TEST(MultipleComponentView, ConstIterator) {
|
||||
using iterator_type = typename decltype(std::declval<entt::Registry<std::uint64_t>>().view<int, char>())::const_iterator_type;
|
||||
|
||||
entt::Registry<std::uint64_t> registry;
|
||||
const auto entity = registry.create();
|
||||
registry.assign<int>(entity);
|
||||
registry.assign<char>(entity);
|
||||
|
||||
const auto view = registry.view<int, char>();
|
||||
|
||||
iterator_type cend{view.cbegin()};
|
||||
iterator_type cbegin{};
|
||||
cbegin = view.cend();
|
||||
std::swap(cbegin, cend);
|
||||
|
||||
ASSERT_EQ(cbegin, view.cbegin());
|
||||
ASSERT_EQ(cend, view.cend());
|
||||
ASSERT_NE(cbegin, cend);
|
||||
|
||||
ASSERT_EQ(view.cbegin()++, view.cbegin());
|
||||
ASSERT_EQ(++view.cbegin(), view.cend());
|
||||
}
|
||||
|
||||
TEST(MultipleComponentView, Contains) {
|
||||
entt::Registry<std::uint64_t> registry;
|
||||
|
||||
const auto e0 = registry.create();
|
||||
registry.assign<int>(e0);
|
||||
registry.assign<char>(e0);
|
||||
|
||||
const auto e1 = registry.create();
|
||||
registry.assign<int>(e1);
|
||||
registry.assign<char>(e1);
|
||||
|
||||
registry.destroy(e0);
|
||||
|
||||
auto view = registry.view<int, char>();
|
||||
|
||||
ASSERT_FALSE(view.contains(e0));
|
||||
ASSERT_TRUE(view.contains(e1));
|
||||
}
|
||||
|
||||
TEST(MultipleComponentView, Empty) {
|
||||
entt::Registry<std::uint64_t> registry;
|
||||
|
||||
const auto e0 = registry.create();
|
||||
registry.assign<double>(e0);
|
||||
registry.assign<int>(e0);
|
||||
registry.assign<float>(e0);
|
||||
|
||||
const auto e1 = registry.create();
|
||||
registry.assign<char>(e1);
|
||||
registry.assign<float>(e1);
|
||||
|
||||
auto view = registry.view<char, int, float>();
|
||||
|
||||
for(auto entity: view) {
|
||||
(void)entity;
|
||||
FAIL();
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MultipleComponentView, Each) {
|
||||
entt::Registry<std::uint64_t> registry;
|
||||
|
||||
const auto e0 = registry.create();
|
||||
registry.assign<int>(e0);
|
||||
registry.assign<char>(e0);
|
||||
|
||||
const auto e1 = registry.create();
|
||||
registry.assign<int>(e1);
|
||||
registry.assign<char>(e1);
|
||||
|
||||
auto view = registry.view<int, char>();
|
||||
const auto &cview = static_cast<const decltype(view) &>(view);
|
||||
std::size_t cnt = 0;
|
||||
|
||||
view.each([&cnt](auto, int &, char &) { ++cnt; });
|
||||
|
||||
ASSERT_EQ(cnt, std::size_t{2});
|
||||
|
||||
cview.each([&cnt](auto, const int &, const char &) { --cnt; });
|
||||
|
||||
ASSERT_EQ(cnt, std::size_t{0});
|
||||
}
|
||||
|
||||
TEST(MultipleComponentView, EachWithHoles) {
|
||||
entt::Registry<std::uint64_t> registry;
|
||||
|
||||
const auto e0 = registry.create();
|
||||
const auto e1 = registry.create();
|
||||
const auto e2 = registry.create();
|
||||
|
||||
registry.assign<char>(e0, '0');
|
||||
registry.assign<char>(e1, '1');
|
||||
|
||||
registry.assign<int>(e0, 0);
|
||||
registry.assign<int>(e2, 2);
|
||||
|
||||
auto view = registry.view<char, int>();
|
||||
|
||||
view.each([e0](auto entity, const char &c, const int &i) {
|
||||
if(e0 == entity) {
|
||||
ASSERT_EQ(c, '0');
|
||||
ASSERT_EQ(i, 0);
|
||||
} else {
|
||||
FAIL();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
TEST(PersistentView, Prepare) {
|
||||
entt::Registry<std::uint64_t> registry;
|
||||
registry.prepare<int, char>();
|
||||
@@ -524,6 +226,299 @@ TEST(PersistentView, Sort) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(SingleComponentView, Functionalities) {
|
||||
entt::Registry<std::uint64_t> registry;
|
||||
auto view = registry.view<char>();
|
||||
const auto &cview = view;
|
||||
|
||||
const auto e0 = registry.create();
|
||||
const auto e1 = registry.create();
|
||||
|
||||
ASSERT_TRUE(view.empty());
|
||||
|
||||
registry.assign<int>(e1);
|
||||
registry.assign<char>(e1);
|
||||
|
||||
ASSERT_NO_THROW(registry.view<char>().begin()++);
|
||||
ASSERT_NO_THROW(++registry.view<char>().begin());
|
||||
|
||||
ASSERT_NE(view.begin(), view.end());
|
||||
ASSERT_NE(cview.begin(), cview.end());
|
||||
ASSERT_EQ(view.size(), typename decltype(view)::size_type{1});
|
||||
ASSERT_FALSE(view.empty());
|
||||
|
||||
registry.assign<char>(e0);
|
||||
|
||||
ASSERT_EQ(view.size(), typename decltype(view)::size_type{2});
|
||||
|
||||
view.get(e0) = '1';
|
||||
view.get(e1) = '2';
|
||||
|
||||
for(auto entity: view) {
|
||||
const auto &cview = static_cast<const decltype(view) &>(view);
|
||||
ASSERT_TRUE(cview.get(entity) == '1' || cview.get(entity) == '2');
|
||||
}
|
||||
|
||||
ASSERT_EQ(*(view.data() + 0), e1);
|
||||
ASSERT_EQ(*(view.data() + 1), e0);
|
||||
|
||||
ASSERT_EQ(*(view.raw() + 0), '2');
|
||||
ASSERT_EQ(*(static_cast<const decltype(view) &>(view).raw() + 1), '1');
|
||||
|
||||
registry.remove<char>(e0);
|
||||
registry.remove<char>(e1);
|
||||
|
||||
ASSERT_EQ(view.begin(), view.end());
|
||||
ASSERT_EQ(view.cbegin(), view.cend());
|
||||
ASSERT_TRUE(view.empty());
|
||||
}
|
||||
|
||||
TEST(SingleComponentView, ElementAccess) {
|
||||
entt::Registry<std::uint64_t> registry;
|
||||
auto view = registry.view<int>();
|
||||
const auto &cview = view;
|
||||
|
||||
const auto e0 = registry.create();
|
||||
registry.assign<int>(e0);
|
||||
|
||||
const auto e1 = registry.create();
|
||||
registry.assign<int>(e1);
|
||||
|
||||
for(typename decltype(view)::size_type i{}; i < view.size(); ++i) {
|
||||
ASSERT_EQ(view[i], i ? e0 : e1);
|
||||
ASSERT_EQ(cview[i], i ? e0 : e1);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(SingleComponentView, Contains) {
|
||||
entt::Registry<std::uint64_t> registry;
|
||||
|
||||
const auto e0 = registry.create();
|
||||
registry.assign<int>(e0);
|
||||
|
||||
const auto e1 = registry.create();
|
||||
registry.assign<int>(e1);
|
||||
|
||||
registry.destroy(e0);
|
||||
|
||||
auto view = registry.view<int>();
|
||||
|
||||
ASSERT_FALSE(view.contains(e0));
|
||||
ASSERT_TRUE(view.contains(e1));
|
||||
}
|
||||
|
||||
TEST(SingleComponentView, Empty) {
|
||||
entt::Registry<std::uint64_t> registry;
|
||||
|
||||
const auto e0 = registry.create();
|
||||
registry.assign<char>(e0);
|
||||
registry.assign<double>(e0);
|
||||
|
||||
const auto e1 = registry.create();
|
||||
registry.assign<char>(e1);
|
||||
|
||||
auto view = registry.view<int>();
|
||||
|
||||
ASSERT_EQ(view.size(), entt::Registry<std::uint64_t>::size_type{0});
|
||||
|
||||
for(auto entity: view) {
|
||||
(void)entity;
|
||||
FAIL();
|
||||
}
|
||||
}
|
||||
|
||||
TEST(SingleComponentView, Each) {
|
||||
entt::Registry<std::uint64_t> registry;
|
||||
|
||||
registry.assign<int>(registry.create());
|
||||
registry.assign<int>(registry.create());
|
||||
|
||||
auto view = registry.view<int>();
|
||||
const auto &cview = static_cast<const decltype(view) &>(view);
|
||||
std::size_t cnt = 0;
|
||||
|
||||
view.each([&cnt](auto, int &) { ++cnt; });
|
||||
|
||||
ASSERT_EQ(cnt, std::size_t{2});
|
||||
|
||||
cview.each([&cnt](auto, const int &) { --cnt; });
|
||||
|
||||
ASSERT_EQ(cnt, std::size_t{0});
|
||||
}
|
||||
|
||||
TEST(MultipleComponentView, Functionalities) {
|
||||
entt::Registry<std::uint64_t> registry;
|
||||
auto view = registry.view<int, char>();
|
||||
const auto &cview = view;
|
||||
|
||||
ASSERT_TRUE(view.empty());
|
||||
|
||||
const auto e0 = registry.create();
|
||||
registry.assign<char>(e0);
|
||||
|
||||
const auto e1 = registry.create();
|
||||
registry.assign<int>(e1);
|
||||
|
||||
ASSERT_FALSE(view.empty());
|
||||
|
||||
registry.assign<char>(e1);
|
||||
|
||||
auto it = registry.view<int, char>().begin();
|
||||
|
||||
ASSERT_EQ(*it, e1);
|
||||
ASSERT_EQ(++it, (registry.view<int, char>().end()));
|
||||
|
||||
ASSERT_NO_THROW((registry.view<int, char>().begin()++));
|
||||
ASSERT_NO_THROW((++registry.view<int, char>().begin()));
|
||||
|
||||
ASSERT_NE(view.begin(), view.end());
|
||||
ASSERT_NE(cview.begin(), cview.end());
|
||||
ASSERT_EQ(view.size(), decltype(view.size()){1});
|
||||
|
||||
registry.get<char>(e0) = '1';
|
||||
registry.get<char>(e1) = '2';
|
||||
registry.get<int>(e1) = 42;
|
||||
|
||||
for(auto entity: view) {
|
||||
const auto &cview = static_cast<const decltype(view) &>(view);
|
||||
ASSERT_EQ(std::get<0>(cview.get<int, char>(entity)), 42);
|
||||
ASSERT_EQ(std::get<1>(view.get<int, char>(entity)), '2');
|
||||
ASSERT_EQ(cview.get<char>(entity), '2');
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MultipleComponentView, Iterator) {
|
||||
entt::Registry<std::uint64_t> registry;
|
||||
const auto entity = registry.create();
|
||||
registry.assign<int>(entity);
|
||||
registry.assign<char>(entity);
|
||||
|
||||
const auto view = registry.view<int, char>();
|
||||
using iterator_type = typename decltype(view)::iterator_type;
|
||||
|
||||
iterator_type end{view.begin()};
|
||||
iterator_type begin{};
|
||||
begin = view.end();
|
||||
std::swap(begin, end);
|
||||
|
||||
ASSERT_EQ(begin, view.begin());
|
||||
ASSERT_EQ(end, view.end());
|
||||
ASSERT_NE(begin, end);
|
||||
|
||||
ASSERT_EQ(view.begin()++, view.begin());
|
||||
ASSERT_EQ(++view.begin(), view.end());
|
||||
}
|
||||
|
||||
TEST(MultipleComponentView, ConstIterator) {
|
||||
entt::Registry<std::uint64_t> registry;
|
||||
const auto entity = registry.create();
|
||||
registry.assign<int>(entity);
|
||||
registry.assign<char>(entity);
|
||||
|
||||
const auto view = registry.view<int, char>();
|
||||
using iterator_type = typename decltype(view)::iterator_type;
|
||||
|
||||
iterator_type cend{view.cbegin()};
|
||||
iterator_type cbegin{};
|
||||
cbegin = view.cend();
|
||||
std::swap(cbegin, cend);
|
||||
|
||||
ASSERT_EQ(cbegin, view.cbegin());
|
||||
ASSERT_EQ(cend, view.cend());
|
||||
ASSERT_NE(cbegin, cend);
|
||||
|
||||
ASSERT_EQ(view.cbegin()++, view.cbegin());
|
||||
ASSERT_EQ(++view.cbegin(), view.cend());
|
||||
}
|
||||
|
||||
TEST(MultipleComponentView, Contains) {
|
||||
entt::Registry<std::uint64_t> registry;
|
||||
|
||||
const auto e0 = registry.create();
|
||||
registry.assign<int>(e0);
|
||||
registry.assign<char>(e0);
|
||||
|
||||
const auto e1 = registry.create();
|
||||
registry.assign<int>(e1);
|
||||
registry.assign<char>(e1);
|
||||
|
||||
registry.destroy(e0);
|
||||
|
||||
auto view = registry.view<int, char>();
|
||||
|
||||
ASSERT_FALSE(view.contains(e0));
|
||||
ASSERT_TRUE(view.contains(e1));
|
||||
}
|
||||
|
||||
TEST(MultipleComponentView, Empty) {
|
||||
entt::Registry<std::uint64_t> registry;
|
||||
|
||||
const auto e0 = registry.create();
|
||||
registry.assign<double>(e0);
|
||||
registry.assign<int>(e0);
|
||||
registry.assign<float>(e0);
|
||||
|
||||
const auto e1 = registry.create();
|
||||
registry.assign<char>(e1);
|
||||
registry.assign<float>(e1);
|
||||
|
||||
auto view = registry.view<char, int, float>();
|
||||
|
||||
for(auto entity: view) {
|
||||
(void)entity;
|
||||
FAIL();
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MultipleComponentView, Each) {
|
||||
entt::Registry<std::uint64_t> registry;
|
||||
|
||||
const auto e0 = registry.create();
|
||||
registry.assign<int>(e0);
|
||||
registry.assign<char>(e0);
|
||||
|
||||
const auto e1 = registry.create();
|
||||
registry.assign<int>(e1);
|
||||
registry.assign<char>(e1);
|
||||
|
||||
auto view = registry.view<int, char>();
|
||||
const auto &cview = static_cast<const decltype(view) &>(view);
|
||||
std::size_t cnt = 0;
|
||||
|
||||
view.each([&cnt](auto, int &, char &) { ++cnt; });
|
||||
|
||||
ASSERT_EQ(cnt, std::size_t{2});
|
||||
|
||||
cview.each([&cnt](auto, const int &, const char &) { --cnt; });
|
||||
|
||||
ASSERT_EQ(cnt, std::size_t{0});
|
||||
}
|
||||
|
||||
TEST(MultipleComponentView, EachWithHoles) {
|
||||
entt::Registry<std::uint64_t> registry;
|
||||
|
||||
const auto e0 = registry.create();
|
||||
const auto e1 = registry.create();
|
||||
const auto e2 = registry.create();
|
||||
|
||||
registry.assign<char>(e0, '0');
|
||||
registry.assign<char>(e1, '1');
|
||||
|
||||
registry.assign<int>(e0, 0);
|
||||
registry.assign<int>(e2, 2);
|
||||
|
||||
auto view = registry.view<char, int>();
|
||||
|
||||
view.each([e0](auto entity, const char &c, const int &i) {
|
||||
if(e0 == entity) {
|
||||
ASSERT_EQ(c, '0');
|
||||
ASSERT_EQ(i, 0);
|
||||
} else {
|
||||
FAIL();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
TEST(RawView, Functionalities) {
|
||||
entt::Registry<std::uint64_t> registry;
|
||||
auto view = registry.view<char>(entt::raw_t{});
|
||||
@@ -634,3 +629,234 @@ TEST(RawView, Each) {
|
||||
|
||||
ASSERT_EQ(cnt, std::size_t{0});
|
||||
}
|
||||
|
||||
TEST(RuntimeView, Functionalities) {
|
||||
entt::Registry<std::uint64_t> registry;
|
||||
using component_type = typename decltype(registry)::component_type;
|
||||
|
||||
// forces the creation of the pools
|
||||
registry.reserve<int>(0);
|
||||
registry.reserve<char>(0);
|
||||
|
||||
component_type types[] = { registry.type<int>(), registry.type<char>() };
|
||||
auto view = registry.view(std::begin(types), std::end(types));
|
||||
const auto &cview = view;
|
||||
|
||||
ASSERT_TRUE(view.empty());
|
||||
|
||||
const auto e0 = registry.create();
|
||||
registry.assign<char>(e0);
|
||||
|
||||
const auto e1 = registry.create();
|
||||
registry.assign<int>(e1);
|
||||
|
||||
ASSERT_FALSE(view.empty());
|
||||
|
||||
registry.assign<char>(e1);
|
||||
|
||||
auto it = registry.view(std::begin(types), std::end(types)).begin();
|
||||
|
||||
ASSERT_EQ(*it, e1);
|
||||
ASSERT_EQ(++it, (registry.view(std::begin(types), std::end(types)).end()));
|
||||
|
||||
ASSERT_NO_THROW((registry.view(std::begin(types), std::end(types)).begin()++));
|
||||
ASSERT_NO_THROW((++registry.view(std::begin(types), std::end(types)).begin()));
|
||||
|
||||
ASSERT_NE(view.begin(), view.end());
|
||||
ASSERT_NE(cview.begin(), cview.end());
|
||||
ASSERT_EQ(view.size(), decltype(view.size()){1});
|
||||
|
||||
registry.get<char>(e0) = '1';
|
||||
registry.get<char>(e1) = '2';
|
||||
registry.get<int>(e1) = 42;
|
||||
|
||||
for(auto entity: view) {
|
||||
ASSERT_EQ(registry.get<int>(entity), 42);
|
||||
ASSERT_EQ(registry.get<char>(entity), '2');
|
||||
}
|
||||
}
|
||||
|
||||
TEST(RuntimeView, Iterator) {
|
||||
entt::Registry<std::uint64_t> registry;
|
||||
using component_type = typename decltype(registry)::component_type;
|
||||
|
||||
const auto entity = registry.create();
|
||||
registry.assign<int>(entity);
|
||||
registry.assign<char>(entity);
|
||||
|
||||
component_type types[] = { registry.type<int>(), registry.type<char>() };
|
||||
auto view = registry.view(std::begin(types), std::end(types));
|
||||
using iterator_type = typename decltype(view)::iterator_type;
|
||||
|
||||
iterator_type end{view.begin()};
|
||||
iterator_type begin{};
|
||||
begin = view.end();
|
||||
std::swap(begin, end);
|
||||
|
||||
ASSERT_EQ(begin, view.begin());
|
||||
ASSERT_EQ(end, view.end());
|
||||
ASSERT_NE(begin, end);
|
||||
|
||||
ASSERT_EQ(view.begin()++, view.begin());
|
||||
ASSERT_EQ(++view.begin(), view.end());
|
||||
}
|
||||
|
||||
TEST(RuntimeView, ConstIterator) {
|
||||
entt::Registry<std::uint64_t> registry;
|
||||
using component_type = typename decltype(registry)::component_type;
|
||||
|
||||
const auto entity = registry.create();
|
||||
registry.assign<int>(entity);
|
||||
registry.assign<char>(entity);
|
||||
|
||||
component_type types[] = { registry.type<int>(), registry.type<char>() };
|
||||
auto view = registry.view(std::begin(types), std::end(types));
|
||||
using iterator_type = typename decltype(view)::iterator_type;
|
||||
|
||||
iterator_type cend{view.cbegin()};
|
||||
iterator_type cbegin{};
|
||||
cbegin = view.cend();
|
||||
std::swap(cbegin, cend);
|
||||
|
||||
ASSERT_EQ(cbegin, view.cbegin());
|
||||
ASSERT_EQ(cend, view.cend());
|
||||
ASSERT_NE(cbegin, cend);
|
||||
|
||||
ASSERT_EQ(view.cbegin()++, view.cbegin());
|
||||
ASSERT_EQ(++view.cbegin(), view.cend());
|
||||
}
|
||||
|
||||
TEST(RuntimeView, Contains) {
|
||||
entt::Registry<std::uint64_t> registry;
|
||||
using component_type = typename decltype(registry)::component_type;
|
||||
|
||||
const auto e0 = registry.create();
|
||||
registry.assign<int>(e0);
|
||||
registry.assign<char>(e0);
|
||||
|
||||
const auto e1 = registry.create();
|
||||
registry.assign<int>(e1);
|
||||
registry.assign<char>(e1);
|
||||
|
||||
registry.destroy(e0);
|
||||
|
||||
component_type types[] = { registry.type<int>(), registry.type<char>() };
|
||||
auto view = registry.view(std::begin(types), std::end(types));
|
||||
|
||||
ASSERT_FALSE(view.contains(e0));
|
||||
ASSERT_TRUE(view.contains(e1));
|
||||
}
|
||||
|
||||
TEST(RuntimeView, Empty) {
|
||||
entt::Registry<std::uint64_t> registry;
|
||||
using component_type = typename decltype(registry)::component_type;
|
||||
|
||||
const auto e0 = registry.create();
|
||||
registry.assign<double>(e0);
|
||||
registry.assign<int>(e0);
|
||||
registry.assign<float>(e0);
|
||||
|
||||
const auto e1 = registry.create();
|
||||
registry.assign<char>(e1);
|
||||
registry.assign<float>(e1);
|
||||
|
||||
component_type types[] = { registry.type<char>(), registry.type<int>(), registry.type<float>() };
|
||||
auto view = registry.view(std::begin(types), std::end(types));
|
||||
|
||||
for(auto entity: view) {
|
||||
(void)entity;
|
||||
FAIL();
|
||||
}
|
||||
}
|
||||
|
||||
TEST(RuntimeView, Each) {
|
||||
entt::Registry<std::uint64_t> registry;
|
||||
using component_type = typename decltype(registry)::component_type;
|
||||
|
||||
const auto e0 = registry.create();
|
||||
registry.assign<int>(e0);
|
||||
registry.assign<char>(e0);
|
||||
|
||||
const auto e1 = registry.create();
|
||||
registry.assign<int>(e1);
|
||||
registry.assign<char>(e1);
|
||||
|
||||
component_type types[] = { registry.type<int>(), registry.type<char>() };
|
||||
auto view = registry.view(std::begin(types), std::end(types));
|
||||
std::size_t cnt = 0;
|
||||
|
||||
view.each([&cnt](auto) { ++cnt; });
|
||||
|
||||
ASSERT_EQ(cnt, std::size_t{2});
|
||||
}
|
||||
|
||||
TEST(RuntimeView, EachWithHoles) {
|
||||
entt::Registry<std::uint64_t> registry;
|
||||
using component_type = typename decltype(registry)::component_type;
|
||||
|
||||
const auto e0 = registry.create();
|
||||
const auto e1 = registry.create();
|
||||
const auto e2 = registry.create();
|
||||
|
||||
registry.assign<char>(e0, '0');
|
||||
registry.assign<char>(e1, '1');
|
||||
|
||||
registry.assign<int>(e0, 0);
|
||||
registry.assign<int>(e2, 2);
|
||||
|
||||
component_type types[] = { registry.type<int>(), registry.type<char>() };
|
||||
auto view = registry.view(std::begin(types), std::end(types));
|
||||
|
||||
view.each([e0](auto entity) {
|
||||
ASSERT_EQ(e0, entity);
|
||||
});
|
||||
}
|
||||
|
||||
TEST(RuntimeView, MissingPool) {
|
||||
entt::Registry<std::uint64_t> registry;
|
||||
using component_type = typename decltype(registry)::component_type;
|
||||
|
||||
const auto e0 = registry.create();
|
||||
registry.assign<int>(e0);
|
||||
|
||||
component_type types[] = { registry.type<int>(), registry.type<char>() };
|
||||
auto view = registry.view(std::begin(types), std::end(types));
|
||||
|
||||
ASSERT_TRUE(view.empty());
|
||||
ASSERT_EQ(view.size(), decltype(view.size()){0});
|
||||
|
||||
registry.assign<char>(e0);
|
||||
|
||||
ASSERT_TRUE(view.empty());
|
||||
ASSERT_EQ(view.size(), decltype(view.size()){0});
|
||||
ASSERT_FALSE(view.contains(e0));
|
||||
|
||||
view.each([](auto) { FAIL(); });
|
||||
|
||||
for(auto entity: view) {
|
||||
(void)entity;
|
||||
FAIL();
|
||||
}
|
||||
}
|
||||
|
||||
TEST(RuntimeView, EmptyRange) {
|
||||
entt::Registry<std::uint64_t> registry;
|
||||
using component_type = typename decltype(registry)::component_type;
|
||||
|
||||
const auto e0 = registry.create();
|
||||
registry.assign<int>(e0);
|
||||
|
||||
const component_type *ptr = nullptr;
|
||||
auto view = registry.view(ptr, ptr);
|
||||
|
||||
ASSERT_TRUE(view.empty());
|
||||
ASSERT_EQ(view.size(), decltype(view.size()){0});
|
||||
ASSERT_FALSE(view.contains(e0));
|
||||
|
||||
view.each([](auto) { FAIL(); });
|
||||
|
||||
for(auto entity: view) {
|
||||
(void)entity;
|
||||
FAIL();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,34 +235,41 @@ public:
|
||||
|
||||
duk_push_array(ctx);
|
||||
|
||||
dreg.registry.each([ctx, nargs, &pos, &dreg](auto entity) {
|
||||
auto ®istry = dreg.registry;
|
||||
auto &func = dreg.func;
|
||||
bool match = true;
|
||||
std::vector<typename entt::DefaultRegistry::component_type> components;
|
||||
std::vector<typename entt::DefaultRegistry::component_type> runtime;
|
||||
|
||||
for (duk_idx_t arg = 0; match && arg < nargs; arg++) {
|
||||
auto type = duk_require_uint(ctx, arg);
|
||||
for(duk_idx_t arg = 0; arg < nargs; arg++) {
|
||||
auto type = duk_require_uint(ctx, arg);
|
||||
|
||||
if(type < udef) {
|
||||
assert(func.find(type) != func.cend());
|
||||
match = (registry.*func[type].test)(entity);
|
||||
} else {
|
||||
const auto ctype = registry.type<DuktapeRuntime>();
|
||||
assert(func.find(ctype) != func.cend());
|
||||
match = (registry.*func[ctype].test)(entity);
|
||||
|
||||
if(match) {
|
||||
auto &components = registry.get<DuktapeRuntime>(entity).components;
|
||||
match = (components.find(type) != components.cend());
|
||||
}
|
||||
if(type < udef) {
|
||||
components.push_back(type);
|
||||
} else {
|
||||
if(runtime.empty()) {
|
||||
components.push_back(dreg.registry.type<DuktapeRuntime>());
|
||||
}
|
||||
}
|
||||
|
||||
if(match) {
|
||||
runtime.push_back(type);
|
||||
}
|
||||
}
|
||||
|
||||
auto view = dreg.registry.view(components.cbegin(), components.cend());
|
||||
|
||||
for(const auto entity: view) {
|
||||
if(runtime.empty()) {
|
||||
duk_push_uint(ctx, entity);
|
||||
duk_put_prop_index(ctx, -2, pos++);
|
||||
} else {
|
||||
const auto &components = dreg.registry.get<DuktapeRuntime>(entity).components;
|
||||
const auto match = std::all_of(runtime.cbegin(), runtime.cend(), [&components](const auto type) {
|
||||
return components.find(type) != components.cend();
|
||||
});
|
||||
|
||||
if(match) {
|
||||
duk_push_uint(ctx, entity);
|
||||
duk_put_prop_index(ctx, -2, pos++);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user