Michele Caini 6 лет назад
Родитель
Сommit
df5b90cc7c
2 измененных файлов с 98 добавлено и 21 удалено
  1. 1 2
      TODO
  2. 97 19
      docs/md/entity.md

+ 1 - 2
TODO

@@ -17,7 +17,7 @@
 * built-in support for dual (or N-) buffering
 * allow for custom stomp functions
 * deprecate/replace snapshot
-* experiment with hi-bitset for views
+* hibitset, views and non-owning groups
 * custom (decoupled) pools ==> double buffering, shared components, multi-model
 * make meta work across boundaries
   - inline variables are fine here, only the head represents a problem
@@ -32,4 +32,3 @@
 * multi component registry::remove and some others?
   - auto foo(It first, It last = entity_type{null})
 * range based registry::remove and some others?
-* nested groups: AB/ABC/ABCD/... (hints: sort, check functions)

+ 97 - 19
docs/md/entity.md

@@ -37,6 +37,7 @@
     * [Full-owning groups](#full-owning-groups)
     * [Partial-owning groups](#partial-owning-groups)
     * [Non-owning groups](#non-owning-groups)
+    * [Nested groups](#nested-groups)
   * [Types: const, non-const and all in between](#types-const-non-const-and-all-in-between)
   * [Give me everything](#give-me-everything)
   * [What is allowed and what is not](#what-is-allowed-and-what-is-not)
@@ -194,9 +195,9 @@ registry.destroy(view.begin(), view.end());
 ```
 
 In both cases, the `create` member function accepts also a list of default
-constructible types of components to assign to the entities before to return.
-It's a faster alternative to the creation and subsequent assignment of
-components in separate steps.
+constructible component types to assign to the entities before to return. It's a
+faster alternative to the creation and subsequent assignment of components in
+separate steps.
 
 When an entity is destroyed, the registry can freely reuse it internally with a
 slightly different identifier. In particular, the version of an entity is
@@ -847,7 +848,7 @@ consequence of a couple of design choices from the past and in the present:
   correctly from scratch.
 
 * Furthermore, the registry makes heavy use of _type-erasure_ techniques
-  internally and doesn't know at any time what types of components it contains.
+  internally and doesn't know at any time what component types it contains.
   Therefore being explicit at the call point is mandatory.
 
 There exists also another version of the `component` member function that
@@ -1061,11 +1062,11 @@ other functionality of the registry and keep memory usage at a minimum.
 Groups come in three different flavors: _full-owning groups_, _partial-owning
 groups_ and _non-owning groups_. The main difference between them is in terms of
 performance.<br/>
-Groups can literally _own_ one or more types of components. It means that they
-will be allowed to rearrange pools so as to speed up iterations. Roughly
-speaking: the more components a group owns, the faster it is to iterate them.
-On the other side, a given component can belong only to one group, so users have
-to define groups carefully to get the best out of them.
+Groups can literally _own_ one or more component types. It means that they will
+be allowed to rearrange pools so as to speed up iterations. Roughly speaking:
+the more components a group owns, the faster it is to iterate them. On the other
+side, a given component can belong to multiple groups only if they are _nested_,
+so users have to define groups carefully to get the best out of them.
 
 Continue reading for more details or refer to the inline documentation.
 
@@ -1223,9 +1224,9 @@ views.
 
 ## Groups
 
-Groups are meant to iterate multiple components at once and offer a faster
-alternative to views. Roughly speaking, they just play in another league when
-compared to views.<br/>
+Groups are meant to iterate multiple components at once and offer a (much)
+faster alternative to views. Roughly speaking, they just play in another league
+when compared to views.<br/>
 Groups overcome the performance of the other tools available but require to get
 the ownership of components and this sets some constraints on pools. On the
 other side, groups aren't an automatism that increases memory consumption,
@@ -1332,9 +1333,7 @@ Sorting owned components is no longer allowed once the group has been created.
 However, full-owning groups can be sorted by means of their `sort` member
 functions, if required. Sorting a full-owning group affects all the instances of
 the same group (it means that users don't have to call `sort` on each instance
-to sort all of them because they share the underlying data structure).<br/>
-The elements that aren't part of the group can still be sorted separately for
-each pool using the `sort` member function of the registry.
+to sort all of them because they share the underlying data structure).
 
 ### Partial-owning groups
 
@@ -1366,10 +1365,7 @@ However, partial-owning groups can be sorted by means of their `sort` member
 functions, if required. Sorting a partial-owning group affects all the instances
 of the same group (it means that users don't have to call `sort` on each
 instance to sort all of them because they share the underlying data
-structure).<br/>
-Regarding the owned types, the elements that aren't part of the group can still
-be sorted separately for each pool using the `sort` member function of the
-registry.
+structure).
 
 ### Non-owning groups
 
@@ -1400,6 +1396,88 @@ required. Sorting a non-owning group affects all the instance of the same group
 (it means that users don't have to call `sort` on each instance to sort all of
 them because they share the set of entities).
 
+### Nested groups
+
+A type of component cannot be owned by two or more conflicting groups such as:
+
+* `registry.group<transform, sprite>()`.
+* `registry.group<transform, rotation>()`.
+
+However, the same type can be owned by groups belonging to the same _family_,
+also called _nested groups_, such as:
+
+* `registry.group<sprite, transform>()`.
+* `registry.group<sprite, transform, rotation>()`.
+
+Fortunately, these are also very common cases if not the most common ones.<br/>
+This allows users to have the highest possible performance on a greater number
+of component combinations.
+
+Two nested groups are such that they own at least one type of component and the
+list of component types involved by one of them is contained entirely in that of
+the other. More specifically, this applies independently to all component lists
+used to define a group.<br/>
+Therefore, the rules for defining whether two or more groups are nested can be
+summarized as:
+
+* One of the groups involves one or more additional component types with respect
+  to the other, whether they are owned, observed or excluded.
+
+* The list of component types owned by the most restrictive group is the same or
+  contains entirely that of the others. This also applies to the list of
+  observed and excluded components.
+
+It means that more nested groups _extend_ the their parents by adding more
+conditions in the form of new components.
+
+As mentioned, the components don't necessarily have to be all _owned_ so that
+two groups can be considered nested. In other words, the following definitions
+are fully valid:
+
+* `registry.group<sprite>(entt::get<renderable>)`.
+* `registry.group<sprite, transform>(entt::get<renderable>)`.
+* `registry.group<sprite, transform>(entt::get<renderable, rotation>)`.
+
+Exclusion lists also play their part in this respect. When it comes to defining
+nested groups, an excluded type of component `T` is treated as being an observed
+type `not_T`. Therefore, these two definitions:
+
+* `registry.group<sprite, transform>()`.
+* `registry.group<sprite, transform>(entt::exclude<rotation>)`.
+
+Are treated as if users were defining the following groups:
+
+* `group<sprite, transform>()`.
+* `group<sprite, transform>(entt::get<not_rotation>)`.
+
+Where `not_rotation` is an empty tag present only when `rotation` is not.
+
+Because of this, to define a new group that is more restrictive than an existing
+one, it's enough to take the list of component types of the latter and extend it
+by adding new component types either owned, observed or excluded, without any
+precautions depending on the case.<br/>
+The opposite is also true. To define a _larger_ group, it will be enough to take
+an existing one and remove _constraints_ from it, in whatever form they are
+expressed.<br/>
+Note that the greater the number of component types involved by a group, the
+more restrictive it is.
+
+Despite the extreme flexibility of nested groups, which allow to independently
+use component types either owned, observed or excluded, the real strength of
+this tool lies in the possibility of defining a greater number of groups that
+**own** the same components, thus offering the best performance in more
+cases.<br/>
+In fact, given a list of component types involved by a group, the greater the
+number of those owned, the greater the performance of the group itself.
+
+As a side note, it's no longer possible to sort all groups when defining nested
+ones. This is because the most restrictive groups share the elements with the
+less restrictive ones and ordering the latter would invalidate the former.<br/>
+However, given a family of nested groups, it's still possible to sort the most
+restrictive of them. To prevent users from having to remember which of their
+groups is the most restrictive, they offer the `sortable` member function to
+know if their items can be sorted or not.
+
 ## Types: const, non-const and all in between
 
 The `registry` class offers two overloads when it comes to constructing views