diff --git a/doc/Tutorial.md b/doc/Tutorial.md index a48d4b123..1c6efc77a 100644 --- a/doc/Tutorial.md +++ b/doc/Tutorial.md @@ -433,61 +433,44 @@ registry.runSystem(); 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(); - registry.addSystem(); - registry.addSystem(); - registry.addSystem(); - registry.addSystem(); - registry.addSystem(); - registry.runSystems(); // ABCDEF - // If you want you can still call systems one by one - registry.runSystem(); // 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(); - registry.addSystem(); - registry.addSystem(); - // Group 'def' - registry.addSystem(); - registry.addSystem(); - registry.addSystem(); - - // 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(); + // Explicit OnUpdate, require to be casted as size_t if template parameter + registry.addSystem(Pipeline::PedefinedPhases::OnUpdate)>(); + // Explicit OnLoad but using enum value directly + registry.addSystemInPhase(Pipeline::PedefinedPhases::OnLoad); + registry.addSystem(); + registry.addSystem(); + registry.addSystem(); + + + // 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 + // Or even phase by phase + registry.runSystemsPhase(); +``` ## Using resources diff --git a/src/ecstasy/registry/Registry.cpp b/src/ecstasy/registry/Registry.cpp index f22dbd784..072f5a8c6 100644 --- a/src/ecstasy/registry/Registry.cpp +++ b/src/ecstasy/registry/Registry.cpp @@ -24,7 +24,7 @@ namespace ecstasy return _builder.build(); } - Registry::Registry(bool addEntities) + Registry::Registry(bool addEntities) : _pipeline(*this) { if (addEntities) addResource(); @@ -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() diff --git a/src/ecstasy/registry/Registry.hpp b/src/ecstasy/registry/Registry.hpp index e76311819..2cd838262 100644 --- a/src/ecstasy/registry/Registry.hpp +++ b/src/ecstasy/registry/Registry.hpp @@ -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" @@ -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 @@ -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. @@ -828,10 +833,36 @@ namespace ecstasy /// @author Andréas Leroux (andreas.leroux@epitech.eu) /// @since 1.0.0 (2022-10-17) /// - template S, size_t Priority = 0, typename... Args> + template S, + Pipeline::PhaseId Phase = static_cast(Pipeline::PredefinedPhases::OnUpdate), + typename... Args> S &addSystem(Args &&...args) { - return _systems.emplace(std::forward(args)...); + return addSystemInPhase(Phase, std::forward(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 (andreas.leroux@epitech.eu) + /// @since 1.0.0 (2024-11-21) + /// + template S, util::meta::is_size_t_convertible T, typename... Args> + S &addSystemInPhase(T phaseId, Args &&...args) + { + S &system = _systems.emplace(std::forward(args)...); + + _pipeline.addSystem(typeid(S), static_cast(phaseId)); + return system; } /// @@ -1170,11 +1201,21 @@ namespace ecstasy _systems.get().run(*this); } + /// + /// @brief Run a specific system from the registry. + /// + /// @param[in] systemId System type index to run. + /// + /// @author Andréas Leroux (andreas.leroux@epitech.eu) + /// @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 (andreas.leroux@epitech.eu) /// @since 1.0.0 (2022-10-17) @@ -1182,20 +1223,14 @@ namespace ecstasy 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 (andreas.leroux@epitech.eu) - /// @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. @@ -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 (andreas.leroux@epitech.eu) + /// @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 (andreas.leroux@epitech.eu) + /// @since 1.0.0 (2024-11-21) + /// + [[nodiscard]] constexpr const Pipeline &getPipeline() const noexcept + { + return _pipeline; + } + private: /// @brief Registry resources. Instances _resources; /// @brief Registry storages. Instances _storages; /// @brief Registry systems. - SystemInstances _systems; + Instances _systems; + /// @brief System pipeline. + Pipeline _pipeline; /// @internal /// @brief Erase all the @p entity components. diff --git a/src/ecstasy/system/CMakeLists.txt b/src/ecstasy/system/CMakeLists.txt index 7f49f9bf5..bf1f662dc 100644 --- a/src/ecstasy/system/CMakeLists.txt +++ b/src/ecstasy/system/CMakeLists.txt @@ -3,6 +3,8 @@ set(SRCROOT ${SRCROOT}/system) set(SRC ${SRC} + ${INCROOT}/Pipeline.hpp + ${SRCROOT}/Pipeline.cpp ${INCROOT}/ISystem.hpp PARENT_SCOPE ) diff --git a/src/ecstasy/system/Pipeline.cpp b/src/ecstasy/system/Pipeline.cpp new file mode 100644 index 000000000..70180f474 --- /dev/null +++ b/src/ecstasy/system/Pipeline.cpp @@ -0,0 +1,118 @@ +/// +/// @file Pipeline.cpp +/// @author Andréas Leroux (andreas.leroux@epitech.eu) +/// @brief +/// @version 1.0.0 +/// @date 2024-11-21 +/// +/// @copyright Copyright (c) ECSTASY 2022 - 2024 +/// +/// + +#include "Pipeline.hpp" +#include "ecstasy/registry/Registry.hpp" + +namespace ecstasy +{ + + Pipeline::SystemIterator Pipeline::Phase::begin() const noexcept + { + return _pipeline._systemsIds.begin() + static_cast(_begin); + } + + Pipeline::SystemIterator Pipeline::Phase::end() const noexcept + { + return _pipeline._systemsIds.begin() + static_cast(end_idx()); + } + + void Pipeline::Phase::run() const + { + for (auto it = begin(); it < end(); ++it) { + _pipeline._registry.runSystem(*it); + } + } + + Pipeline::Pipeline(Registry ®istry) : _registry(registry) + { + } + + void Pipeline::addSystem(std::type_index system, Pipeline::PhaseId phaseId) + { + Phase &phase = getPhase(phaseId); + + // Phase end is always after the last valid system (like end() in std::vector) + _systemsIds.insert(_systemsIds.begin() + static_cast(phase.end_idx()), system); + ++phase._size; + + /// Need to update all phases iterators + auto phaseIt = _phases.find(phaseId); + size_t nextPhaseBegin = phase.end_idx(); + + for (++phaseIt; phaseIt != _phases.end(); ++phaseIt) { + phaseIt->second._begin = nextPhaseBegin; + nextPhaseBegin = phaseIt->second.end_idx(); + } + } + + void Pipeline::run() const + { + for (auto &phase : _phases) { + phase.second.run(); + } + } + + void Pipeline::run(Pipeline::PhaseId phase) const + { + auto phaseIt = _phases.find(phase); + + if (phaseIt == _phases.end()) + return; + + phaseIt->second.run(); + } + + Pipeline::Phase &Pipeline::getPhase(Pipeline::PhaseId id, bool autoRegister) + { + if (autoRegister) + return registerPhase(id); + auto it = _phases.find(id); + + if (it != _phases.end()) + return it->second; + throw std::out_of_range("Phase not found"); + } + + const Pipeline::Phase &Pipeline::getSystemPhase(std::type_index system) const + { + auto it = std::find(_systemsIds.begin(), _systemsIds.end(), system); + + if (it == _systemsIds.end()) + throw std::out_of_range("System not found in any phase"); + for (auto &phase : _phases) { + if (it >= phase.second.begin() && it < phase.second.end()) + return phase.second; + } + throw std::out_of_range("System not found in any phase"); + } + + Pipeline::Phase &Pipeline::registerPhase(Pipeline::PhaseId id) + { + auto it = _phases.find(id); + + if (it != _phases.end()) + return it->second; + + it = _phases.emplace(id, Phase(*this, id)).first; + Phase &phase = it->second; + + // No phase preceeding this one, set begin to the start of the systems list + if (it == _phases.begin()) { + phase._begin = 0; + } else { + phase._begin = std::prev(it)->second.end_idx(); + } + // No need to update following phases since our phase is empty + return phase; + } + +} // namespace ecstasy diff --git a/src/ecstasy/system/Pipeline.hpp b/src/ecstasy/system/Pipeline.hpp new file mode 100644 index 000000000..f1bbb5010 --- /dev/null +++ b/src/ecstasy/system/Pipeline.hpp @@ -0,0 +1,291 @@ +/// +/// @file Pipeline.hpp +/// @author Andréas Leroux (andreas.leroux@epitech.eu) +/// @brief +/// @version 1.0.0 +/// @date 2024-11-21 +/// +/// @copyright Copyright (c) ECSTASY 2022 - 2024 +/// +/// + +#ifndef ECSTASY_SYSTEM_PIPELINE_HPP_ +#define ECSTASY_SYSTEM_PIPELINE_HPP_ + +#include +#include +#include +#include "ecstasy/system/ISystem.hpp" + +namespace ecstasy +{ + /// @brief Forward declaration of Registry class. + class Registry; + + /// + /// @brief Pipeline of systems to orchestrate the execution of systems. + /// + /// @author Andréas Leroux (andreas.leroux@epitech.eu) + /// @since 1.0.0 (2024-11-21) + /// + class Pipeline { + public: + /// @brief Type of system iterator. + using SystemIterator = std::vector::const_iterator; + /// @brief Type of phase identifiers. + using PhaseId = std::size_t; + + /// + /// @brief Some predefined phases + /// + /// @note Theses phases are inspired from the default ones in + /// [flecs](https://www.flecs.dev/flecs/md_docs_2DesignWithFlecs.html#selecting-a-phase) + /// + /// @note The values are arbitrary and can be changed, they allow to insert custom phases between them. + /// + /// @since 1.0.0 (2024-11-21) + /// + enum class PredefinedPhases : std::size_t { + OnLoad = 100, ///< Load entities into the registry. (Ex: Keyboard inputs) + PostLoad = 200, ///< After loading entities into the registry. (Ex: Convert inputs to game actions) + PreUpdate = 300, ///< Before updating entities. (Ex: Initializing an empty frame/clearing the previous one) + OnUpdate = 400, ///< Update entities, this is the default system. (Ex: Physics simulation) + OnValidate = 500, ///< Validate entities state after the update. (Ex: Collision detection) + PostUpdate = 600, ///< After updating entities. (Ex: Collision resolution) + PreStore = 700, ///< Before storing entities. (Ex: Transform matrices computation) + OnStore = 800, ///< Store entities. (Ex: Rendering) + }; + + /// + /// @brief A phase in the pipeline. + /// + /// @author Andréas Leroux (andreas.leroux@epitech.eu) + /// @since 1.0.0 (2024-11-21) + /// + class Phase { + private: + /// + /// @brief Construct a new empty Phase object. + /// + /// @note This constructor is private, use the Pipeline::getPhase() method to get a phase. + /// + /// @author Andréas Leroux (andreas.leroux@epitech.eu) + /// @since 1.0.0 (2024-11-21) + /// + constexpr Phase(Pipeline &pipeline, PhaseId id) : _pipeline(pipeline), _begin(), _size(0), _id(id) + { + } + + public: + /// + /// @brief Run all systems in this phase. + /// + /// @author Andréas Leroux (andreas.leroux@epitech.eu) + /// @since 1.0.0 (2024-11-21) + /// + void run() const; + + /// + /// @brief Get the number of systems in this phase. + /// + /// @return std::size_t The number of systems in this phase. + /// @author Andréas Leroux (andreas.leroux@epitech.eu) + /// @since 1.0.0 (2024-11-21) + /// + [[nodiscard]] constexpr std::size_t getSize() const noexcept + { + return _size; + } + + /// + /// @brief Get the phase ID. + /// + /// @return constexpr PhaseId The phase identifier. + /// + /// @author Andréas Leroux (andreas.leroux@epitech.eu) + /// @since 1.0.0 (2024-11-21) + /// + [[nodiscard]] constexpr PhaseId getId() const noexcept + { + return _id; + } + + /// + /// @brief Get the iterator to the first system in this phase. + /// + /// @return SystemIterator Iterator to the first system in this phase. + /// + /// @author Andréas Leroux (andreas.leroux@epitech.eu) + /// @since 1.0.0 (2024-11-21) + /// + [[nodiscard]] SystemIterator begin() const noexcept; + + /// + /// @brief Get the iterator past the last system in this phase. + /// + /// @warning This does @b not represent a system in this phase. + /// + /// @return SystemIterator Iterator past the last system in this phase. + /// + /// @author Andréas Leroux (andreas.leroux@epitech.eu) + /// @since 1.0.0 (2024-11-21) + /// + [[nodiscard]] SystemIterator end() const noexcept; + + private: + /// @brief Owning pipeline. + Pipeline &_pipeline; + /// @brief Index of the first system in this phase. + std::size_t _begin; + /// @brief Number of systems in this phase. + std::size_t _size; + /// @brief Identifier of the phase. + PhaseId _id; + + /// + /// @brief Get the index of the first system in this phase. + /// + /// @return constexpr std::size_t The index of the first system in this phase. + /// + /// @author Andréas Leroux (andreas.leroux@epitech.eu) + /// @since 1.0.0 (2024-11-21) + /// + [[nodiscard]] constexpr std::size_t begin_idx() const + { + return _begin; + } + + /// + /// @brief Get the index past the last system in this phase. + /// + /// @warning This index is not valid to access the last system. + /// + /// @return constexpr std::size_t The index past the last system in this phase. + /// + /// @author Andréas Leroux (andreas.leroux@epitech.eu) + /// @since 1.0.0 (2024-11-21) + /// + [[nodiscard]] constexpr std::size_t end_idx() const + { + return _begin + _size; + } + + /// @brief Allow Pipeline to access the private members. + friend Pipeline; + }; + + /// + /// @brief Construct a new Pipeline owned by the given registry. + /// + /// @param[in] registry The registry owning this pipeline. + /// @author Andréas Leroux (andreas.leroux@epitech.eu) + /// @since 1.0.0 (2024-11-21) + /// + Pipeline(Registry ®istry); + + /// + /// @brief Get a Phase instance from its identifier. Create it if it does not exist. + /// + /// @param[in] id Identifier of the phase. + /// @param[in] autoRegister Whether to register the phase if it does not exist. Default is @c true. + /// + /// @return Phase& Reference to the phase. + /// + /// @throw std::out_of_range If the phase does not exist and @p autoRegister is @c false. + /// + /// @author Andréas Leroux (andreas.leroux@epitech.eu) + /// @since 1.0.0 (2024-11-21) + /// + [[nodiscard]] Phase &getPhase(PhaseId id, bool autoRegister = true); + + /// + /// @brief Get the phase containing the given system. + /// + /// @param[in] system Type of the system to find. + /// + /// @return Phase& Reference to the phase containing the system. + /// + /// @throw std::out_of_range If the system is not in any phase. + /// + /// @author Andréas Leroux (andreas.leroux@epitech.eu) + /// @since 1.0.0 (2024-11-21) + /// + [[nodiscard]] const Phase &getSystemPhase(std::type_index system) const; + + /// + /// @brief Get the phase containing the given system. + /// + /// @tparam S Type of the system to find. + /// + /// @return const Phase& Reference to the phase containing the system. + /// + /// @throw std::out_of_range If the system is not in any phase. + /// + /// @author Andréas Leroux (andreas.leroux@epitech.eu) + /// @since 1.0.0 (2024-11-21) + /// + template S> + [[nodiscard]] const Phase &getSystemPhase() const + { + return getSystemPhase(typeid(S)); + } + + /// + /// @brief Register a phase with the given identifier. + /// + /// @note Does nothing if the phase already exists. + /// + /// @param[in] id Identifier of the phase. + /// + /// @return Phase& Reference to the newly created phase. + /// + /// @author Andréas Leroux (andreas.leroux@epitech.eu) + /// @since 1.0.0 (2024-11-21) + /// + Phase ®isterPhase(PhaseId id); + + /// + /// @brief Add a system to the pipeline. + /// + /// @note The systems are owned by the registry, not the pipeline. + /// + /// @param[in] system Type of the system to add. + /// @param[in] phase Phase in which the system should be. Default is @ref Pipeline::PredefinedPhases::OnUpdate + /// "OnUpdate". + /// + /// @author Andréas Leroux (andreas.leroux@epitech.eu) + /// @since 1.0.0 (2024-11-21) + /// + void addSystem(std::type_index system, PhaseId phase = static_cast(PredefinedPhases::OnUpdate)); + + /// + /// @brief Run a frame of the pipeline. + /// + /// @note This will run all phases by ascending order of their identifier. + /// + /// @author Andréas Leroux (andreas.leroux@epitech.eu) + /// @since 1.0.0 (2024-11-21) + /// + void run() const; + + /// + /// @brief Run a specific phase of the pipeline. + /// + /// @param[in] phase Identifier of the phase to run. + /// + /// @author Andréas Leroux (andreas.leroux@epitech.eu) + /// @since 1.0.0 (2024-11-21) + /// + void run(PhaseId phase) const; + + private: + /// @brief Ordered list of systems. + std::vector _systemsIds; + /// @brief Ordered map of phases + std::map _phases; + /// @brief Owning registry. + Registry &_registry; + }; +} // namespace ecstasy + +#endif /* !ECSTASY_SYSTEM_PIPELINE_HPP_ */ diff --git a/src/util/meta/CMakeLists.txt b/src/util/meta/CMakeLists.txt index d2c3b1548..13289a900 100644 --- a/src/util/meta/CMakeLists.txt +++ b/src/util/meta/CMakeLists.txt @@ -9,6 +9,7 @@ set(SRC ${INCROOT}/filter.hpp ${INCROOT}/include.hpp ${INCROOT}/index.hpp + ${INCROOT}/is_size_t_convertible.hpp ${INCROOT}/is_std_vector.hpp ${INCROOT}/is_type_bounded_array.hpp ${INCROOT}/outer_join.hpp diff --git a/src/util/meta/is_size_t_convertible.hpp b/src/util/meta/is_size_t_convertible.hpp new file mode 100644 index 000000000..6644d0b3d --- /dev/null +++ b/src/util/meta/is_size_t_convertible.hpp @@ -0,0 +1,35 @@ +/// +/// @file is_size_t_convertible.hpp +/// @author Andréas Leroux (andreas.leroux@epitech.eu) +/// @brief Check if a type is a std::size_t or convertible to it. +/// @version 1.0.0 +/// @date 2024-11-21 +/// +/// @copyright Copyright (c) ECSTASY 2022 - 2024 +/// +/// + +#ifndef UTIL_META_IS_STD_SIZE_T_CONVERTIBLE_HPP_ +#define UTIL_META_IS_STD_SIZE_T_CONVERTIBLE_HPP_ + +#include +#include + +namespace util::meta +{ + + /// + /// @brief Check if a type is std::size_t or is convertible to it. + /// + /// @tparam T Type to check. + /// + /// @author Andréas Leroux (andreas.leroux@epitech.eu) + /// @since 1.0.0 (2024-11-21) + /// + template + concept is_size_t_convertible = + std::same_as || requires(T value) { static_cast(value); }; + +} // namespace util::meta + +#endif /* !UTIL_META_IS_STD_SIZE_T_CONVERTIBLE_HPP_ */ diff --git a/tests/registry/tests_Registry.cpp b/tests/registry/tests_Registry.cpp index ce77c6239..2f8fb9a6e 100644 --- a/tests/registry/tests_Registry.cpp +++ b/tests/registry/tests_Registry.cpp @@ -903,23 +903,46 @@ PRINT(D); PRINT(E); PRINT(F); -TEST(Registry, SystemPriorities_Letters_Ascending) +TEST(Registry, Pipeline_Implicit_Order) +{ + ecstasy::Registry registry; + testing::internal::CaptureStdout(); + + registry.addSystem(); + registry.addSystem(); + registry.addSystem(); + registry.addSystem(); + registry.addSystem(); + registry.addSystem(); + + const ecstasy::Pipeline &pipeline = registry.getPipeline(); + + GTEST_ASSERT_EQ( + pipeline.getSystemPhase().getId(), static_cast(ecstasy::Pipeline::PredefinedPhases::OnUpdate)); + + registry.runSystems(); + GTEST_ASSERT_EQ(testing::internal::GetCapturedStdout(), "ABCDEF"); +} + +TEST(Registry, Pipeline_Explicit_Order) { ecstasy::Registry registry; testing::internal::CaptureStdout(); registry.addSystem(); - registry.addSystem(); - registry.addSystem(); registry.addSystem(); + registry.addSystem(); registry.addSystem(); + registry.addSystem(); registry.addSystem(); + GTEST_ASSERT_EQ(registry.getPipeline().getSystemPhase().getId(), 4); + registry.runSystems(); GTEST_ASSERT_EQ(testing::internal::GetCapturedStdout(), "ABCDEF"); } -TEST(Registry, SystemPriorities_Letters_Descending) +TEST(Registry, Pipeline_Explicit_Descending) { ecstasy::Registry registry; testing::internal::CaptureStdout(); @@ -935,30 +958,29 @@ TEST(Registry, SystemPriorities_Letters_Descending) GTEST_ASSERT_EQ(testing::internal::GetCapturedStdout(), "FEDCBA"); } -TEST(Registry, SystemPriorities_Group) +TEST(Registry, Pipeline_Custom_Phases) { - size_t mask = 0xff00; - const size_t abc = 0x0100; - const size_t def = 0x0200; + const size_t abc = 1; + const size_t def = 2; ecstasy::Registry registry; testing::internal::CaptureStdout(); - registry.addSystem(); - registry.addSystem(); - registry.addSystem(); - registry.addSystem(); - registry.addSystem(); - registry.addSystem(); + registry.addSystem(); + registry.addSystem(); + registry.addSystemInPhase(def); + registry.addSystemInPhase(abc); + registry.addSystem(); + registry.addSystem(); registry.runSystems(); EXPECT_EQ(testing::internal::GetCapturedStdout(), "ABCDEF"); testing::internal::CaptureStdout(); - registry.runSystems(abc, mask); + registry.runSystemsPhase(abc); EXPECT_EQ(testing::internal::GetCapturedStdout(), "ABC"); testing::internal::CaptureStdout(); - registry.runSystems(def, mask); + registry.runSystemsPhase(def); EXPECT_EQ(testing::internal::GetCapturedStdout(), "DEF"); } diff --git a/tests/util/meta/CMakeLists.txt b/tests/util/meta/CMakeLists.txt index b702914ff..b52953435 100644 --- a/tests/util/meta/CMakeLists.txt +++ b/tests/util/meta/CMakeLists.txt @@ -6,6 +6,7 @@ set(SRC ${SRCROOT}/tests_apply.cpp ${SRCROOT}/tests_contains.cpp ${SRCROOT}/tests_filter.cpp + ${SRCROOT}/tests_is_size_t_convertible.cpp ${SRCROOT}/tests_is_type_bounded_array.cpp ${SRCROOT}/tests_outer_join.cpp ${SRCROOT}/tests_type_set_eq.cpp diff --git a/tests/util/meta/tests_is_size_t_convertible.cpp b/tests/util/meta/tests_is_size_t_convertible.cpp new file mode 100644 index 000000000..203713b87 --- /dev/null +++ b/tests/util/meta/tests_is_size_t_convertible.cpp @@ -0,0 +1,12 @@ +#include +#include "util/meta/is_size_t_convertible.hpp" + +enum class MyEnum : std::size_t { A = 42, B = 100 }; +enum class AnotherEnum { A = 42, B = 100 }; + +TEST(is_size_t_convertible, all) +{ + static_assert(util::meta::is_size_t_convertible); + static_assert(util::meta::is_size_t_convertible); + static_assert(util::meta::is_size_t_convertible); +}