Просмотр исходного кода

doc:
* const thread safe registry
* detached views

Michele Caini 4 лет назад
Родитель
Сommit
89b2131052
1 измененных файлов с 37 добавлено и 32 удалено
  1. 37 32
      docs/md/entity.md

+ 37 - 32
docs/md/entity.md

@@ -161,16 +161,12 @@ below. For more details, please refer to the inline documentation.
 In `EnTT`, pools of components are made available through a specialized version
 of a sparse set.
 
-Each pool contains all the instances of a single component, as well as all the
-entities to which it's assigned. Sparse arrays are also _paged_ to avoid wasting
-memory in some cases while packed arrays are not for obvious reasons.<br/>
-Pools also make available at any time a pointer to the packed lists of entities
-and components they contain, in addition to the number of elements in use. For
-this reason, pools can rearrange their items in order to keep the internal
-arrays tightly packed and maximize performance.
-
-At the moment, it's possible to specialize pools within certain limits, although
-a more flexible and user-friendly model is under development.
+Each pool contains all the instances of a single component and all the entities
+to which it's assigned. Sparse arrays are _paged_ to avoid wasting memory in
+some cases. Packed arrays of components are also paged to have pointer stability
+upon additions. Packed arrays of entities are not instead.<br/>
+All pools can rearrange their items in order to keep the internal arrays tightly
+packed and maximize performance.
 
 # The Registry, the Entity and the Component
 
@@ -181,7 +177,7 @@ to represent an entity. Because `std::uint32_t` is large enough for almost all
 the cases, there exists also the enum class `entt::entity` that _wraps_ it and
 the alias `entt::registry` for `entt::basic_registry<entt::entity>`.
 
-Entities are represented by _entity identifiers_. An entity identifier carries
+Entities are represented by _entity identifiers_. An entity identifier contains
 information about the entity itself and its version.<br/>
 User defined identifiers can be introduced through enum classes and class types
 that define an `entity_type` member of type `std::uint32_t` or `std::uint64_t`.
@@ -1327,7 +1323,7 @@ In particular:
   void operator()(entt::entity, const T &);
   ```
 
-  The output archive can freely decide how to serialize the data. The register
+  The output archive can freely decide how to serialize the data. The registry
   is not affected at all by the decision.
 
 * An input archive, the one used when restoring a snapshot, must expose a
@@ -1432,10 +1428,13 @@ type view exposes utility functions to get the estimated number of entities it
 is going to return and to know if it contains a given entity.<br/>
 Refer to the inline documentation for all the details.
 
-There is no need to store views aside for they are extremely cheap to construct,
-even though valid views can be copied without problems and reused freely.<br/>
-Views also return newly created and correctly initialized iterators whenever
-`begin` or `end` are invoked.
+There is no need to store views aside as they are extremely cheap to construct.
+In fact, this is even discouraged when creating a view from a const registry.
+Since all storage are lazily initialized, they may not exist when the view is
+built. Therefore, the view itself will refer to an empty _placeholder_ and will
+never be re-assigned the actual storage.<br/>
+In all cases, views return newly created and correctly initialized iterators for
+the storage they refer to when `begin` or `end` are invoked.
 
 Views share the way they are created by means of a registry:
 
@@ -1548,7 +1547,7 @@ Refer to the inline documentation for all the details.
 
 Runtime views are pretty cheap to construct and should not be stored aside 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/>
+thrown away.<br/>
 To iterate a runtime view, either use it in a range-for loop:
 
 ```cpp
@@ -1618,9 +1617,8 @@ list for owned components. It's also possible to ask a group if it contains a
 given entity.<br/>
 Refer to the inline documentation for all the details.
 
-There is no need to store groups aside for they are extremely cheap to
-construct, even though valid groups can be copied without problems and reused
-freely.<br/>
+There is no need to store groups aside for they are extremely cheap to create,
+even though valid groups can be copied without problems and reused freely.<br/>
 A group performs an initialization step the very first time it's requested and
 this could be quite costly. To avoid it, consider creating the group when no
 components have been assigned yet. If the registry is empty, preparation is
@@ -2133,25 +2131,32 @@ should even benefit from it further.
 
 ## Const registry
 
-Contrary to what the standard library containers offer, a const registry is
-generally but not completely thread safe.<br/>
-In particular, one (and only one) of its const member functions isn't fully
-thread safe. That is the `view` method.
-
+A const registry is also fully thread safe. This means that it won't be able to
+lazily initialize a missing storage when a view is generated.<br/>
 The reason for this is easy to explain. To avoid requiring types to be
-_announced_ in advance, the registry lazily initializes the storage objects for
-the different components.<br/>
-In most cases, this isn't even necessary. The absence of a storage is itself the
-required information. However, when building a view, all pools must necessarily
-exist. This makes the `view` member function not thread safe even in its const
-overload, unless all pools already exist.
+_announced_ in advance, a registry lazily creates the storage objects for the
+different components. However, this isn't possible for a thread safe const
+registry.<br/>
+On the other side, all pools must necessarily _exist_ when creating a view.
+Therefore, static _placeholders_ for missing storage are used to fill the gap.
+
+Note that returned views are always valid and behave as expected in the context
+of the caller. The only difference is that static _placeholders_ (if any) are
+never renewed.<br/>
+As a result, a view created from a const registry may behave incorrectly over
+time if it's kept for a second use.<br/>
+Therefore, if the general advice is to create views when necessary and discard
+them immediately afterwards, this becomes almost a rule when it comes to views
+generated from a const registry.
 
 Fortunately, there is also a way to instantiate storage classes early when in
 doubt or when there are special requirements.<br/>
 Calling the `prepare` method is equivalent to _announcing_ the existence of a
 particular storage, to avoid running into problems. For those interested, there
 are also alternative approaches, such as a single threaded tick for the registry
-warm-up, but these are not always applicable.
+warm-up, but these are not always applicable.<br/>
+In this case, no placeholders will be used since all storage exist. In other
+words, views never risk becoming _invalid_.
 
 # Beyond this document