Skip to content

Commit

Permalink
Introduce new system Pipeline behavior
Browse files Browse the repository at this point in the history
Close: #184
  • Loading branch information
AndreasLrx committed Nov 21, 2024
1 parent 19680ae commit 74422f5
Show file tree
Hide file tree
Showing 11 changed files with 618 additions and 94 deletions.
81 changes: 32 additions & 49 deletions doc/Tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -433,61 +433,44 @@ registry.runSystem<Movement>();
registry.runSystems();
```
By default systems added with `Registry.addSystem()` have the same priority and the order of execution is undefined when calling `Registry.runSystems()`.
But you can use the system priorities/groups to order them and be able to specify their priority and run a given system group.
### Pipeline
1. Priority
By default systems added with `Registry.addSystem()` run in the registration order. But you can use @ref ecstasy::Pipeline "pipelines" to have a better configuration of your systems run order.
When adding a system to the registry you can add a priority to each system as a second template parameter.
Calling `runSystems()` will run the systems in ascending priority order.
You can group your systems into @ref ecstasy::Pipeline::Phase "phases" which will have an associated priority, being also its identifier.
Some predefined phases already exists and may be enough for you: @ref ecstasy::Pipeline::PredefinedPhases "PredefinedPhases".
@warning
You can set the same priority for multiple systems but it doesn't ensure they will always run in the same order.
```cpp
ecstasy::Registry registry;
registry.addSystem<A, 1>();
registry.addSystem<B, 2>();
registry.addSystem<C, 3>();
registry.addSystem<D, 4>();
registry.addSystem<E, 5>();
registry.addSystem<F, 6>();
registry.runSystems(); // ABCDEF
// If you want you can still call systems one by one
registry.runSystem<A>(); // A
```

2. Group and masks
@note
If you don't specify a phase, the @ref ecstasy::Pipeline::PredefinedPhases::OnUpdate "OnUpdate" is used.
In fact their is nothing more than the priority value. But using bitmask you can create groups.
@warning
I strongly recommend you to use enum types, or const values/macros.
The following example use 8 bits for the group id and 8 for the system priority.
```cpp
ecstasy::Registry registry;
```cpp
ecstasy::Registry registry;
// Mask of the group part on the priority (bits 8->15)
size_t mask = 0xff00;
// Groups 'identifiers'
const size_t abc = 0x0100;
const size_t def = 0x0200;

// Group 'abc'
registry.addSystem<A, abc + 1>();
registry.addSystem<B, abc + 2>();
registry.addSystem<C, abc + 3>();
// Group 'def'
registry.addSystem<D, def + 1>();
registry.addSystem<E, def + 2>();
registry.addSystem<F, def + 3>();

// The following still calls the systems in the right order because their priorities are still ascending
registry.runSystems(); // ABCDEF

// You can run a system group by sending the group id and the mask
registry.runSystems(abc, mask); // ABC
registry.runSystems(def, mask); // DEF
```
// Default to OnUpdate
registry.addSystem<A>();
// Explicit OnUpdate, require to be casted as size_t if template parameter
registry.addSystem<B, static_cast<size_t>(Pipeline::PedefinedPhases::OnUpdate)>();
// Explicit OnLoad but using enum value directly
registry.addSystemInPhase<C>(Pipeline::PedefinedPhases::OnLoad);
registry.addSystem<D, 250>();
registry.addSystem<E, 251>();
registry.addSystem<F, Pipeline::PedefinedPhases::OnUpdate>();
// Will run in order:
// - OnLoad(100): C
// - Custom 250: D
// - Custom 251: E
// - OnUpdate(400): BCF
registry.runSystems();
// If you want you can still call systems one by one
registry.runSystem<A>(); // A
// Or even phase by phase
registry.runSystemsPhase<Pipeline::PredefinedPhases::OnUpdate>();
```

## Using resources

Expand Down
22 changes: 9 additions & 13 deletions src/ecstasy/registry/Registry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ namespace ecstasy
return _builder.build();
}

Registry::Registry(bool addEntities)
Registry::Registry(bool addEntities) : _pipeline(*this)
{
if (addEntities)
addResource<Entities>();
Expand Down Expand Up @@ -81,23 +81,19 @@ namespace ecstasy
storage.second->erase(entities);
}

void Registry::runSystem(const std::type_index &systemId)
{
_systems.get(systemId).run(*this);
}

void Registry::runSystems()
{
for (auto &[type, system] : _systems.getInner())
system->run(*this);
_pipeline.run();
}

void Registry::runSystems(size_t group, size_t mask)
void Registry::runSystemsPhase(Pipeline::PhaseId phase)
{
auto &systems = _systems.getInner();
auto it = systems.begin();

// Run all systems with the same group and mask
while (it != systems.end()) {
if ((it->first.second & mask) == group)
it->second->run(*this);
++it;
}
_pipeline.run(phase);
}

void Registry::clear()
Expand Down
95 changes: 79 additions & 16 deletions src/ecstasy/registry/Registry.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "ecstasy/storages/StorageConcepts.hpp"
#include "ecstasy/storages/SystemInstances.hpp"
#include "ecstasy/system/ISystem.hpp"
#include "ecstasy/system/Pipeline.hpp"
#include "ecstasy/thread/LockableView.hpp"
#include "util/Allocator.hpp"
#include "util/StackAllocator.hpp"
Expand All @@ -34,6 +35,7 @@
#include "concepts/component_type.hpp"
#include "concepts/queryable_type.hpp"
#include "ecstasy/registry/concepts/modifier_allocator_size.hpp"
#include "util/meta/is_size_t_convertible.hpp"
#include "util/meta/outer_join.hpp"

#ifdef _MSC_VER
Expand Down Expand Up @@ -815,8 +817,11 @@ namespace ecstasy
///
/// @brief Add a new system in the registry.
///
/// @note Call @ref addSystemInPhase to add a system in a specific phase using runtime phase id or an Enum type
/// for phase id.
///
/// @tparam S System to add.
/// @tparam Priority System priority. See @ref Registry::runSystems().
/// @tparam Phase System phase. See @ref Pipeline.
/// @tparam Args The type of arguments to pass to the constructor of @b S.
///
/// @param[in] args The arguments to pass to the constructor of @b S.
Expand All @@ -828,10 +833,36 @@ namespace ecstasy
/// @author Andréas Leroux ([email protected])
/// @since 1.0.0 (2022-10-17)
///
template <std::derived_from<ISystem> S, size_t Priority = 0, typename... Args>
template <std::derived_from<ISystem> S,
Pipeline::PhaseId Phase = static_cast<Pipeline::PhaseId>(Pipeline::PredefinedPhases::OnUpdate),
typename... Args>
S &addSystem(Args &&...args)
{
return _systems.emplace<S, Priority>(std::forward<Args>(args)...);
return addSystemInPhase<S>(Phase, std::forward<Args>(args)...);
}

///
/// @brief Add a new system in the registry in a specific phase.
///
/// @tparam S System to add.
/// @tparam T Phase id type. Usually an enum convertible to size_t, or a size_t directly.
/// @tparam Args The type of arguments to pass to the constructor of @b S.
///
/// @param[in] phaseId Phase id where to add the system.
/// @param[in] args The arguments to pass to the constructor of @b S.
///
/// @return S& newly created System.
///
/// @author Andréas Leroux ([email protected])
/// @since 1.0.0 (2024-11-21)
///
template <std::derived_from<ISystem> S, util::meta::is_size_t_convertible T, typename... Args>
S &addSystemInPhase(T phaseId, Args &&...args)
{
S &system = _systems.emplace<S>(std::forward<Args>(args)...);

_pipeline.addSystem(typeid(S), static_cast<size_t>(phaseId));
return system;
}

///
Expand Down Expand Up @@ -1170,32 +1201,36 @@ namespace ecstasy
_systems.get<S>().run(*this);
}

///
/// @brief Run a specific system from the registry.
///
/// @param[in] systemId System type index to run.
///
/// @author Andréas Leroux ([email protected])
/// @since 1.0.0 (2024-11-21)
///
void runSystem(const std::type_index &systemId);

///
/// @brief Run all systems present in the registry.
///
/// @note Systems are run in ascending priority order. If two systems have the same priority, run order is
/// undefined.
/// @note Systems are run in ascending registration order. You can have further control using
/// @ref ecstasy::Pipeline::Phase "Phases".
///
/// @author Andréas Leroux ([email protected])
/// @since 1.0.0 (2022-10-17)
///
void runSystems();

///
/// @brief Run all systems whose priority match the given group.
/// @brief Run all systems present in the registry for the given phase.
///
/// @note The system groups can be seen as an internet network: The @p group 'id' is the network address, the
/// @p mask is the network mask and each system priority in the group is a host in the network range.
/// @param[in] phase Phase to run the systems for.
///
/// @note Systems in the group are run in ascending priority order. If two systems have the same priority, run
/// order is undefined.
///
/// @param[in] group Group priority 'id'.
/// @param[in] mask Priority 'id' mask.
/// @author Andréas Leroux ([email protected])
/// @since 1.0.0 (2022-12-19)
/// @since 1.0.0 (2024-11-21)
///
void runSystems(size_t group, size_t mask);
void runSystemsPhase(Pipeline::PhaseId phase);

///
/// @brief Get a const reference to the storages instances.
Expand Down Expand Up @@ -1223,13 +1258,41 @@ namespace ecstasy
return _storages;
}

///
/// @brief Get a reference to the registry pipeline.
///
/// @return Pipeline& Reference to the registry pipeline instance.
///
/// @author Andréas Leroux ([email protected])
/// @since 1.0.0 (2024-11-21)
///
[[nodiscard]] constexpr Pipeline &getPipeline() noexcept
{
return _pipeline;
}

///
/// @brief Get a const reference to the registry pipeline.
///
/// @return const Pipeline& Const reference to the registry pipeline instance.
///
/// @author Andréas Leroux ([email protected])
/// @since 1.0.0 (2024-11-21)
///
[[nodiscard]] constexpr const Pipeline &getPipeline() const noexcept
{
return _pipeline;
}

private:
/// @brief Registry resources.
Instances<IResource> _resources;
/// @brief Registry storages.
Instances<IStorage> _storages;
/// @brief Registry systems.
SystemInstances _systems;
Instances<ISystem> _systems;
/// @brief System pipeline.
Pipeline _pipeline;

/// @internal
/// @brief Erase all the @p entity components.
Expand Down
2 changes: 2 additions & 0 deletions src/ecstasy/system/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ set(SRCROOT ${SRCROOT}/system)

set(SRC
${SRC}
${INCROOT}/Pipeline.hpp
${SRCROOT}/Pipeline.cpp
${INCROOT}/ISystem.hpp
PARENT_SCOPE
)
Loading

0 comments on commit 74422f5

Please sign in to comment.