From 0a75bce99d9cae5a04ca5a53ef769106f2648ff5 Mon Sep 17 00:00:00 2001 From: Santiago Castro Date: Tue, 18 Apr 2017 00:36:28 -0300 Subject: [PATCH 01/33] Fix broken Markdown headings (#124) --- readme_fr.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme_fr.md b/readme_fr.md index 808788b0d..22215c42c 100644 --- a/readme_fr.md +++ b/readme_fr.md @@ -43,7 +43,7 @@ Vous pouvez lire des tutoriaux sur l'installation, la compilation et l'utilisati [Wiki](https://github.com/DigitalPulseSoftware/NazaraEngine/wiki) [Forum](https://forum.digitalpulsesoftware.net) -###Remerciements: +### Remerciements: - **RafBill** et **Raakz:** Recherche de bugs et/ou tests - **Fissal "DrFisher" Hannoun**: Aide et conseils lors de la conception de l'architecture du moteur From eb70453574e87eeae2e57b648830711e3ea1e647 Mon Sep 17 00:00:00 2001 From: Gawaboumga Date: Thu, 20 Apr 2017 12:30:43 +0200 Subject: [PATCH 02/33] State machine as a stack (#123) * State machine as a stack * Fix comments linked to code review * I will think to compile, next time --- SDK/include/NDK/StateMachine.hpp | 14 +++- SDK/include/NDK/StateMachine.inl | 137 +++++++++++++++++++++++++------ tests/SDK/NDK/StateMachine.cpp | 64 ++++++++++++++- 3 files changed, 186 insertions(+), 29 deletions(-) diff --git a/SDK/include/NDK/StateMachine.hpp b/SDK/include/NDK/StateMachine.hpp index dae81ecd0..e9e665c44 100644 --- a/SDK/include/NDK/StateMachine.hpp +++ b/SDK/include/NDK/StateMachine.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2017 Jérôme Leclercq +// Copyright (C) 2017 Jérôme Leclercq // This file is part of the "Nazara Development Kit" // For conditions of distribution and use, see copyright notice in Prerequesites.hpp @@ -10,6 +10,7 @@ #include #include #include +#include namespace Ndk { @@ -25,14 +26,21 @@ namespace Ndk inline const std::shared_ptr& GetCurrentState() const; + inline bool IsTopState(const State* state) const; + + inline std::shared_ptr PopState(); + inline bool PopStatesUntil(std::shared_ptr state); + inline void PushState(std::shared_ptr state); + + inline void SetState(std::shared_ptr state); + inline bool Update(float elapsedTime); inline StateMachine& operator=(StateMachine&& fsm) = default; StateMachine& operator=(const StateMachine&) = delete; private: - std::shared_ptr m_currentState; - std::shared_ptr m_nextState; + std::vector> m_states; }; } diff --git a/SDK/include/NDK/StateMachine.inl b/SDK/include/NDK/StateMachine.inl index d30146a97..1086df54d 100644 --- a/SDK/include/NDK/StateMachine.inl +++ b/SDK/include/NDK/StateMachine.inl @@ -3,7 +3,6 @@ // For conditions of distribution and use, see copyright notice in Prerequesites.hpp #include -#include #include namespace Ndk @@ -11,7 +10,7 @@ namespace Ndk /*! * \ingroup NDK * \class Ndk::StateMachine - * \brief NDK class that represents a state machine, to represent the multiple states of your program + * \brief NDK class that represents a state machine, to represent the multiple states of your program as a stack */ /*! @@ -23,61 +22,153 @@ namespace Ndk * \remark Produces a NazaraAssert if nullptr is given */ - inline StateMachine::StateMachine(std::shared_ptr originalState) : - m_currentState(std::move(originalState)) + inline StateMachine::StateMachine(std::shared_ptr originalState) { - NazaraAssert(m_currentState, "StateMachine must have a state to begin with"); - m_currentState->Enter(*this); + NazaraAssert(originalState, "StateMachine must have a state to begin with"); + PushState(std::move(originalState)); } /*! * \brief Destructs the object * - * \remark Calls "Leave" on the state + * \remark Calls "Leave" on all the states */ inline StateMachine::~StateMachine() { - m_currentState->Leave(*this); + for (std::shared_ptr& state : m_states) + state->Leave(*this); } /*! - * \brief Changes the current state of the machine + * \brief Replaces the current state on the top of the machine * - * \param state Next state to represent + * \param state State to replace the top one if it is nullptr, no action is performed */ inline void StateMachine::ChangeState(std::shared_ptr state) { - m_nextState = std::move(state); + if (state) + { + PopState(); + PushState(std::move(state)); + } } /*! - * \brief Gets the current state of the machine + * \brief Gets the current state on the top of the machine * \return A constant reference to the state + * + * \remark The stack is supposed to be non empty, otherwise it is undefined behaviour + * + * \see PopStatesUntil */ inline const std::shared_ptr& StateMachine::GetCurrentState() const { - return m_currentState; + return m_states.back(); } /*! - * \brief Updates the state - * \return True if update is successful + * \brief Checks whether the state is on the top of the machine + * \return true If it is the case + * + * \param state State to compare the top with + */ + + inline bool StateMachine::IsTopState(const State* state) const + { + if (m_states.empty()) + return false; + + return m_states.back().get() == state; + } + + /*! + * \brief Pops the state on the top of the machine + * \return Old state on the top, nullptr if stack was empty + * + * \remark This method can completely empty the stack + */ + + inline std::shared_ptr StateMachine::PopState() + { + if (m_states.empty()) + return nullptr; + + m_states.back()->Leave(*this); + std::shared_ptr oldTopState = std::move(m_states.back()); + m_states.pop_back(); + return oldTopState; + } + + /*! + * \brief Pops all the states of the machine until a specific one is reached + * \return true If that specific state is on top, false if stack is empty + * + * \param state State to find on the stack if it is nullptr, no action is performed + * + * \remark This method can completely empty the stack + */ + + inline bool StateMachine::PopStatesUntil(std::shared_ptr state) + { + if (!state) + return false; + + while (!m_states.empty() && !IsTopState(state.get())) + PopState(); + + return !m_states.empty(); + } + + /*! + * \brief Pushes a new state on the top of the machine + * + * \param state Next state to represent if it is nullptr, it performs no action + * + * \remark Produces a NazaraAssert if the same state is pushed two times on the stack + */ + + inline void StateMachine::PushState(std::shared_ptr state) + { + if (state) + { + NazaraAssert(std::find(m_states.begin(), m_states.end(), state) == m_states.end(), "The same state was pushed two times"); + + m_states.push_back(std::move(state)); + m_states.back()->Enter(*this); + } + } + + /*! + * \brief Pops every states of the machine to put a new one + * + * \param state State to reset the stack with if it is nullptr, no action is performed + */ + + inline void StateMachine::SetState(std::shared_ptr state) + { + if (state) + { + while (!m_states.empty()) + PopState(); + + PushState(std::move(state)); + } + } + + /*! + * \brief Updates all the states + * \return true If update is successful for everyone of them * * \param elapsedTime Delta time used for the update */ inline bool StateMachine::Update(float elapsedTime) { - if (m_nextState) - { - m_currentState->Leave(*this); - m_currentState = std::move(m_nextState); - m_currentState->Enter(*this); - } - - return m_currentState->Update(*this, elapsedTime); + return std::all_of(m_states.begin(), m_states.end(), [=](std::shared_ptr& state) { + return state->Update(*this, elapsedTime); + }); } } diff --git a/tests/SDK/NDK/StateMachine.cpp b/tests/SDK/NDK/StateMachine.cpp index 1c14b1e76..066a150d7 100644 --- a/tests/SDK/NDK/StateMachine.cpp +++ b/tests/SDK/NDK/StateMachine.cpp @@ -28,21 +28,79 @@ class TestState : public Ndk::State bool m_isUpdated; }; +class SecondTestState : public Ndk::State +{ + public: + void Enter(Ndk::StateMachine& /*fsm*/) override + { + m_isUpdated = false; + } + + bool IsUpdated() const + { + return m_isUpdated; + } + + void Leave(Ndk::StateMachine& /*fsm*/) override + { + } + + bool Update(Ndk::StateMachine& fsm, float /*elapsedTime*/) override + { + if (fsm.IsTopState(this)) + m_isUpdated = true; + return true; + } + + private: + bool m_isUpdated; +}; + SCENARIO("State & StateMachine", "[NDK][STATE]") { - GIVEN("A statemachine with our TestState") + GIVEN("A statemachine with our test states") { std::shared_ptr testState = std::make_shared(); - Ndk::StateMachine stateMachine(testState); + std::shared_ptr secondTestState = std::make_shared(); + Ndk::StateMachine stateMachine(secondTestState); + stateMachine.PushState(testState); REQUIRE(!testState->IsUpdated()); + REQUIRE(!secondTestState->IsUpdated()); WHEN("We update our machine") { stateMachine.Update(1.f); - THEN("Our state has been updated") + THEN("Our state on the top has been updated but not the bottom one") { + REQUIRE(stateMachine.IsTopState(testState.get())); + REQUIRE(!stateMachine.IsTopState(secondTestState.get())); + REQUIRE(testState->IsUpdated()); + REQUIRE(!secondTestState->IsUpdated()); + } + } + + WHEN("We exchange the states' positions while emptying the stack") + { + REQUIRE(stateMachine.PopStatesUntil(secondTestState)); + REQUIRE(stateMachine.IsTopState(secondTestState.get())); + + std::shared_ptr oldState = stateMachine.PopState(); + REQUIRE(stateMachine.PopState() == nullptr); + + stateMachine.SetState(testState); + stateMachine.PushState(oldState); + + stateMachine.Update(1.f); + + THEN("Both states should be updated") + { + REQUIRE(!stateMachine.IsTopState(testState.get())); + REQUIRE(stateMachine.IsTopState(secondTestState.get())); + + REQUIRE(testState->IsUpdated()); + REQUIRE(secondTestState->IsUpdated()); } } } From 48b348135ee7a93ebcaebf15aee47e69b76de183 Mon Sep 17 00:00:00 2001 From: Lynix Date: Thu, 20 Apr 2017 23:42:45 +0200 Subject: [PATCH 03/33] Refactor EntityList and prevent World to invalidate its own handles between updates --- SDK/include/NDK/EntityList.cpp | 0 SDK/include/NDK/EntityList.hpp | 62 +++++---- SDK/include/NDK/EntityList.inl | 163 +++++++++++++---------- SDK/include/NDK/Systems/RenderSystem.hpp | 2 +- SDK/include/NDK/World.hpp | 16 ++- SDK/include/NDK/World.inl | 11 +- SDK/src/NDK/EntityList.cpp | 14 ++ SDK/src/NDK/Systems/RenderSystem.cpp | 25 +++- SDK/src/NDK/World.cpp | 82 +++++++----- 9 files changed, 231 insertions(+), 144 deletions(-) create mode 100644 SDK/include/NDK/EntityList.cpp create mode 100644 SDK/src/NDK/EntityList.cpp diff --git a/SDK/include/NDK/EntityList.cpp b/SDK/include/NDK/EntityList.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/SDK/include/NDK/EntityList.hpp b/SDK/include/NDK/EntityList.hpp index 4047388cc..22a632e65 100644 --- a/SDK/include/NDK/EntityList.hpp +++ b/SDK/include/NDK/EntityList.hpp @@ -16,45 +16,59 @@ namespace Ndk class NDK_API EntityList { public: - using Container = std::vector; + class iterator; + friend iterator; + using size_type = std::size_t; - EntityList() = default; + inline EntityList(); ~EntityList() = default; inline void Clear(); - inline bool Has(const Entity* entity); - inline bool Has(EntityId entity); + inline bool Has(const Entity* entity) const; + inline bool Has(EntityId entity) const; inline void Insert(Entity* entity); inline void Remove(Entity* entity); // STL API - inline Container::iterator begin(); - inline Container::const_iterator begin() const; - - inline Container::const_iterator cbegin() const; - inline Container::const_iterator cend() const; - inline Container::const_reverse_iterator crbegin() const; - inline Container::const_reverse_iterator crend() const; - + inline iterator begin(); inline bool empty() const; - - inline Container::iterator end(); - inline Container::const_iterator end() const; - - inline Container::reverse_iterator rbegin(); - inline Container::const_reverse_iterator rbegin() const; - - inline Container::reverse_iterator rend(); - inline Container::const_reverse_iterator rend() const; - - inline Container::size_type size() const; + inline iterator end(); + inline size_type size() const; private: - std::vector m_entities; + inline std::size_t FindNext(std::size_t currentId) const; + inline World* GetWorld() const; + Nz::Bitset m_entityBits; + World* m_world; + }; + + class EntityList::iterator : public std::iterator + { + friend EntityList; + + public: + inline iterator(const iterator& iterator); + + const EntityHandle& operator*() const; + + inline iterator& operator=(const iterator& iterator); + inline iterator& operator++(); + inline iterator operator++(int); + + friend inline bool operator==(const iterator& lhs, const iterator& rhs); + friend inline bool operator!=(const iterator& lhs, const iterator& rhs); + + friend inline void swap(iterator& lhs, iterator& rhs); + + private: + inline iterator(const EntityList* world, std::size_t nextId); + + std::size_t m_nextEntityId; + const EntityList* m_list; }; } diff --git a/SDK/include/NDK/EntityList.inl b/SDK/include/NDK/EntityList.inl index 126af0a7e..6d9447a7f 100644 --- a/SDK/include/NDK/EntityList.inl +++ b/SDK/include/NDK/EntityList.inl @@ -2,8 +2,10 @@ // This file is part of the "Nazara Development Kit" // For conditions of distribution and use, see copyright notice in Prerequesites.hpp +#include #include #include +#include "EntityList.hpp" namespace Ndk { @@ -16,22 +18,34 @@ namespace Ndk /*! * \brief Clears the set from every entities */ - - inline void EntityList::Clear() + inline EntityList::EntityList() : + m_world(nullptr) { - m_entities.clear(); - m_entityBits.Clear(); } /*! - * \brief Checks whether or not the set contains the entity + * \brief Clears the set from every entities + * + * \remark This resets the implicit world member, allowing you to insert entities from a different world than previously + */ + inline void EntityList::Clear() + { + m_entityBits.Clear(); + m_world = nullptr; + } + + /*! + * \brief Checks whether or not the EntityList contains the entity * \return true If it is the case * * \param entity Pointer to the entity + * + * \remark If the Insert function was called since the EntityList construction (or last call to Clear), the entity passed by parameter must belong to the same world as the previously inserted entities. */ - - inline bool EntityList::Has(const Entity* entity) + inline bool EntityList::Has(const Entity* entity) const { + NazaraAssert(!m_world || !entity || entity->GetWorld() == m_world, "Incompatible world"); + return entity && entity->IsValid() && Has(entity->GetId()); } @@ -41,8 +55,7 @@ namespace Ndk * * \param id Identifier of the entity */ - - inline bool EntityList::Has(EntityId entity) + inline bool EntityList::Has(EntityId entity) const { return m_entityBits.UnboundedTest(entity); } @@ -50,18 +63,18 @@ namespace Ndk /*! * \brief Inserts the entity into the set * - * \param entity Pointer to the entity + * \param entity Valid pointer to an entity * * \remark If entity is already contained, no action is performed + * \remark If any entity has been inserted since construction (or last Clear call), the entity must belong to the same world as the previously inserted entities */ - inline void EntityList::Insert(Entity* entity) { - if (!Has(entity)) - { - m_entities.emplace_back(entity); - m_entityBits.UnboundedSet(entity->GetId(), true); - } + NazaraAssert(entity, "Invalid entity"); + NazaraAssert(!m_world || entity->GetWorld() == m_world, "Incompatible world"); + + m_entityBits.UnboundedSet(entity->GetId(), true); + m_world = entity->GetWorld(); } /*! @@ -70,89 +83,101 @@ namespace Ndk * \param entity Pointer to the entity * * \remark If entity is not contained, no action is performed + * \remark This function never resets the implicit world member, even if it empties the list. Use the Clear method if you want to reset it. + * + * \see Clear */ - inline void EntityList::Remove(Entity* entity) { - if (Has(entity)) - { - auto it = std::find(m_entities.begin(), m_entities.end(), *entity); - NazaraAssert(it != m_entities.end(), "Entity should be part of the vector"); - - std::swap(*it, m_entities.back()); - m_entities.pop_back(); // We get it out of the vector - m_entityBits.UnboundedSet(entity->GetId(), false); - } + m_entityBits.UnboundedSet(entity->GetId(), false); } - // Nz::Interface STD - inline EntityList::Container::iterator EntityList::begin() + // STL Interface + inline EntityList::iterator EntityList::begin() { - return m_entities.begin(); - } - - inline EntityList::Container::const_iterator EntityList::begin() const - { - return m_entities.begin(); - } - - inline EntityList::Container::const_iterator EntityList::cbegin() const - { - return m_entities.cbegin(); - } - - inline EntityList::Container::const_iterator EntityList::cend() const - { - return m_entities.cend(); - } - - inline EntityList::Container::const_reverse_iterator EntityList::crbegin() const - { - return m_entities.crbegin(); - } - - inline EntityList::Container::const_reverse_iterator EntityList::crend() const - { - return m_entities.crend(); + return EntityList::iterator(this, m_entityBits.FindFirst()); } inline bool EntityList::empty() const { - return m_entities.empty(); + return m_entityBits.TestAny(); } - inline EntityList::Container::iterator EntityList::end() + inline EntityList::iterator EntityList::end() { - return m_entities.end(); + return EntityList::iterator(this, m_entityBits.npos); } - inline EntityList::Container::const_iterator EntityList::end() const + inline EntityList::size_type EntityList::size() const { - return m_entities.end(); + return m_entityBits.Count(); } - inline EntityList::Container::reverse_iterator EntityList::rbegin() + inline std::size_t EntityList::FindNext(std::size_t currentId) const { - return m_entities.rbegin(); + return m_entityBits.FindNext(currentId); } - inline EntityList::Container::const_reverse_iterator EntityList::rbegin() const + inline World* EntityList::GetWorld() const { - return m_entities.rbegin(); + return m_world; } - inline EntityList::Container::reverse_iterator EntityList::rend() + + inline EntityList::iterator::iterator(const EntityList* list, std::size_t nextId) : + m_nextEntityId(nextId), + m_list(list) { - return m_entities.rend(); } - inline EntityList::Container::const_reverse_iterator EntityList::rend() const + inline EntityList::iterator::iterator(const iterator& iterator) : + m_nextEntityId(iterator.m_nextEntityId), + m_list(iterator.m_list) { - return m_entities.rend(); } - inline EntityList::Container::size_type EntityList::size() const + inline EntityList::iterator& EntityList::iterator::operator=(const iterator& iterator) { - return m_entities.size(); + m_nextEntityId = iterator.m_nextEntityId; + m_list = iterator.m_list; + + return *this; + } + + inline EntityList::iterator& EntityList::iterator::operator++() + { + m_nextEntityId = m_list->FindNext(m_nextEntityId); + + return *this; + } + + inline EntityList::iterator EntityList::iterator::operator++(int) + { + std::size_t previousId = m_nextEntityId; + + m_nextEntityId = m_list->FindNext(m_nextEntityId); + + return iterator(m_list, previousId); + } + + inline bool operator==(const EntityList::iterator& lhs, const EntityList::iterator& rhs) + { + NazaraAssert(lhs.m_list == rhs.m_list, "Cannot compare iterator coming from different lists"); + + return lhs.m_nextEntityId == rhs.m_nextEntityId; + } + + inline bool operator!=(const EntityList::iterator& lhs, const EntityList::iterator& rhs) + { + return !operator==(lhs, rhs); + } + + inline void swap(EntityList::iterator& lhs, EntityList::iterator& rhs) + { + NazaraAssert(lhs.m_list == rhs.m_list, "Cannot compare iterator coming from different lists"); + + using std::swap; + + std::swap(lhs.m_nextEntityId, rhs.m_nextEntityId); } } diff --git a/SDK/include/NDK/Systems/RenderSystem.hpp b/SDK/include/NDK/Systems/RenderSystem.hpp index 61f7bd0a0..a74cb9d0e 100644 --- a/SDK/include/NDK/Systems/RenderSystem.hpp +++ b/SDK/include/NDK/Systems/RenderSystem.hpp @@ -57,7 +57,7 @@ namespace Ndk std::unique_ptr m_renderTechnique; std::vector m_volumeEntries; - EntityList m_cameras; + std::vector m_cameras; EntityList m_drawables; EntityList m_directionalLights; EntityList m_lights; diff --git a/SDK/include/NDK/World.hpp b/SDK/include/NDK/World.hpp index 9b2546eda..b1f1abde1 100644 --- a/SDK/include/NDK/World.hpp +++ b/SDK/include/NDK/World.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -28,7 +29,7 @@ namespace Ndk friend Entity; public: - using EntityList = std::vector; + using EntityVector = std::vector; inline World(bool addDefaultSystems = true); World(const World&) = delete; @@ -41,13 +42,13 @@ namespace Ndk template SystemType& AddSystem(Args&&... args); const EntityHandle& CreateEntity(); - inline EntityList CreateEntities(unsigned int count); + inline EntityVector CreateEntities(unsigned int count); void Clear() noexcept; const EntityHandle& CloneEntity(EntityId id); const EntityHandle& GetEntity(EntityId id); - inline const EntityList& GetEntities(); + inline const EntityList& GetEntities() const; inline BaseSystem& GetSystem(SystemIndex index); template SystemType& GetSystem(); @@ -55,7 +56,7 @@ namespace Ndk template bool HasSystem() const; void KillEntity(Entity* entity); - inline void KillEntities(const EntityList& list); + inline void KillEntities(const EntityVector& list); inline bool IsEntityValid(const Entity* entity) const; inline bool IsEntityIdValid(EntityId id) const; @@ -79,19 +80,22 @@ namespace Ndk struct EntityBlock { EntityBlock(Entity&& e) : - entity(std::move(e)) + entity(std::move(e)), + handle(&entity) { } EntityBlock(EntityBlock&& block) = default; Entity entity; - std::size_t aliveIndex; + EntityHandle handle; }; std::vector> m_systems; std::vector m_orderedSystems; std::vector m_entities; + std::vector m_entityBlocks; + std::vector> m_waitingEntities; std::vector m_freeIdList; EntityList m_aliveEntities; Nz::Bitset m_dirtyEntities; diff --git a/SDK/include/NDK/World.inl b/SDK/include/NDK/World.inl index dcdb0c057..45d358ad4 100644 --- a/SDK/include/NDK/World.inl +++ b/SDK/include/NDK/World.inl @@ -82,9 +82,9 @@ namespace Ndk * \param count Number of entities to create */ - inline World::EntityList World::CreateEntities(unsigned int count) + inline World::EntityVector World::CreateEntities(unsigned int count) { - EntityList list; + EntityVector list; list.reserve(count); for (unsigned int i = 0; i < count; ++i) @@ -98,7 +98,7 @@ namespace Ndk * \return A constant reference to the entities */ - inline const World::EntityList& World::GetEntities() + inline const EntityList& World::GetEntities() const { return m_aliveEntities; } @@ -170,7 +170,7 @@ namespace Ndk * \param list Set of entities to kill */ - inline void World::KillEntities(const EntityList& list) + inline void World::KillEntities(const EntityVector& list) { for (const EntityHandle& entity : list) KillEntity(entity); @@ -197,7 +197,7 @@ namespace Ndk inline bool World::IsEntityIdValid(EntityId id) const { - return id < m_entities.size() && m_entities[id].entity.IsValid(); + return id < m_entityBlocks.size() && m_entityBlocks[id]->entity.IsValid(); } /*! @@ -270,6 +270,7 @@ namespace Ndk m_killedEntities = std::move(world.m_killedEntities); m_orderedSystems = std::move(world.m_orderedSystems); m_orderedSystemsUpdated = world.m_orderedSystemsUpdated; + m_waitingEntities = std::move(world.m_waitingEntities); m_entities = std::move(world.m_entities); for (EntityBlock& block : m_entities) diff --git a/SDK/src/NDK/EntityList.cpp b/SDK/src/NDK/EntityList.cpp new file mode 100644 index 000000000..03fd3afc3 --- /dev/null +++ b/SDK/src/NDK/EntityList.cpp @@ -0,0 +1,14 @@ +// Copyright (C) 2017 Jérôme Leclercq +// This file is part of the "Nazara Development Kit" +// For conditions of distribution and use, see copyright notice in Prerequesites.hpp + +#include +#include + +namespace Ndk +{ + const EntityHandle& EntityList::iterator::operator*() const + { + return m_list->GetWorld()->GetEntity(static_cast(m_nextEntityId)); + } +} diff --git a/SDK/src/NDK/Systems/RenderSystem.cpp b/SDK/src/NDK/Systems/RenderSystem.cpp index f586056a2..44b0d1bba 100644 --- a/SDK/src/NDK/Systems/RenderSystem.cpp +++ b/SDK/src/NDK/Systems/RenderSystem.cpp @@ -48,7 +48,15 @@ namespace Ndk { m_forceRenderQueueInvalidation = true; //< Hackfix until lights and particles are handled by culling list - m_cameras.Remove(entity); + for (auto it = m_cameras.begin(); it != m_cameras.end(); ++it) + { + if (it->GetObject() == entity) + { + m_cameras.erase(it); + break; + } + } + m_directionalLights.Remove(entity); m_drawables.Remove(entity); m_lights.Remove(entity); @@ -74,14 +82,23 @@ namespace Ndk if (entity->HasComponent() && entity->HasComponent()) { - m_cameras.Insert(entity); + m_cameras.emplace_back(entity); std::sort(m_cameras.begin(), m_cameras.end(), [](const EntityHandle& handle1, const EntityHandle& handle2) { return handle1->GetComponent().GetLayer() < handle2->GetComponent().GetLayer(); }); } else - m_cameras.Remove(entity); + { + for (auto it = m_cameras.begin(); it != m_cameras.end(); ++it) + { + if (it->GetObject() == entity) + { + m_cameras.erase(it); + break; + } + } + } if (entity->HasComponent() && entity->HasComponent()) { @@ -181,7 +198,7 @@ namespace Ndk GraphicsComponent& graphicsComponent = drawable->GetComponent(); graphicsComponent.EnsureBoundingVolumeUpdate(); } - + bool forceInvalidation = false; std::size_t visibilityHash = m_drawableCulling.Cull(camComponent.GetFrustum(), &forceInvalidation); diff --git a/SDK/src/NDK/World.cpp b/SDK/src/NDK/World.cpp index cad4660d4..db3cef529 100644 --- a/SDK/src/NDK/World.cpp +++ b/SDK/src/NDK/World.cpp @@ -60,29 +60,47 @@ namespace Ndk const EntityHandle& World::CreateEntity() { EntityId id; + EntityBlock* entBlock; if (!m_freeIdList.empty()) { // We get an identifier id = m_freeIdList.back(); m_freeIdList.pop_back(); + + entBlock = &m_entities[id]; + m_entityBlocks[id] = entBlock; } else { // We allocate a new entity id = static_cast(m_entities.size()); - // We can't use emplace_back due to the scope - m_entities.push_back(Entity(this, id)); + if (m_entities.capacity() > m_entities.size()) + { + m_entities.push_back(Entity(this, id)); //< We can't use emplace_back due to the scope + entBlock = &m_entities.back(); + } + else + { + // Pushing to entities would reallocate vector and thus, invalidate EntityHandles (which we don't want until world update) + // To prevent this, allocate them into a separate vector and move them at update + // For now, we are counting on m_entities grow strategy to prevent + m_waitingEntities.emplace_back(std::make_unique(Entity(this, id))); + entBlock = m_waitingEntities.back().get(); + } + + if (id >= m_entityBlocks.size()) + m_entityBlocks.resize(id + 1); + + m_entityBlocks[id] = entBlock; } - // We initialise the entity and we add it to the list of alive entities - Entity& entity = m_entities[id].entity; - entity.Create(); + // We initialize the entity and we add it to the list of alive entities + entBlock->entity.Create(); - m_aliveEntities.emplace_back(&entity); - m_entities[id].aliveIndex = m_aliveEntities.size() - 1; + m_aliveEntities.Insert(&entBlock->entity); - return m_aliveEntities.back(); + return entBlock->handle; } /*! @@ -97,7 +115,7 @@ namespace Ndk // This is made to avoid that handle warn uselessly entities before their destruction m_entities.clear(); - m_aliveEntities.clear(); + m_aliveEntities.Clear(); m_dirtyEntities.Clear(); m_killedEntities.Clear(); } @@ -158,7 +176,7 @@ namespace Ndk const EntityHandle& World::GetEntity(EntityId id) { if (IsEntityIdValid(id)) - return m_aliveEntities[m_entities[id].aliveIndex]; + return m_entities[id].handle; else { NazaraError("Invalid ID"); @@ -177,45 +195,39 @@ namespace Ndk if (!m_orderedSystemsUpdated) ReorderSystems(); + // Move waiting entities to entity list + if (!m_waitingEntities.empty()) + { + constexpr std::size_t MinEntityCapacity = 10; //< We want to be able to grow entity count by at least ten entities per update without going to the waiting list + + m_entities.reserve(m_entities.size() + m_waitingEntities.size() + MinEntityCapacity); + for (auto& blockPtr : m_waitingEntities) + m_entities.push_back(std::move(*blockPtr)); + + m_waitingEntities.clear(); + } + // Handle killed entities before last call for (std::size_t i = m_killedEntities.FindFirst(); i != m_killedEntities.npos; i = m_killedEntities.FindNext(i)) { - EntityBlock& block = m_entities[i]; - Entity& entity = block.entity; + NazaraAssert(i < m_entityBlocks.size(), "Entity index out of range"); - NazaraAssert(entity.IsValid(), "Entity must be valid"); + Entity* entity = &m_entityBlocks[i]->entity; // Destruction of the entity (invalidation of handle by the same way) - entity.Destroy(); + entity->Destroy(); // Send back the identifier of the entity to the free queue - m_freeIdList.push_back(entity.GetId()); - - // We take out the handle from the list of alive entities - // With the idiom swap and pop - - NazaraAssert(block.aliveIndex < m_aliveEntities.size(), "Alive index out of range"); - - if (block.aliveIndex < m_aliveEntities.size() - 1) // If it's not the last handle - { - EntityHandle& lastHandle = m_aliveEntities.back(); - EntityHandle& myHandle = m_aliveEntities[block.aliveIndex]; - - myHandle = std::move(lastHandle); - - // We don't forget to update the index associated to the entity - m_entities[myHandle->GetId()].aliveIndex = block.aliveIndex; - } - m_aliveEntities.pop_back(); + m_freeIdList.push_back(entity->GetId()); } m_killedEntities.Reset(); // Handle of entities which need an update from the systems for (std::size_t i = m_dirtyEntities.FindFirst(); i != m_dirtyEntities.npos; i = m_dirtyEntities.FindNext(i)) { - NazaraAssert(i < m_entities.size(), "Entity index out of range"); + NazaraAssert(i < m_entityBlocks.size(), "Entity index out of range"); - Entity* entity = &m_entities[i].entity; + Entity* entity = &m_entityBlocks[i]->entity; // Check entity validity (as it could have been reported as dirty and killed during the same iteration) if (!entity->IsValid()) @@ -242,7 +254,7 @@ namespace Ndk } else { - // No, it shouldn't, remove it if it's part of the system + // No it shouldn't, remove it if it's part of the system if (partOfSystem) system->RemoveEntity(entity); } From 53b92106ff29b9257725a39bad41e1846923ae22 Mon Sep 17 00:00:00 2001 From: Lynix Date: Fri, 21 Apr 2017 01:02:23 +0200 Subject: [PATCH 04/33] Sdk/World: Fix block pointers --- SDK/src/NDK/World.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/SDK/src/NDK/World.cpp b/SDK/src/NDK/World.cpp index db3cef529..3daf21e06 100644 --- a/SDK/src/NDK/World.cpp +++ b/SDK/src/NDK/World.cpp @@ -73,7 +73,7 @@ namespace Ndk else { // We allocate a new entity - id = static_cast(m_entities.size()); + id = static_cast(m_entityBlocks.size()); if (m_entities.capacity() > m_entities.size()) { @@ -205,6 +205,10 @@ namespace Ndk m_entities.push_back(std::move(*blockPtr)); m_waitingEntities.clear(); + + // Update entity blocks pointers + for (std::size_t i = 0; i < m_entities.size(); ++i) + m_entityBlocks[i] = &m_entities[i]; } // Handle killed entities before last call From 203509f14163e34930d4021847307cbecb53281e Mon Sep 17 00:00:00 2001 From: Lynix Date: Fri, 21 Apr 2017 01:02:46 +0200 Subject: [PATCH 05/33] Sdk/BaseSystem: Reuse EntityList --- SDK/include/NDK/BaseSystem.hpp | 6 ++--- SDK/include/NDK/BaseSystem.inl | 22 +++++------------- SDK/include/NDK/EntityList.hpp | 11 ++++++--- SDK/include/NDK/EntityList.inl | 41 +++++++++++++++++++++++++++++++--- 4 files changed, 54 insertions(+), 26 deletions(-) diff --git a/SDK/include/NDK/BaseSystem.hpp b/SDK/include/NDK/BaseSystem.hpp index a323f2952..99dcbf768 100644 --- a/SDK/include/NDK/BaseSystem.hpp +++ b/SDK/include/NDK/BaseSystem.hpp @@ -9,6 +9,7 @@ #include #include +#include #include namespace Ndk @@ -33,7 +34,7 @@ namespace Ndk bool Filters(const Entity* entity) const; - inline const std::vector& GetEntities() const; + inline const EntityList& GetEntities() const; inline SystemIndex GetIndex() const; inline int GetUpdateOrder() const; inline float GetUpdateRate() const; @@ -84,12 +85,11 @@ namespace Ndk static inline bool Initialize(); static inline void Uninitialize(); - std::vector m_entities; - Nz::Bitset m_entityBits; Nz::Bitset<> m_excludedComponents; mutable Nz::Bitset<> m_filterResult; Nz::Bitset<> m_requiredAnyComponents; Nz::Bitset<> m_requiredComponents; + EntityList m_entities; SystemIndex m_systemIndex; World* m_world; bool m_updateEnabled; diff --git a/SDK/include/NDK/BaseSystem.inl b/SDK/include/NDK/BaseSystem.inl index e949d254b..5969dd8e3 100644 --- a/SDK/include/NDK/BaseSystem.inl +++ b/SDK/include/NDK/BaseSystem.inl @@ -56,7 +56,7 @@ namespace Ndk * \return A constant reference to the list of entities */ - inline const std::vector& BaseSystem::GetEntities() const + inline const EntityList& BaseSystem::GetEntities() const { return m_entities; } @@ -121,10 +121,7 @@ namespace Ndk inline bool BaseSystem::HasEntity(const Entity* entity) const { - if (!entity) - return false; - - return m_entityBits.UnboundedTest(entity->GetId()); + return m_entities.Has(entity); } /*! @@ -288,9 +285,7 @@ namespace Ndk { NazaraAssert(entity, "Invalid entity"); - m_entities.emplace_back(entity); - m_entityBits.UnboundedSet(entity->GetId(), true); - + m_entities.Insert(entity); entity->RegisterSystem(m_systemIndex); OnEntityAdded(entity); @@ -308,14 +303,7 @@ namespace Ndk { NazaraAssert(entity, "Invalid entity"); - auto it = std::find(m_entities.begin(), m_entities.end(), *entity); - NazaraAssert(it != m_entities.end(), "Entity is not part of this system"); - - // To avoid moving a lot of handles, we swap and pop - std::swap(*it, m_entities.back()); - m_entities.pop_back(); // We get it out of the vector - - m_entityBits.Reset(entity->GetId()); + m_entities.Remove(entity); entity->UnregisterSystem(m_systemIndex); OnEntityRemoved(entity); // And we alert our callback @@ -360,7 +348,7 @@ namespace Ndk } /*! - * \brief Uninitializes the BaseSystem + * \brief Uninitialize the BaseSystem */ inline void BaseSystem::Uninitialize() diff --git a/SDK/include/NDK/EntityList.hpp b/SDK/include/NDK/EntityList.hpp index 22a632e65..76097f87b 100644 --- a/SDK/include/NDK/EntityList.hpp +++ b/SDK/include/NDK/EntityList.hpp @@ -21,6 +21,8 @@ namespace Ndk using size_type = std::size_t; inline EntityList(); + inline EntityList(const EntityList& entityList); + inline EntityList(EntityList&& entityList) noexcept; ~EntityList() = default; inline void Clear(); @@ -33,11 +35,14 @@ namespace Ndk inline void Remove(Entity* entity); // STL API - inline iterator begin(); + inline iterator begin() const; inline bool empty() const; - inline iterator end(); + inline iterator end() const; inline size_type size() const; + inline EntityList& operator=(const EntityList& entityList); + inline EntityList& operator=(EntityList&& entityList) noexcept; + private: inline std::size_t FindNext(std::size_t currentId) const; inline World* GetWorld() const; @@ -46,7 +51,7 @@ namespace Ndk World* m_world; }; - class EntityList::iterator : public std::iterator + class NDK_API EntityList::iterator : public std::iterator { friend EntityList; diff --git a/SDK/include/NDK/EntityList.inl b/SDK/include/NDK/EntityList.inl index 6d9447a7f..8d639b762 100644 --- a/SDK/include/NDK/EntityList.inl +++ b/SDK/include/NDK/EntityList.inl @@ -16,13 +16,32 @@ namespace Ndk */ /*! - * \brief Clears the set from every entities + * \brief Construct a new entity list */ inline EntityList::EntityList() : m_world(nullptr) { } + /*! + * \brief Construct a new entity list by copying another one + */ + inline EntityList::EntityList(const EntityList& entityList) : + m_entityBits(entityList.m_entityBits), + m_world(entityList.m_world) + { + } + + /*! + * \brief Construct a new entity list by moving a list into this one + */ + inline EntityList::EntityList(EntityList&& entityList) noexcept : + m_entityBits(std::move(entityList.m_entityBits)), + m_world(entityList.m_world) + { + } + + /*! * \brief Clears the set from every entities * @@ -93,7 +112,7 @@ namespace Ndk } // STL Interface - inline EntityList::iterator EntityList::begin() + inline EntityList::iterator EntityList::begin() const { return EntityList::iterator(this, m_entityBits.FindFirst()); } @@ -103,7 +122,7 @@ namespace Ndk return m_entityBits.TestAny(); } - inline EntityList::iterator EntityList::end() + inline EntityList::iterator EntityList::end() const { return EntityList::iterator(this, m_entityBits.npos); } @@ -113,6 +132,22 @@ namespace Ndk return m_entityBits.Count(); } + inline EntityList& EntityList::operator=(const EntityList& entityList) + { + m_entityBits = entityList.m_entityBits; + m_world = entityList.m_world; + + return *this; + } + + inline EntityList& EntityList::operator=(EntityList && entityList) noexcept + { + m_entityBits = std::move(entityList.m_entityBits); + m_world = entityList.m_world; + + return *this; + } + inline std::size_t EntityList::FindNext(std::size_t currentId) const { return m_entityBits.FindNext(currentId); From 3c99782643007fbb501c25e517d0b6e839b3e6cc Mon Sep 17 00:00:00 2001 From: Lynix Date: Fri, 21 Apr 2017 21:27:07 +0200 Subject: [PATCH 06/33] Sdk/EntityList: Fix empty() behavior --- SDK/include/NDK/EntityList.inl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SDK/include/NDK/EntityList.inl b/SDK/include/NDK/EntityList.inl index 8d639b762..ad87540cd 100644 --- a/SDK/include/NDK/EntityList.inl +++ b/SDK/include/NDK/EntityList.inl @@ -119,7 +119,7 @@ namespace Ndk inline bool EntityList::empty() const { - return m_entityBits.TestAny(); + return !m_entityBits.TestAny(); } inline EntityList::iterator EntityList::end() const From 4f6706284e99eb0f85fa200bcd064ba8c1cd8dce Mon Sep 17 00:00:00 2001 From: Lynix Date: Fri, 21 Apr 2017 21:27:27 +0200 Subject: [PATCH 07/33] Sdk/World: Fix entity block handling --- SDK/include/NDK/World.inl | 3 ++- SDK/src/NDK/World.cpp | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/SDK/include/NDK/World.inl b/SDK/include/NDK/World.inl index 45d358ad4..4720ab3db 100644 --- a/SDK/include/NDK/World.inl +++ b/SDK/include/NDK/World.inl @@ -266,6 +266,7 @@ namespace Ndk { m_aliveEntities = std::move(world.m_aliveEntities); m_dirtyEntities = std::move(world.m_dirtyEntities); + m_entityBlocks = std::move(world.m_entityBlocks); m_freeIdList = std::move(world.m_freeIdList); m_killedEntities = std::move(world.m_killedEntities); m_orderedSystems = std::move(world.m_orderedSystems); @@ -285,7 +286,7 @@ namespace Ndk inline void World::Invalidate() { - m_dirtyEntities.Resize(m_entities.size(), false); + m_dirtyEntities.Resize(m_entityBlocks.size(), false); m_dirtyEntities.Set(true); // Activation of all bits } diff --git a/SDK/src/NDK/World.cpp b/SDK/src/NDK/World.cpp index 3daf21e06..a40d5f175 100644 --- a/SDK/src/NDK/World.cpp +++ b/SDK/src/NDK/World.cpp @@ -68,6 +68,8 @@ namespace Ndk m_freeIdList.pop_back(); entBlock = &m_entities[id]; + entBlock->handle.Reset(&entBlock->entity); //< Reset handle (as it was reset when entity got destroyed) + m_entityBlocks[id] = entBlock; } else @@ -114,6 +116,7 @@ namespace Ndk // First, destruction of entities, then handles // This is made to avoid that handle warn uselessly entities before their destruction m_entities.clear(); + m_entityBlocks.clear(); m_aliveEntities.Clear(); m_dirtyEntities.Clear(); From 33b10989e2da7f7c39ac68d618d513a6f9dc28ec Mon Sep 17 00:00:00 2001 From: Lynix Date: Fri, 21 Apr 2017 21:39:37 +0200 Subject: [PATCH 08/33] Physics2D/PhysWorld2D: Initialize callbacks to nullptr to prevent misuse --- include/Nazara/Physics2D/PhysWorld2D.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/Nazara/Physics2D/PhysWorld2D.hpp b/include/Nazara/Physics2D/PhysWorld2D.hpp index 4873e5642..30a3b1141 100644 --- a/include/Nazara/Physics2D/PhysWorld2D.hpp +++ b/include/Nazara/Physics2D/PhysWorld2D.hpp @@ -64,10 +64,10 @@ namespace Nz struct Callback { - ContactEndCallback endCallback; - ContactPreSolveCallback preSolveCallback; - ContactPostSolveCallback postSolveCallback; - ContactStartCallback startCallback; + ContactEndCallback endCallback = nullptr; + ContactPreSolveCallback preSolveCallback = nullptr; + ContactPostSolveCallback postSolveCallback = nullptr; + ContactStartCallback startCallback = nullptr; void* userdata; }; From 140e52203d25c0a5df6c6bf3efdd39dcd428a271 Mon Sep 17 00:00:00 2001 From: Lynix Date: Fri, 21 Apr 2017 21:48:05 +0200 Subject: [PATCH 09/33] Graphics/ForwardRenderQueue: Cleanup a bit --- include/Nazara/Graphics/ForwardRenderQueue.hpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/include/Nazara/Graphics/ForwardRenderQueue.hpp b/include/Nazara/Graphics/ForwardRenderQueue.hpp index f0ac6edda..218fd312d 100644 --- a/include/Nazara/Graphics/ForwardRenderQueue.hpp +++ b/include/Nazara/Graphics/ForwardRenderQueue.hpp @@ -73,7 +73,7 @@ namespace Nz std::vector billboards; }; - typedef std::map BatchedBillboardContainer; + using BatchedBillboardContainer = std::map; struct BatchedBillboardPipelineEntry { @@ -81,7 +81,7 @@ namespace Nz bool enabled = false; }; - typedef std::map BillboardPipelineBatches; + using BillboardPipelineBatches = std::map; /// Sprites struct SpriteChain_XYZ_Color_UV @@ -97,7 +97,7 @@ namespace Nz std::vector spriteChains; }; - typedef std::map SpriteOverlayBatches; + using SpriteOverlayBatches = std::map; struct BatchedBasicSpriteEntry { @@ -107,7 +107,7 @@ namespace Nz bool enabled = false; }; - typedef std::map SpriteMaterialBatches; + using SpriteMaterialBatches = std::map; struct BatchedSpritePipelineEntry { @@ -115,7 +115,7 @@ namespace Nz bool enabled = false; }; - typedef std::map SpritePipelineBatches; + using SpritePipelineBatches = std::map; /// Meshes struct MeshDataComparator @@ -132,7 +132,7 @@ namespace Nz Spheref squaredBoundingSphere; }; - typedef std::map MeshInstanceContainer; + using MeshInstanceContainer = std::map; struct BatchedModelEntry { @@ -142,7 +142,7 @@ namespace Nz bool enabled = false; }; - typedef std::map MeshMaterialBatches; + using MeshMaterialBatches = std::map; struct BatchedMaterialEntry { @@ -150,7 +150,7 @@ namespace Nz MeshMaterialBatches materialMap; }; - typedef std::map MeshPipelineBatches; + using MeshPipelineBatches = std::map; struct TransparentModelData { @@ -160,7 +160,7 @@ namespace Nz const Material* material; }; - typedef std::vector TransparentModelContainer; + using TransparentModelContainer = std::vector; struct Layer { From c06db7d9c5a796a54f7f073ae97b1a796a77676c Mon Sep 17 00:00:00 2001 From: Lynix Date: Sat, 22 Apr 2017 12:58:14 +0200 Subject: [PATCH 10/33] SDK: Fix unit tests --- tests/SDK/NDK/BaseSystem.cpp | 4 ++-- tests/SDK/NDK/Entity.cpp | 12 ++++++------ tests/SDK/NDK/EntityOwner.cpp | 6 +++--- tests/SDK/NDK/World.cpp | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/SDK/NDK/BaseSystem.cpp b/tests/SDK/NDK/BaseSystem.cpp index a202c1f70..3bb0fa9e4 100644 --- a/tests/SDK/NDK/BaseSystem.cpp +++ b/tests/SDK/NDK/BaseSystem.cpp @@ -38,7 +38,7 @@ SCENARIO("BaseSystem", "[NDK][BASESYSTEM]") WHEN("We add an entity") { - const Ndk::EntityHandle& entity = world.CreateEntity(); + Ndk::EntityHandle entity = world.CreateEntity(); entity->AddComponent(); THEN("System should have it") @@ -50,7 +50,7 @@ SCENARIO("BaseSystem", "[NDK][BASESYSTEM]") WHEN("We add an entity with excluded component") { - const Ndk::EntityHandle& entity = world.CreateEntity(); + Ndk::EntityHandle entity = world.CreateEntity(); entity->AddComponent(); entity->AddComponent(); diff --git a/tests/SDK/NDK/Entity.cpp b/tests/SDK/NDK/Entity.cpp index cf02370ec..dd3216fb0 100644 --- a/tests/SDK/NDK/Entity.cpp +++ b/tests/SDK/NDK/Entity.cpp @@ -57,18 +57,18 @@ SCENARIO("Entity", "[NDK][ENTITY]") { Ndk::World world; Ndk::BaseSystem& system = world.AddSystem(); - const Ndk::EntityHandle& entity = world.CreateEntity(); + Ndk::EntityHandle entity = world.CreateEntity(); WHEN("We add our UpdateComponent") { UpdatableComponent& updatableComponent = entity->AddComponent(); - REQUIRE(!updatableComponent.IsUpdated()); + CHECK(!updatableComponent.IsUpdated()); THEN("Update the world should update the entity's component") { world.Update(1.f); UpdatableComponent& updatableComponentGet = entity->GetComponent(); - REQUIRE(updatableComponentGet.IsUpdated()); + CHECK(updatableComponentGet.IsUpdated()); } THEN("Update the world should not update the entity's component if it's disabled") @@ -76,14 +76,14 @@ SCENARIO("Entity", "[NDK][ENTITY]") entity->Enable(false); world.Update(1.f); UpdatableComponent& updatableComponentGet = entity->GetComponent(); - REQUIRE(!updatableComponentGet.IsUpdated()); + CHECK(!updatableComponentGet.IsUpdated()); } THEN("We can remove its component") { entity->RemoveComponent(Ndk::GetComponentIndex()); world.Update(1.f); - REQUIRE(!entity->HasComponent()); + CHECK(!entity->HasComponent()); } } @@ -94,7 +94,7 @@ SCENARIO("Entity", "[NDK][ENTITY]") THEN("It's no more valid") { - REQUIRE(!world.IsEntityValid(entity)); + CHECK(!world.IsEntityValid(entity)); } } } diff --git a/tests/SDK/NDK/EntityOwner.cpp b/tests/SDK/NDK/EntityOwner.cpp index eb9a29edf..df5130c2a 100644 --- a/tests/SDK/NDK/EntityOwner.cpp +++ b/tests/SDK/NDK/EntityOwner.cpp @@ -7,7 +7,7 @@ SCENARIO("EntityOwner", "[NDK][ENTITYOWNER]") GIVEN("A world & an entity") { Ndk::World world; - const Ndk::EntityHandle& entity = world.CreateEntity(); + Ndk::EntityHandle entity = world.CreateEntity(); WHEN("We set the ownership of the entity to our owner") { @@ -15,14 +15,14 @@ SCENARIO("EntityOwner", "[NDK][ENTITYOWNER]") THEN("Entity is still valid") { - REQUIRE(entity.IsValid()); + CHECK(entity.IsValid()); } THEN("Resetting or getting out of scope is no more valid") { entityOwner.Reset(); world.Update(1.f); - REQUIRE(!entity.IsValid()); + CHECK(!entity.IsValid()); } } } diff --git a/tests/SDK/NDK/World.cpp b/tests/SDK/NDK/World.cpp index 85ee241b4..d46b6621b 100644 --- a/tests/SDK/NDK/World.cpp +++ b/tests/SDK/NDK/World.cpp @@ -79,7 +79,7 @@ SCENARIO("World", "[NDK][WORLD]") AND_WHEN("We update our world with our entity") { REQUIRE(&world.GetSystem(UpdateSystem::systemIndex) == &world.GetSystem()); - const Ndk::EntityHandle& entity = world.CreateEntity(); + Ndk::EntityHandle entity = world.CreateEntity(); UpdatableComponent& component = entity->AddComponent(); THEN("Our entity component must be updated") From 0708531f6c78325fdb42ab1deb711a360f760399 Mon Sep 17 00:00:00 2001 From: Lynix Date: Sat, 22 Apr 2017 12:58:39 +0200 Subject: [PATCH 11/33] Sdk/World: Inline GetEntity and KillEntity --- SDK/include/NDK/World.hpp | 4 ++-- SDK/include/NDK/World.inl | 37 ++++++++++++++++++++++++++++++++++++- SDK/src/NDK/World.cpp | 36 +----------------------------------- 3 files changed, 39 insertions(+), 38 deletions(-) diff --git a/SDK/include/NDK/World.hpp b/SDK/include/NDK/World.hpp index b1f1abde1..29b9a4a9e 100644 --- a/SDK/include/NDK/World.hpp +++ b/SDK/include/NDK/World.hpp @@ -47,7 +47,7 @@ namespace Ndk void Clear() noexcept; const EntityHandle& CloneEntity(EntityId id); - const EntityHandle& GetEntity(EntityId id); + inline const EntityHandle& GetEntity(EntityId id); inline const EntityList& GetEntities() const; inline BaseSystem& GetSystem(SystemIndex index); template SystemType& GetSystem(); @@ -55,7 +55,7 @@ namespace Ndk inline bool HasSystem(SystemIndex index) const; template bool HasSystem() const; - void KillEntity(Entity* entity); + inline void KillEntity(Entity* entity); inline void KillEntities(const EntityVector& list); inline bool IsEntityValid(const Entity* entity) const; diff --git a/SDK/include/NDK/World.inl b/SDK/include/NDK/World.inl index 4720ab3db..d45f0ff1d 100644 --- a/SDK/include/NDK/World.inl +++ b/SDK/include/NDK/World.inl @@ -164,18 +164,53 @@ namespace Ndk return HasSystem(index); } + /*! + * \brief Marks an entity for deletion + * + * \param Pointer to the entity + * + * \remark If the entity pointer is invalid, nothing is done + * \remark For safety, entities are not killed until the next world update + */ + inline void World::KillEntity(Entity* entity) + { + if (IsEntityValid(entity)) + m_killedEntities.UnboundedSet(entity->GetId(), true); + } + /*! * \brief Kills a set of entities * + * This function has the same effect as calling KillEntity for every entity contained in the vector + * * \param list Set of entities to kill */ - inline void World::KillEntities(const EntityVector& list) { for (const EntityHandle& entity : list) KillEntity(entity); } + /*! + * \brief Gets an entity + * \return A constant reference to a handle of the entity + * + * \param id Identifier of the entity + * + * \remark Handle referenced by this function can move in memory when updating the world, do not keep a reference to a handle from a world update to another + * \remark If an invalid identifier is provided, an error got triggered and an invalid handle is returned + */ + inline const EntityHandle& World::GetEntity(EntityId id) + { + if (IsEntityIdValid(id)) + return m_entityBlocks[id]->handle; + else + { + NazaraError("Invalid ID"); + return EntityHandle::InvalidHandle; + } + } + /*! * \brief Checks whether or not an entity is valid * \return true If it is the case diff --git a/SDK/src/NDK/World.cpp b/SDK/src/NDK/World.cpp index a40d5f175..88a23060d 100644 --- a/SDK/src/NDK/World.cpp +++ b/SDK/src/NDK/World.cpp @@ -141,7 +141,7 @@ namespace Ndk return EntityHandle::InvalidHandle; } - EntityHandle clone = CreateEntity(); + const EntityHandle& clone = CreateEntity(); const Nz::Bitset<>& componentBits = original->GetComponentBits(); for (std::size_t i = componentBits.FindFirst(); i != componentBits.npos; i = componentBits.FindNext(i)) @@ -153,40 +153,6 @@ namespace Ndk return GetEntity(clone->GetId()); } - /*! - * \brief Kills an entity - * - * \param Pointer to the entity - * - * \remark No change is done if entity is invalid - */ - - void World::KillEntity(Entity* entity) - { - if (IsEntityValid(entity)) - m_killedEntities.UnboundedSet(entity->GetId(), true); - } - - /*! - * \brief Gets an entity - * \return A constant reference to the modified entity - * - * \param id Identifier of the entity - * - * \remark Produces a NazaraError if entity identifier is not valid - */ - - const EntityHandle& World::GetEntity(EntityId id) - { - if (IsEntityIdValid(id)) - return m_entities[id].handle; - else - { - NazaraError("Invalid ID"); - return EntityHandle::InvalidHandle; - } - } - /*! * \brief Updates the world * From ec310b957749d36ced26a6c99c2b9a8beabed965 Mon Sep 17 00:00:00 2001 From: Lynix Date: Sat, 22 Apr 2017 12:59:06 +0200 Subject: [PATCH 12/33] Sdk/Entity: Fix removed components sometimes not being removed --- SDK/src/NDK/Entity.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/SDK/src/NDK/Entity.cpp b/SDK/src/NDK/Entity.cpp index 7cdc87d07..7419ac627 100644 --- a/SDK/src/NDK/Entity.cpp +++ b/SDK/src/NDK/Entity.cpp @@ -24,6 +24,7 @@ namespace Ndk HandledObject(std::move(entity)), m_components(std::move(entity.m_components)), m_componentBits(std::move(entity.m_componentBits)), + m_removedComponentBits(std::move(entity.m_removedComponentBits)), m_systemBits(std::move(entity.m_systemBits)), m_id(entity.m_id), m_world(entity.m_world), From 06038a4d81feda62e87e1a5ac975b4d723936555 Mon Sep 17 00:00:00 2001 From: Lynix Date: Sat, 22 Apr 2017 15:08:05 +0200 Subject: [PATCH 13/33] Cleanup pass --- include/Nazara/Math/Box.hpp | 4 +-- include/Nazara/Math/Box.inl | 34 +++++------------- include/Nazara/Math/Rect.hpp | 4 +-- include/Nazara/Math/Rect.inl | 32 ++++------------- include/Nazara/Math/Sphere.hpp | 4 +-- include/Nazara/Math/Sphere.inl | 34 +++++------------- include/Nazara/Utility/IndexIterator.hpp | 18 +++++----- include/Nazara/Utility/IndexIterator.inl | 16 ++++----- include/Nazara/Utility/IndexMapper.hpp | 18 +++++----- include/Nazara/Utility/TriangleIterator.hpp | 6 ++-- plugins/Assimp/Plugin.cpp | 8 ++--- src/Nazara/Core/Hash/MD5.cpp | 8 ++--- src/Nazara/Core/Hash/Whirlpool.cpp | 12 +++---- src/Nazara/Core/String.cpp | 2 +- .../Graphics/ForwardRenderTechnique.cpp | 4 +-- src/Nazara/Physics2D/PhysWorld2D.cpp | 18 +++++----- src/Nazara/Physics2D/RigidBody2D.cpp | 2 +- src/Nazara/Utility/Formats/MD5MeshLoader.cpp | 8 ++--- src/Nazara/Utility/Formats/OBJLoader.cpp | 4 +-- src/Nazara/Utility/Formats/OBJParser.cpp | 6 ++-- src/Nazara/Utility/IndexMapper.cpp | 36 +++++++------------ src/Nazara/Utility/Mesh.cpp | 16 ++++----- src/Nazara/Utility/SimpleTextDrawer.cpp | 6 ++-- src/Nazara/Utility/Skeleton.cpp | 17 +++------ src/Nazara/Utility/TriangleIterator.cpp | 13 ++----- 25 files changed, 124 insertions(+), 206 deletions(-) diff --git a/include/Nazara/Math/Box.hpp b/include/Nazara/Math/Box.hpp index b3f0aed2b..9434cda27 100644 --- a/include/Nazara/Math/Box.hpp +++ b/include/Nazara/Math/Box.hpp @@ -74,8 +74,8 @@ namespace Nz Box& Transform(const Matrix4& matrix, bool applyTranslation = true); Box& Translate(const Vector3& translation); - T& operator[](unsigned int i); - T operator[](unsigned int i) const; + T& operator[](std::size_t i); + T operator[](std::size_t i) const; Box operator*(T scalar) const; Box operator*(const Vector3& vec) const; diff --git a/include/Nazara/Math/Box.inl b/include/Nazara/Math/Box.inl index fb22039ba..1b5276ee6 100644 --- a/include/Nazara/Math/Box.inl +++ b/include/Nazara/Math/Box.inl @@ -735,24 +735,15 @@ namespace Nz * \brief Returns the ith element of the box * \return A reference to the ith element of the box * - * \remark Access to index greather than 6 is undefined behavior - * \remark Produce a NazaraError if you try to acces to index greather than 6 with NAZARA_MATH_SAFE defined + * \remark Access to index greater than 6 is undefined behavior + * \remark Produce a NazaraError if you try to access to index greater than 6 with NAZARA_MATH_SAFE defined * \throw std::domain_error if NAZARA_MATH_SAFE is defined and one of you try to acces to index greather than 6 */ template - T& Box::operator[](unsigned int i) + T& Box::operator[](std::size_t i) { - #if NAZARA_MATH_SAFE - if (i >= 6) - { - StringStream ss; - ss << "Index out of range: (" << i << " >= 6)"; - - NazaraError(ss); - throw std::domain_error(ss.ToString()); - } - #endif + NazaraAssert(i < 6, "Index out of range"); return *(&x+i); } @@ -761,24 +752,15 @@ namespace Nz * \brief Returns the ith element of the box * \return A value to the ith element of the box * - * \remark Access to index greather than 6 is undefined behavior - * \remark Produce a NazaraError if you try to acces to index greather than 6 with NAZARA_MATH_SAFE defined + * \remark Access to index greater than 6 is undefined behavior + * \remark Produce a NazaraError if you try to access to index greater than 6 with NAZARA_MATH_SAFE defined * \throw std::domain_error if NAZARA_MATH_SAFE is defined and one of you try to acces to index greather than 6 */ template - T Box::operator[](unsigned int i) const + T Box::operator[](std::size_t i) const { - #if NAZARA_MATH_SAFE - if (i >= 6) - { - StringStream ss; - ss << "Index out of range: (" << i << " >= 6)"; - - NazaraError(ss); - throw std::domain_error(ss.ToString()); - } - #endif + NazaraAssert(i < 6, "Index out of range"); return *(&x+i); } diff --git a/include/Nazara/Math/Rect.hpp b/include/Nazara/Math/Rect.hpp index f297cddd3..9ca7f2ef5 100644 --- a/include/Nazara/Math/Rect.hpp +++ b/include/Nazara/Math/Rect.hpp @@ -64,8 +64,8 @@ namespace Nz Rect& Translate(const Vector2& translation); - T& operator[](unsigned int i); - T operator[](unsigned int i) const; + T& operator[](std::size_t i); + T operator[](std::size_t i) const; Rect operator*(T scalar) const; Rect operator*(const Vector2& vec) const; diff --git a/include/Nazara/Math/Rect.inl b/include/Nazara/Math/Rect.inl index 0365ebf54..47a837cbd 100644 --- a/include/Nazara/Math/Rect.inl +++ b/include/Nazara/Math/Rect.inl @@ -578,23 +578,14 @@ namespace Nz * \return A reference to the ith element of the rectangle * * \remark Access to index greather than 4 is undefined behavior - * \remark Produce a NazaraError if you try to acces to index greather than 4 with NAZARA_MATH_SAFE defined + * \remark Produce a NazaraError if you try to access to index greater than 4 with NAZARA_MATH_SAFE defined * \throw std::domain_error if NAZARA_MATH_SAFE is defined and one of you try to acces to index greather than 4 */ template - T& Rect::operator[](unsigned int i) + T& Rect::operator[](std::size_t i) { - #if NAZARA_MATH_SAFE - if (i >= 4) - { - StringStream ss; - ss << "Index out of range: (" << i << " >= 4)"; - - NazaraError(ss); - throw std::domain_error(ss.ToString()); - } - #endif + NazaraAssert(i < 4, "Index out of range"); return *(&x+i); } @@ -603,24 +594,15 @@ namespace Nz * \brief Returns the ith element of the rectangle * \return A value to the ith element of the rectangle * - * \remark Access to index greather than 4 is undefined behavior - * \remark Produce a NazaraError if you try to acces to index greather than 4 with NAZARA_MATH_SAFE defined + * \remark Access to index greater than 4 is undefined behavior + * \remark Produce a NazaraError if you try to access to index greater than 4 with NAZARA_MATH_SAFE defined * \throw std::domain_error if NAZARA_MATH_SAFE is defined and one of you try to acces to index greather than 4 */ template - T Rect::operator[](unsigned int i) const + T Rect::operator[](std::size_t i) const { - #if NAZARA_MATH_SAFE - if (i >= 4) - { - StringStream ss; - ss << "Index out of range: (" << i << " >= 4)"; - - NazaraError(ss); - throw std::domain_error(ss.ToString()); - } - #endif + NazaraAssert(i < 4, "Index out of range"); return *(&x+i); } diff --git a/include/Nazara/Math/Sphere.hpp b/include/Nazara/Math/Sphere.hpp index 57d2d6765..cfa693507 100644 --- a/include/Nazara/Math/Sphere.hpp +++ b/include/Nazara/Math/Sphere.hpp @@ -60,8 +60,8 @@ namespace Nz String ToString() const; - T& operator[](unsigned int i); - T operator[](unsigned int i) const; + T& operator[](std::size_t i); + T operator[](std::size_t i) const; Sphere operator*(T scalar) const; Sphere& operator=(const Sphere& other) = default; diff --git a/include/Nazara/Math/Sphere.inl b/include/Nazara/Math/Sphere.inl index 8d803c2e0..5aede77c2 100644 --- a/include/Nazara/Math/Sphere.inl +++ b/include/Nazara/Math/Sphere.inl @@ -466,24 +466,15 @@ namespace Nz * \brief Returns the ith element of the sphere * \return A reference to the ith element of the sphere * - * \remark Access to index greather than 4 is undefined behavior - * \remark Produce a NazaraError if you try to acces to index greather than 4 with NAZARA_MATH_SAFE defined + * \remark Access to index greater than 4 is undefined behavior + * \remark Produce a NazaraError if you try to access to index greater than 4 with NAZARA_MATH_SAFE defined * \throw std::domain_error if NAZARA_MATH_SAFE is defined and one of you try to acces to index greather than 4 */ template - T& Sphere::operator[](unsigned int i) + T& Sphere::operator[](std::size_t i) { - #if NAZARA_MATH_SAFE - if (i >= 4) - { - StringStream ss; - ss << "Index out of range: (" << i << " >= 4)"; - - NazaraError(ss); - throw std::domain_error(ss.ToString()); - } - #endif + NazaraAssert(i < 4, "Index out of range"); return *(&x+i); } @@ -492,24 +483,15 @@ namespace Nz * \brief Returns the ith element of the sphere * \return A value to the ith element of the sphere * - * \remark Access to index greather than 4 is undefined behavior - * \remark Produce a NazaraError if you try to acces to index greather than 4 with NAZARA_MATH_SAFE defined + * \remark Access to index greater than 4 is undefined behavior + * \remark Produce a NazaraError if you try to access to index greater than 4 with NAZARA_MATH_SAFE defined * \throw std::domain_error if NAZARA_MATH_SAFE is defined and one of you try to acces to index greather than 4 */ template - T Sphere::operator[](unsigned int i) const + T Sphere::operator[](std::size_t i) const { - #if NAZARA_MATH_SAFE - if (i >= 4) - { - StringStream ss; - ss << "Index out of range: (" << i << " >= 4)"; - - NazaraError(ss); - throw std::domain_error(ss.ToString()); - } - #endif + NazaraAssert(i < 4, "Index out of range"); return *(&x+i); } diff --git a/include/Nazara/Utility/IndexIterator.hpp b/include/Nazara/Utility/IndexIterator.hpp index 5c15bbdc2..63f9d96b9 100644 --- a/include/Nazara/Utility/IndexIterator.hpp +++ b/include/Nazara/Utility/IndexIterator.hpp @@ -26,15 +26,15 @@ namespace Nz Reference operator*() const; - Reference operator[](unsigned int index) const; + Reference operator[](std::size_t index) const; IndexIterator& operator=(const IndexIterator& iterator); - IndexIterator operator+(unsigned int indexCount) const; - IndexIterator operator-(unsigned int indexCount) const; + IndexIterator operator+(std::size_t indexCount) const; + IndexIterator operator-(std::size_t indexCount) const; - IndexIterator& operator+=(unsigned int indexCount); - IndexIterator& operator-=(unsigned int indexCount); + IndexIterator& operator+=(std::size_t indexCount); + IndexIterator& operator-=(std::size_t indexCount); IndexIterator& operator++(); IndexIterator operator++(int); @@ -50,10 +50,10 @@ namespace Nz friend bool operator>=(const IndexIterator& lhs, const IndexIterator& rhs); private: - IndexIterator(IndexMapper* mapper, unsigned int index); + IndexIterator(IndexMapper* mapper, std::size_t index); IndexMapper* m_mapper; - unsigned int m_index; + std::size_t m_index; }; class IndexIterator::Reference @@ -70,10 +70,10 @@ namespace Nz operator UInt32() const; private: - Reference(IndexMapper* mapper, unsigned int index); + Reference(IndexMapper* mapper, std::size_t index); IndexMapper* m_mapper; - unsigned int m_index; + std::size_t m_index; }; } diff --git a/include/Nazara/Utility/IndexIterator.inl b/include/Nazara/Utility/IndexIterator.inl index df1613d67..0398059f6 100644 --- a/include/Nazara/Utility/IndexIterator.inl +++ b/include/Nazara/Utility/IndexIterator.inl @@ -20,7 +20,7 @@ namespace Nz { } - inline IndexIterator::IndexIterator(IndexMapper* mapper, unsigned int index) : + inline IndexIterator::IndexIterator(IndexMapper* mapper, std::size_t index) : m_mapper(mapper), m_index(index) { @@ -31,7 +31,7 @@ namespace Nz return Reference(m_mapper, m_index); } - inline IndexIterator::Reference IndexIterator::operator[](unsigned int index) const + inline IndexIterator::Reference IndexIterator::operator[](std::size_t index) const { return Reference(m_mapper, m_index+index); } @@ -44,24 +44,24 @@ namespace Nz return *this; } - inline IndexIterator IndexIterator::operator+(unsigned int indexCount) const + inline IndexIterator IndexIterator::operator+(std::size_t indexCount) const { return IndexIterator(m_mapper, m_index + indexCount); } - inline IndexIterator IndexIterator::operator-(unsigned int indexCount) const + inline IndexIterator IndexIterator::operator-(std::size_t indexCount) const { return IndexIterator(m_mapper, m_index - indexCount); } - inline IndexIterator& IndexIterator::operator+=(unsigned int indexCount) + inline IndexIterator& IndexIterator::operator+=(std::size_t indexCount) { m_index += indexCount; return *this; } - inline IndexIterator& IndexIterator::operator-=(unsigned int indexCount) + inline IndexIterator& IndexIterator::operator-=(std::size_t indexCount) { m_index += indexCount; @@ -133,7 +133,7 @@ namespace Nz /**************************IndexIterator::Reference*************************/ - inline IndexIterator::Reference::Reference(IndexMapper* mapper, unsigned int index) : + inline IndexIterator::Reference::Reference(IndexMapper* mapper, std::size_t index) : m_mapper(mapper), m_index(index) { @@ -148,7 +148,7 @@ namespace Nz inline IndexIterator::Reference& IndexIterator::Reference::operator=(const IndexIterator::Reference& reference) { - m_mapper->Set(m_index, reference); // Conversion implicite en UInt32 + m_mapper->Set(m_index, reference); // Implicit conversion to UInt32 return *this; } diff --git a/include/Nazara/Utility/IndexMapper.hpp b/include/Nazara/Utility/IndexMapper.hpp index 84c73bc53..b18e8989f 100644 --- a/include/Nazara/Utility/IndexMapper.hpp +++ b/include/Nazara/Utility/IndexMapper.hpp @@ -17,9 +17,6 @@ namespace Nz class IndexIterator; class SubMesh; - using IndexMapperGetter = UInt32 (*)(const void* buffer, unsigned int i); - using IndexMapperSetter = void (*)(void* buffer, unsigned int i, UInt32 value); - class NAZARA_UTILITY_API IndexMapper { public: @@ -29,11 +26,11 @@ namespace Nz IndexMapper(const SubMesh* subMesh, BufferAccess access = BufferAccess_ReadOnly); ~IndexMapper() = default; - UInt32 Get(unsigned int i) const; + UInt32 Get(std::size_t i) const; const IndexBuffer* GetBuffer() const; - unsigned int GetIndexCount() const; + std::size_t GetIndexCount() const; - void Set(unsigned int i, UInt32 value); + void Set(std::size_t i, UInt32 value); void Unmap(); @@ -45,10 +42,13 @@ namespace Nz // Méthodes STD private: + using Getter = UInt32(*)(const void* buffer, std::size_t i); + using Setter = void(*)(void* buffer, std::size_t i, UInt32 value); + BufferMapper m_mapper; - IndexMapperGetter m_getter; - IndexMapperSetter m_setter; - unsigned int m_indexCount; + Getter m_getter; + Setter m_setter; + std::size_t m_indexCount; }; } diff --git a/include/Nazara/Utility/TriangleIterator.hpp b/include/Nazara/Utility/TriangleIterator.hpp index bfc5a01fc..8bf17a2eb 100644 --- a/include/Nazara/Utility/TriangleIterator.hpp +++ b/include/Nazara/Utility/TriangleIterator.hpp @@ -24,7 +24,7 @@ namespace Nz bool Advance(); - UInt32 operator[](unsigned int i) const; + UInt32 operator[](std::size_t i) const; void Unmap(); @@ -32,8 +32,8 @@ namespace Nz PrimitiveMode m_primitiveMode; UInt32 m_triangleIndices[3]; IndexMapper m_indexMapper; - unsigned int m_currentIndex; - unsigned int m_indexCount; + std::size_t m_currentIndex; + std::size_t m_indexCount; }; } diff --git a/plugins/Assimp/Plugin.cpp b/plugins/Assimp/Plugin.cpp index a5cdf3cbf..8a4ec0e09 100644 --- a/plugins/Assimp/Plugin.cpp +++ b/plugins/Assimp/Plugin.cpp @@ -153,7 +153,7 @@ bool Load(Mesh* mesh, Stream& stream, const MeshParams& parameters) if (animatedMesh) { - mesh->CreateSkeletal(joints.size()); + mesh->CreateSkeletal(UInt32(joints.size())); Skeleton* skeleton = mesh->GetSkeleton(); @@ -171,7 +171,7 @@ bool Load(Mesh* mesh, Stream& stream, const MeshParams& parameters) mesh->CreateStatic(); // aiMaterial index in scene => Material index and data in Mesh - std::unordered_map> materials; + std::unordered_map> materials; for (unsigned int i = 0; i < scene->mNumMeshes; ++i) { @@ -300,7 +300,7 @@ bool Load(Mesh* mesh, Stream& stream, const MeshParams& parameters) if (aiGetMaterialInteger(aiMat, AI_MATKEY_TWOSIDED, &iValue) == aiReturn_SUCCESS) matData.SetParameter(MaterialData::FaceCulling, !iValue); - matIt = materials.insert(std::make_pair(iMesh->mMaterialIndex, std::make_pair(materials.size(), std::move(matData)))).first; + matIt = materials.insert(std::make_pair(iMesh->mMaterialIndex, std::make_pair(UInt32(materials.size()), std::move(matData)))).first; } subMesh->SetMaterialIndex(matIt->first); @@ -308,7 +308,7 @@ bool Load(Mesh* mesh, Stream& stream, const MeshParams& parameters) mesh->AddSubMesh(subMesh); } - mesh->SetMaterialCount(std::max(materials.size(), 1)); + mesh->SetMaterialCount(std::max(UInt32(materials.size()), 1)); for (const auto& pair : materials) mesh->SetMaterialData(pair.second.first, pair.second.second); } diff --git a/src/Nazara/Core/Hash/MD5.cpp b/src/Nazara/Core/Hash/MD5.cpp index 769ccb1a8..b0f25d684 100644 --- a/src/Nazara/Core/Hash/MD5.cpp +++ b/src/Nazara/Core/Hash/MD5.cpp @@ -101,7 +101,7 @@ namespace Nz { struct HashMD5_state { - UInt32 count[2]; /* message length in bits, lsw first */ + std::size_t count[2]; /* message length in bits, lsw first */ UInt32 abcd[4]; /* digest buffer */ UInt8 buf[64]; /* accumulate block */ }; @@ -280,9 +280,9 @@ namespace Nz void HashMD5::Append(const UInt8* data, std::size_t len) { const UInt8 *p = data; - int left = len; + std::size_t left = len; int offset = (m_state->count[0] >> 3) & 63; - UInt32 nbits = len << 3; + std::size_t nbits = len << 3; if (len <= 0) return; @@ -296,7 +296,7 @@ namespace Nz /* Process an initial partial block. */ if (offset) { - int copy = (offset + len > 64 ? 64 - offset : len); + std::size_t copy = (offset + len > 64 ? 64 - offset : len); std::memcpy(m_state->buf + offset, p, copy); if (offset + copy < 64) diff --git a/src/Nazara/Core/Hash/Whirlpool.cpp b/src/Nazara/Core/Hash/Whirlpool.cpp index 12b3e2a1b..e01fb4f4f 100644 --- a/src/Nazara/Core/Hash/Whirlpool.cpp +++ b/src/Nazara/Core/Hash/Whirlpool.cpp @@ -75,8 +75,8 @@ namespace Nz { struct HashWhirlpool_state { - int bufferBits; // current number of bits on the buffer */ - int bufferPos; // current (possibly incomplete) byte slot on the buffer */ + std::size_t bufferBits; // current number of bits on the buffer */ + std::size_t bufferPos; // current (possibly incomplete) byte slot on the buffer */ UInt8 bitLength[32]; // global number of hashed bits (256-bit counter) */ UInt8 buffer[64]; // buffer of data to hash */ UInt64 hash[8]; // the hashing state */ @@ -877,8 +877,8 @@ namespace Nz UInt32 b; UInt8* buffer = m_state->buffer; UInt8* bitLength = m_state->bitLength; - int bufferBits = m_state->bufferBits; - int bufferPos = m_state->bufferPos; + std::size_t bufferBits = m_state->bufferBits; + std::size_t bufferPos = m_state->bufferPos; // tally the length of the added data UInt64 value = len; @@ -968,8 +968,8 @@ namespace Nz UInt8 *buffer = m_state->buffer; UInt8 *bitLength = m_state->bitLength; - int bufferBits = m_state->bufferBits; - int bufferPos = m_state->bufferPos; + std::size_t bufferBits = m_state->bufferBits; + std::size_t bufferPos = m_state->bufferPos; UInt8 *digest = result; // append a '1'-bit diff --git a/src/Nazara/Core/String.cpp b/src/Nazara/Core/String.cpp index 7ea8c7df2..98a8b882c 100644 --- a/src/Nazara/Core/String.cpp +++ b/src/Nazara/Core/String.cpp @@ -2115,7 +2115,7 @@ namespace Nz return ptr - m_sharedString->string.get(); } - catch (utf8::not_enough_room& e) + catch (utf8::not_enough_room& /*e*/) { // Returns npos } diff --git a/src/Nazara/Graphics/ForwardRenderTechnique.cpp b/src/Nazara/Graphics/ForwardRenderTechnique.cpp index 543587b3d..2f9319ca0 100644 --- a/src/Nazara/Graphics/ForwardRenderTechnique.cpp +++ b/src/Nazara/Graphics/ForwardRenderTechnique.cpp @@ -33,8 +33,8 @@ namespace Nz Vector2f uv; }; - std::size_t s_maxQuads = std::numeric_limits::max() / 6; - std::size_t s_vertexBufferSize = 4 * 1024 * 1024; // 4 MiB + UInt32 s_maxQuads = std::numeric_limits::max() / 6; + UInt32 s_vertexBufferSize = 4 * 1024 * 1024; // 4 MiB } /*! diff --git a/src/Nazara/Physics2D/PhysWorld2D.cpp b/src/Nazara/Physics2D/PhysWorld2D.cpp index 0e22ae0af..cbf098786 100644 --- a/src/Nazara/Physics2D/PhysWorld2D.cpp +++ b/src/Nazara/Physics2D/PhysWorld2D.cpp @@ -62,9 +62,9 @@ namespace Nz if (cpShape* shape = cpSpacePointQueryNearest(m_handle, { from.x, from.y }, maxDistance, filter, &queryInfo)) { - result->closestPoint.Set(queryInfo.point.x, queryInfo.point.y); - result->distance = queryInfo.distance; - result->fraction.Set(queryInfo.gradient.x, queryInfo.gradient.y); + result->closestPoint.Set(Nz::Vector2(queryInfo.point.x, queryInfo.point.y)); + result->distance = float(queryInfo.distance); + result->fraction.Set(Nz::Vector2(queryInfo.gradient.x, queryInfo.gradient.y)); result->nearestBody = static_cast(cpShapeGetUserData(shape)); return true; @@ -90,9 +90,9 @@ namespace Nz ResultType results = static_cast(data); RaycastHit hitInfo; - hitInfo.fraction = alpha; - hitInfo.hitNormal.Set(normal.x, normal.y); - hitInfo.hitPos.Set(point.x, point.y); + hitInfo.fraction = float(alpha); + hitInfo.hitNormal.Set(Nz::Vector2(normal.x, normal.y)); + hitInfo.hitPos.Set(Nz::Vector2(point.x, point.y)); hitInfo.nearestBody = static_cast(cpShapeGetUserData(shape)); results->emplace_back(std::move(hitInfo)); @@ -116,9 +116,9 @@ namespace Nz if (cpShape* shape = cpSpaceSegmentQueryFirst(m_handle, { from.x, from.y }, { to.x, to.y }, radius, filter, &queryInfo)) { - hitInfo->fraction = queryInfo.alpha; - hitInfo->hitNormal.Set(queryInfo.normal.x, queryInfo.normal.y); - hitInfo->hitPos.Set(queryInfo.point.x, queryInfo.point.y); + hitInfo->fraction = float(queryInfo.alpha); + hitInfo->hitNormal.Set(Nz::Vector2(queryInfo.normal.x, queryInfo.normal.y)); + hitInfo->hitPos.Set(Nz::Vector2(queryInfo.point.x, queryInfo.point.y)); hitInfo->nearestBody = static_cast(cpShapeGetUserData(queryInfo.shape)); return true; diff --git a/src/Nazara/Physics2D/RigidBody2D.cpp b/src/Nazara/Physics2D/RigidBody2D.cpp index f31e9ef54..3b2f927f9 100644 --- a/src/Nazara/Physics2D/RigidBody2D.cpp +++ b/src/Nazara/Physics2D/RigidBody2D.cpp @@ -215,7 +215,7 @@ namespace Nz cpVect vel = cpBodyGetVelocity(m_handle); Destroy(); - Create(mass, moment); + Create(float(mass), float(moment)); cpBodySetAngle(m_handle, rot); cpBodySetPosition(m_handle, pos); diff --git a/src/Nazara/Utility/Formats/MD5MeshLoader.cpp b/src/Nazara/Utility/Formats/MD5MeshLoader.cpp index 0168e4ffe..5284d1d2c 100644 --- a/src/Nazara/Utility/Formats/MD5MeshLoader.cpp +++ b/src/Nazara/Utility/Formats/MD5MeshLoader.cpp @@ -91,8 +91,8 @@ namespace Nz bool largeIndices = (vertexCount > std::numeric_limits::max()); - IndexBufferRef indexBuffer = IndexBuffer::New(largeIndices, indexCount, parameters.storage, 0); - VertexBufferRef vertexBuffer = VertexBuffer::New(VertexDeclaration::Get(VertexLayout_XYZ_Normal_UV_Tangent_Skinning), vertexCount, parameters.storage, 0); + IndexBufferRef indexBuffer = IndexBuffer::New(largeIndices, UInt32(indexCount), parameters.storage, 0); + VertexBufferRef vertexBuffer = VertexBuffer::New(VertexDeclaration::Get(VertexLayout_XYZ_Normal_UV_Tangent_Skinning), UInt32(vertexCount), parameters.storage, 0); // Index buffer IndexMapper indexMapper(indexBuffer, BufferAccess_DiscardAndWrite); @@ -236,7 +236,7 @@ namespace Nz // Index buffer bool largeIndices = (vertexCount > std::numeric_limits::max()); - IndexBufferRef indexBuffer = IndexBuffer::New(largeIndices, indexCount, parameters.storage, 0); + IndexBufferRef indexBuffer = IndexBuffer::New(largeIndices, UInt32(indexCount), parameters.storage, 0); IndexMapper indexMapper(indexBuffer, BufferAccess_DiscardAndWrite); IndexIterator index = indexMapper.begin(); @@ -251,7 +251,7 @@ namespace Nz indexMapper.Unmap(); // Vertex buffer - VertexBufferRef vertexBuffer = VertexBuffer::New(VertexDeclaration::Get(VertexLayout_XYZ_Normal_UV_Tangent), vertexCount, parameters.storage, 0); + VertexBufferRef vertexBuffer = VertexBuffer::New(VertexDeclaration::Get(VertexLayout_XYZ_Normal_UV_Tangent), UInt32(vertexCount), parameters.storage, 0); BufferMapper vertexMapper(vertexBuffer, BufferAccess_WriteOnly); MeshVertex* vertices = static_cast(vertexMapper.GetPointer()); diff --git a/src/Nazara/Utility/Formats/OBJLoader.cpp b/src/Nazara/Utility/Formats/OBJLoader.cpp index 951892085..563300aaf 100644 --- a/src/Nazara/Utility/Formats/OBJLoader.cpp +++ b/src/Nazara/Utility/Formats/OBJLoader.cpp @@ -233,8 +233,8 @@ namespace Nz } // Création des buffers - IndexBufferRef indexBuffer = IndexBuffer::New(vertexCount > std::numeric_limits::max(), indices.size(), parameters.storage, 0); - VertexBufferRef vertexBuffer = VertexBuffer::New(VertexDeclaration::Get(VertexLayout_XYZ_Normal_UV_Tangent), vertexCount, parameters.storage, 0); + IndexBufferRef indexBuffer = IndexBuffer::New(vertexCount > std::numeric_limits::max(), UInt32(indices.size()), parameters.storage, 0); + VertexBufferRef vertexBuffer = VertexBuffer::New(VertexDeclaration::Get(VertexLayout_XYZ_Normal_UV_Tangent), UInt32(vertexCount), parameters.storage, 0); // Remplissage des indices IndexMapper indexMapper(indexBuffer, BufferAccess_WriteOnly); diff --git a/src/Nazara/Utility/Formats/OBJParser.cpp b/src/Nazara/Utility/Formats/OBJParser.cpp index ab1cbdbfb..f85251f57 100644 --- a/src/Nazara/Utility/Formats/OBJParser.cpp +++ b/src/Nazara/Utility/Formats/OBJParser.cpp @@ -228,7 +228,7 @@ namespace Nz if (p < 0) { - p += m_positions.size() - 1; + p += static_cast(m_positions.size() - 1); if (p < 0) { Error("Vertex index out of range (" + String::Number(p) + " < 0"); @@ -239,7 +239,7 @@ namespace Nz if (n < 0) { - n += m_normals.size() - 1; + n += static_cast(m_normals.size() - 1); if (n < 0) { Error("Normal index out of range (" + String::Number(n) + " < 0"); @@ -250,7 +250,7 @@ namespace Nz if (t < 0) { - t += m_texCoords.size() - 1; + t += static_cast(m_texCoords.size() - 1); if (t < 0) { Error("Texture coordinates index out of range (" + String::Number(t) + " < 0"); diff --git a/src/Nazara/Utility/IndexMapper.cpp b/src/Nazara/Utility/IndexMapper.cpp index 7ddcd9a08..78fdd36a0 100644 --- a/src/Nazara/Utility/IndexMapper.cpp +++ b/src/Nazara/Utility/IndexMapper.cpp @@ -12,38 +12,38 @@ namespace Nz { namespace { - UInt32 GetterSequential(const void* buffer, unsigned int i) + UInt32 GetterSequential(const void* buffer, std::size_t i) { NazaraUnused(buffer); - return i; + return static_cast(i); } - UInt32 Getter16(const void* buffer, unsigned int i) + UInt32 Getter16(const void* buffer, std::size_t i) { const UInt16* ptr = static_cast(buffer); return ptr[i]; } - UInt32 Getter32(const void* buffer, unsigned int i) + UInt32 Getter32(const void* buffer, std::size_t i) { const UInt32* ptr = static_cast(buffer); return ptr[i]; } - void Setter16(void* buffer, unsigned int i, UInt32 value) + void Setter16(void* buffer, std::size_t i, UInt32 value) { UInt16* ptr = static_cast(buffer); ptr[i] = static_cast(value); } - void Setter32(void* buffer, unsigned int i, UInt32 value) + void Setter32(void* buffer, std::size_t i, UInt32 value) { UInt32* ptr = static_cast(buffer); ptr[i] = value; } - void SetterError(void*, unsigned int, UInt32) + void SetterError(void*, std::size_t, UInt32) { NazaraError("Index buffer opened with read-only access"); } @@ -113,15 +113,9 @@ namespace Nz { } - UInt32 IndexMapper::Get(unsigned int i) const + UInt32 IndexMapper::Get(std::size_t i) const { - #if NAZARA_UTILITY_SAFE - if (i >= m_indexCount) - { - NazaraError("Index out of range (" + String::Number(i) + " >= " + String::Number(m_indexCount) + ')'); - return 0; - } - #endif + NazaraAssert(i < m_indexCount, "Index out of range"); return m_getter(m_mapper.GetPointer(), i); } @@ -131,20 +125,14 @@ namespace Nz return m_mapper.GetBuffer(); } - unsigned int IndexMapper::GetIndexCount() const + std::size_t IndexMapper::GetIndexCount() const { return m_indexCount; } - void IndexMapper::Set(unsigned int i, UInt32 value) + void IndexMapper::Set(std::size_t i, UInt32 value) { - #if NAZARA_UTILITY_SAFE - if (i >= m_indexCount) - { - NazaraError("Index out of range (" + String::Number(i) + " >= " + String::Number(m_indexCount) + ')'); - return; - } - #endif + NazaraAssert(i < m_indexCount, "Index out of range"); m_setter(m_mapper.GetPointer(), i, value); } diff --git a/src/Nazara/Utility/Mesh.cpp b/src/Nazara/Utility/Mesh.cpp index 27c1bd186..b6985714e 100644 --- a/src/Nazara/Utility/Mesh.cpp +++ b/src/Nazara/Utility/Mesh.cpp @@ -79,7 +79,7 @@ namespace Nz NazaraAssert(subMesh, "Invalid submesh"); NazaraAssert(subMesh->GetAnimationType() == m_impl->animationType, "Submesh animation type doesn't match mesh animation type"); - m_impl->subMeshes.push_back(subMesh); + m_impl->subMeshes.emplace_back(subMesh); InvalidateAABB(); } @@ -92,10 +92,10 @@ namespace Nz NazaraAssert(subMesh, "Invalid submesh"); NazaraAssert(subMesh->GetAnimationType() == m_impl->animationType, "Submesh animation type doesn't match mesh animation type"); - UInt32 index = m_impl->subMeshes.size(); + std::size_t index = m_impl->subMeshes.size(); - m_impl->subMeshes.push_back(subMesh); - m_impl->subMeshMap[identifier] = index; + m_impl->subMeshes.emplace_back(subMesh); + m_impl->subMeshMap[identifier] = static_cast(index); InvalidateAABB(); } @@ -349,11 +349,11 @@ namespace Nz if (!m_impl->aabbUpdated) { - UInt32 subMeshCount = m_impl->subMeshes.size(); + std::size_t subMeshCount = m_impl->subMeshes.size(); if (subMeshCount > 0) { m_impl->aabb.Set(m_impl->subMeshes[0]->GetAABB()); - for (UInt32 i = 1; i < subMeshCount; ++i) + for (std::size_t i = 1; i < subMeshCount; ++i) m_impl->aabb.ExtendTo(m_impl->subMeshes[i]->GetAABB()); } else @@ -407,7 +407,7 @@ namespace Nz { NazaraAssert(m_impl, "Mesh should be created first"); - return m_impl->materialData.size(); + return static_cast(m_impl->materialData.size()); } Skeleton* Mesh::GetSkeleton() @@ -466,7 +466,7 @@ namespace Nz { NazaraAssert(m_impl, "Mesh should be created first"); - return m_impl->subMeshes.size(); + return static_cast(m_impl->subMeshes.size()); } UInt32 Mesh::GetSubMeshIndex(const String& identifier) const diff --git a/src/Nazara/Utility/SimpleTextDrawer.cpp b/src/Nazara/Utility/SimpleTextDrawer.cpp index d7a1bc713..1901a6ca2 100644 --- a/src/Nazara/Utility/SimpleTextDrawer.cpp +++ b/src/Nazara/Utility/SimpleTextDrawer.cpp @@ -243,7 +243,7 @@ namespace Nz m_workingBounds.MakeZero(); //< Compute bounds as float to speedup bounds computation (as casting between floats and integers is costly) if (m_font) - m_lines.emplace_back(Line{Rectf(0.f, 0.f, 0.f, m_font->GetSizeInfo(m_characterSize).lineHeight), 0}); + m_lines.emplace_back(Line{Rectf(0.f, 0.f, 0.f, float(m_font->GetSizeInfo(m_characterSize).lineHeight)), 0}); else m_lines.emplace_back(Line{Rectf::Zero(), 0}); } @@ -354,7 +354,7 @@ namespace Nz { glyph.atlas = nullptr; - glyph.bounds.Set(m_drawPos.x, m_drawPos.y, float(advance), sizeInfo.lineHeight); + glyph.bounds.Set(m_drawPos.x, m_drawPos.y, float(advance), float(sizeInfo.lineHeight)); glyph.corners[0].Set(glyph.bounds.GetCorner(RectCorner_LeftTop)); glyph.corners[1].Set(glyph.bounds.GetCorner(RectCorner_RightTop)); @@ -377,7 +377,7 @@ namespace Nz m_drawPos.x = 0; m_drawPos.y += sizeInfo.lineHeight; - m_lines.emplace_back(Line{Rectf(0.f, sizeInfo.lineHeight * m_lines.size(), 0.f, sizeInfo.lineHeight), m_glyphs.size() + 1}); + m_lines.emplace_back(Line{Rectf(0.f, float(sizeInfo.lineHeight * m_lines.size()), 0.f, float(sizeInfo.lineHeight)), m_glyphs.size() + 1}); break; } } diff --git a/src/Nazara/Utility/Skeleton.cpp b/src/Nazara/Utility/Skeleton.cpp index 7d33ad9bd..480522b4e 100644 --- a/src/Nazara/Utility/Skeleton.cpp +++ b/src/Nazara/Utility/Skeleton.cpp @@ -72,12 +72,12 @@ namespace Nz if (!m_impl->aabbUpdated) { - UInt32 jointCount = m_impl->joints.size(); + std::size_t jointCount = m_impl->joints.size(); if (jointCount > 0) { Vector3f pos = m_impl->joints[0].GetPosition(); m_impl->aabb.Set(pos.x, pos.y, pos.z, 0.f, 0.f, 0.f); - for (UInt32 i = 1; i < jointCount; ++i) + for (std::size_t i = 1; i < jointCount; ++i) m_impl->aabb.ExtendTo(m_impl->joints[i].GetPosition()); } else @@ -219,7 +219,7 @@ namespace Nz } #endif - return m_impl->joints.size(); + return static_cast(m_impl->joints.size()); } int Skeleton::GetJointIndex(const String& jointName) const @@ -411,16 +411,9 @@ namespace Nz String name = m_impl->joints[i].GetName(); if (!name.IsEmpty()) { - #if NAZARA_UTILITY_SAFE - auto it = m_impl->jointMap.find(name); - if (it != m_impl->jointMap.end()) - { - NazaraWarning("Joint name \"" + name + "\" is already present in joint map for joint #" + String::Number(it->second)); - continue; - } - #endif + NazaraAssert(m_impl->jointMap.find(name) == m_impl->jointMap.end(), "Joint name \"" + name + "\" is already present in joint map"); - m_impl->jointMap[name] = i; + m_impl->jointMap[name] = static_cast(i); } } diff --git a/src/Nazara/Utility/TriangleIterator.cpp b/src/Nazara/Utility/TriangleIterator.cpp index 68f65c0db..ad6dfb2c6 100644 --- a/src/Nazara/Utility/TriangleIterator.cpp +++ b/src/Nazara/Utility/TriangleIterator.cpp @@ -66,18 +66,9 @@ namespace Nz return true; } - UInt32 TriangleIterator::operator[](unsigned int i) const + UInt32 TriangleIterator::operator[](std::size_t i) const { - #if NAZARA_UTILITY_SAFE - if (i >= 3) - { - StringStream ss; - ss << "Index out of range: (" << i << " >= 3)"; - - NazaraError(ss); - throw std::domain_error(ss.ToString()); - } - #endif + NazaraAssert(i < 3, "Index out of range"); return m_triangleIndices[i]; } From d8a2d08e277bab851b872e419a755613a7dfae13 Mon Sep 17 00:00:00 2001 From: Lynix Date: Sat, 22 Apr 2017 15:08:21 +0200 Subject: [PATCH 14/33] UnitTest: Add some checks --- tests/Engine/Math/AlgorithmMath.cpp | 20 ++++++++++++++++++-- tests/SDK/NDK/EntityOwner.cpp | 8 ++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/tests/Engine/Math/AlgorithmMath.cpp b/tests/Engine/Math/AlgorithmMath.cpp index 01413d902..e6dc2218b 100644 --- a/tests/Engine/Math/AlgorithmMath.cpp +++ b/tests/Engine/Math/AlgorithmMath.cpp @@ -1,5 +1,6 @@ #include #include +#include TEST_CASE("Approach", "[MATH][ALGORITHM]") { @@ -48,6 +49,11 @@ TEST_CASE("CountBits", "[MATH][ALGORITHM]") { REQUIRE(Nz::CountBits(0) == 0); } + + SECTION("Number 0xFFFFFFFF has 32 bit set to 1") + { + REQUIRE(Nz::CountBits(0xFFFFFFFF) == 32); + } } TEST_CASE("DegreeToRadian", "[MATH][ALGORITHM]") @@ -205,9 +211,19 @@ TEST_CASE("NumberEquals", "[MATH][ALGORITHM]") CHECK(Nz::NumberEquals(2.35, 2.35, 0.01)); } - SECTION("3 and 4 unsigned should be the same at 1") + SECTION("0 and 4 unsigned should be the same at 1") { - CHECK(Nz::NumberEquals(3U, 4U, 1U)); + CHECK(Nz::NumberEquals(0U, 4U, 4U)); + } + + SECTION("Maximum integer and -1 should not be equal") + { + CHECK_FALSE(Nz::NumberEquals(std::numeric_limits::max(), -1)); + } + + SECTION("Maximum integer and minimum integer should not be equal") + { + CHECK_FALSE(Nz::NumberEquals(std::numeric_limits::max(), std::numeric_limits::min())); } } diff --git a/tests/SDK/NDK/EntityOwner.cpp b/tests/SDK/NDK/EntityOwner.cpp index eb9a29edf..464dce4d6 100644 --- a/tests/SDK/NDK/EntityOwner.cpp +++ b/tests/SDK/NDK/EntityOwner.cpp @@ -24,6 +24,14 @@ SCENARIO("EntityOwner", "[NDK][ENTITYOWNER]") world.Update(1.f); REQUIRE(!entity.IsValid()); } + + THEN("Moving an entity owner works") + { + Ndk::EntityOwner entityOwner2(std::move(entityOwner)); + entityOwner2.Reset(); + world.Update(1.f); + REQUIRE(!entity.IsValid()); + } } } } \ No newline at end of file From 883d7b02a7c1630b3bfe7ac8629184801064de59 Mon Sep 17 00:00:00 2001 From: Lynix Date: Sat, 22 Apr 2017 15:08:43 +0200 Subject: [PATCH 15/33] Math/Algorithm: Optimize NumberEquals unsigned and float cases --- include/Nazara/Math/Algorithm.inl | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/include/Nazara/Math/Algorithm.inl b/include/Nazara/Math/Algorithm.inl index 612cdf952..e50385c78 100644 --- a/include/Nazara/Math/Algorithm.inl +++ b/include/Nazara/Math/Algorithm.inl @@ -98,7 +98,13 @@ namespace Nz return 0; } - template /*constexpr*/ std::enable_if_t::value || !std::is_integral::value, bool> NumberEquals(T a, T b, T maxDifference) + template /*constexpr*/ std::enable_if_t::value, bool> NumberEquals(T a, T b, T maxDifference) + { + T diff = std::abs(a - b); + return diff <= maxDifference; + } + + template /*constexpr*/ std::enable_if_t::value || (!std::is_integral::value && !std::is_floating_point::value), bool> NumberEquals(T a, T b, T maxDifference) { if (b > a) std::swap(a, b); @@ -112,13 +118,8 @@ namespace Nz if (b > a) std::swap(a, b); - if ((b < 0) && (a > std::numeric_limits::max() + b)) - return false; - - if ((b > 0) && (a < std::numeric_limits::min() + b)) - return false; - - return std::abs(a - b) <= maxDifference; + using UnsignedT = std::make_unsigned_t; + return static_cast(a) - static_cast(b) <= static_cast(maxDifference); } } @@ -487,7 +488,7 @@ namespace Nz template constexpr T Lerp(const T& from, const T& to, const T2& interpolation) { - return from + interpolation * (to - from); + return static_cast(from + interpolation * (to - from)); } /*! From f6fe589b03a5a98668f5a5f704bcc0cc7b5a8b25 Mon Sep 17 00:00:00 2001 From: Lynix Date: Sat, 22 Apr 2017 15:33:59 +0200 Subject: [PATCH 16/33] Fix #120 --- src/Nazara/Renderer/Context.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Nazara/Renderer/Context.cpp b/src/Nazara/Renderer/Context.cpp index acc57006e..7fbad6002 100644 --- a/src/Nazara/Renderer/Context.cpp +++ b/src/Nazara/Renderer/Context.cpp @@ -121,9 +121,7 @@ namespace Nz break; default: - // Peut être rajouté par une extension - ss << "Unknown"; - break; + return; //< Block NVidia buffer usage hint for now } ss << '\n'; From e3514db87fe1bd2dd64c60178aff7055cb8c0019 Mon Sep 17 00:00:00 2001 From: Lynix Date: Sat, 22 Apr 2017 17:11:42 +0200 Subject: [PATCH 17/33] Sdk/GraphicsComponent: Add methods to update local matrix and render order --- .../NDK/Components/GraphicsComponent.hpp | 3 +++ .../NDK/Components/GraphicsComponent.inl | 26 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/SDK/include/NDK/Components/GraphicsComponent.hpp b/SDK/include/NDK/Components/GraphicsComponent.hpp index cd62f9b2f..68961b99a 100644 --- a/SDK/include/NDK/Components/GraphicsComponent.hpp +++ b/SDK/include/NDK/Components/GraphicsComponent.hpp @@ -51,6 +51,9 @@ namespace Ndk inline void RemoveFromCullingList(GraphicsComponentCullingList* cullingList) const; + inline void UpdateLocalMatrix(const Nz::InstancedRenderable* instancedRenderable, const Nz::Matrix4f& localMatrix); + inline void UpdateRenderOrder(const Nz::InstancedRenderable* instancedRenderable, int renderOrder); + static ComponentIndex componentIndex; private: diff --git a/SDK/include/NDK/Components/GraphicsComponent.inl b/SDK/include/NDK/Components/GraphicsComponent.inl index e67b6f323..34bd57dd9 100644 --- a/SDK/include/NDK/Components/GraphicsComponent.inl +++ b/SDK/include/NDK/Components/GraphicsComponent.inl @@ -142,6 +142,32 @@ namespace Ndk } } + inline void GraphicsComponent::UpdateLocalMatrix(const Nz::InstancedRenderable* instancedRenderable, const Nz::Matrix4f& localMatrix) + { + for (auto& renderable : m_renderables) + { + if (renderable.renderable == instancedRenderable) + { + renderable.data.localMatrix = localMatrix; + + InvalidateBoundingVolume(); + break; + } + } + } + + inline void GraphicsComponent::UpdateRenderOrder(const Nz::InstancedRenderable* instancedRenderable, int renderOrder) + { + for (auto& renderable : m_renderables) + { + if (renderable.renderable == instancedRenderable) + { + renderable.data.renderOrder = renderOrder; + break; + } + } + } + /*! * \brief Invalidates the bounding volume */ From 52a4a590e1288226dea6f9ed5d8c36c8462f8c52 Mon Sep 17 00:00:00 2001 From: Lynix Date: Sat, 22 Apr 2017 17:13:51 +0200 Subject: [PATCH 18/33] Graphics/Material: Fix depth sorting flag handling for meshes --- .../NDK/Components/CameraComponent.hpp | 2 +- .../NDK/Components/CameraComponent.inl | 10 -- SDK/src/NDK/Components/CameraComponent.cpp | 11 ++ .../Nazara/Graphics/AbstractRenderQueue.hpp | 2 +- include/Nazara/Graphics/AbstractViewer.hpp | 1 + .../Nazara/Graphics/DeferredRenderQueue.hpp | 2 +- include/Nazara/Graphics/DepthRenderQueue.hpp | 2 +- .../Nazara/Graphics/ForwardRenderQueue.hpp | 16 +- src/Nazara/Graphics/DeferredRenderQueue.cpp | 6 +- src/Nazara/Graphics/DepthRenderQueue.cpp | 2 +- src/Nazara/Graphics/ForwardRenderQueue.cpp | 139 +++++++++++------- .../Graphics/ForwardRenderTechnique.cpp | 10 +- src/Nazara/Graphics/MaterialPipeline.cpp | 2 + 13 files changed, 122 insertions(+), 83 deletions(-) diff --git a/SDK/include/NDK/Components/CameraComponent.hpp b/SDK/include/NDK/Components/CameraComponent.hpp index 04859b1de..f4a29f2e4 100644 --- a/SDK/include/NDK/Components/CameraComponent.hpp +++ b/SDK/include/NDK/Components/CameraComponent.hpp @@ -44,7 +44,7 @@ namespace Ndk const Nz::Frustumf& GetFrustum() const override; inline unsigned int GetLayer() const; const Nz::Matrix4f& GetProjectionMatrix() const override; - inline Nz::ProjectionType GetProjectionType() const; + Nz::ProjectionType GetProjectionType() const override; inline const Nz::Vector2f& GetSize() const; const Nz::RenderTarget* GetTarget() const override; inline const Nz::Rectf& GetTargetRegion() const; diff --git a/SDK/include/NDK/Components/CameraComponent.inl b/SDK/include/NDK/Components/CameraComponent.inl index 33c63bfe3..1018f9f3d 100644 --- a/SDK/include/NDK/Components/CameraComponent.inl +++ b/SDK/include/NDK/Components/CameraComponent.inl @@ -117,16 +117,6 @@ namespace Ndk return m_layer; } - /*! - * \brief Gets the projection type of the camera - * \return Projection type of the camera - */ - - inline Nz::ProjectionType CameraComponent::GetProjectionType() const - { - return m_projectionType; - } - /*! * \brief Gets the size of the camera * \return Size of the camera diff --git a/SDK/src/NDK/Components/CameraComponent.cpp b/SDK/src/NDK/Components/CameraComponent.cpp index 13b6bdd2b..2441dd7a8 100644 --- a/SDK/src/NDK/Components/CameraComponent.cpp +++ b/SDK/src/NDK/Components/CameraComponent.cpp @@ -93,6 +93,17 @@ namespace Ndk return m_projectionMatrix; } + + /*! + * \brief Gets the projection type of the camera + * \return Projection type of the camera + */ + Nz::ProjectionType CameraComponent::GetProjectionType() const + { + return m_projectionType; + } + + /*! * \brief Gets the target of the camera * \return A constant reference to the render target of the camera diff --git a/include/Nazara/Graphics/AbstractRenderQueue.hpp b/include/Nazara/Graphics/AbstractRenderQueue.hpp index f0074cd50..0e3b94f2f 100644 --- a/include/Nazara/Graphics/AbstractRenderQueue.hpp +++ b/include/Nazara/Graphics/AbstractRenderQueue.hpp @@ -51,7 +51,7 @@ namespace Nz virtual void AddMesh(int renderOrder, const Material* material, const MeshData& meshData, const Boxf& meshAABB, const Matrix4f& transformMatrix) = 0; virtual void AddPointLight(const PointLight& light); virtual void AddSpotLight(const SpotLight& light); - virtual void AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, unsigned int spriteCount, const Texture* overlay = nullptr) = 0; + virtual void AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, std::size_t spriteCount, const Texture* overlay = nullptr) = 0; virtual void Clear(bool fully = false); diff --git a/include/Nazara/Graphics/AbstractViewer.hpp b/include/Nazara/Graphics/AbstractViewer.hpp index 9bef56373..21a62a890 100644 --- a/include/Nazara/Graphics/AbstractViewer.hpp +++ b/include/Nazara/Graphics/AbstractViewer.hpp @@ -33,6 +33,7 @@ namespace Nz virtual Vector3f GetForward() const = 0; virtual const Frustumf& GetFrustum() const = 0; virtual const Matrix4f& GetProjectionMatrix() const = 0; + virtual Nz::ProjectionType GetProjectionType() const = 0; virtual const RenderTarget* GetTarget() const = 0; virtual const Matrix4f& GetViewMatrix() const = 0; virtual const Recti& GetViewport() const = 0; diff --git a/include/Nazara/Graphics/DeferredRenderQueue.hpp b/include/Nazara/Graphics/DeferredRenderQueue.hpp index 982469873..5f9f7538e 100644 --- a/include/Nazara/Graphics/DeferredRenderQueue.hpp +++ b/include/Nazara/Graphics/DeferredRenderQueue.hpp @@ -37,7 +37,7 @@ namespace Nz void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr alphaPtr) override; void AddDrawable(int renderOrder, const Drawable* drawable) override; void AddMesh(int renderOrder, const Material* material, const MeshData& meshData, const Boxf& meshAABB, const Matrix4f& transformMatrix) override; - void AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, unsigned int spriteCount, const Texture* overlay = nullptr) override; + void AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, std::size_t spriteCount, const Texture* overlay = nullptr) override; void Clear(bool fully = false) override; diff --git a/include/Nazara/Graphics/DepthRenderQueue.hpp b/include/Nazara/Graphics/DepthRenderQueue.hpp index ed7c9f3c3..871cefa66 100644 --- a/include/Nazara/Graphics/DepthRenderQueue.hpp +++ b/include/Nazara/Graphics/DepthRenderQueue.hpp @@ -38,7 +38,7 @@ namespace Nz void AddMesh(int renderOrder, const Material* material, const MeshData& meshData, const Boxf& meshAABB, const Matrix4f& transformMatrix) override; void AddPointLight(const PointLight& light) override; void AddSpotLight(const SpotLight& light) override; - void AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, unsigned int spriteCount, const Texture* overlay = nullptr) override; + void AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, std::size_t spriteCount, const Texture* overlay = nullptr) override; private: inline bool IsMaterialSuitable(const Material* material) const; diff --git a/include/Nazara/Graphics/ForwardRenderQueue.hpp b/include/Nazara/Graphics/ForwardRenderQueue.hpp index 218fd312d..b081641c1 100644 --- a/include/Nazara/Graphics/ForwardRenderQueue.hpp +++ b/include/Nazara/Graphics/ForwardRenderQueue.hpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -41,7 +42,7 @@ namespace Nz void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr alphaPtr) override; void AddDrawable(int renderOrder, const Drawable* drawable) override; void AddMesh(int renderOrder, const Material* material, const MeshData& meshData, const Boxf& meshAABB, const Matrix4f& transformMatrix) override; - void AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, unsigned int spriteCount, const Texture* overlay = nullptr) override; + void AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, std::size_t spriteCount, const Texture* overlay = nullptr) override; void Clear(bool fully = false) override; @@ -156,7 +157,7 @@ namespace Nz { Matrix4f transformMatrix; MeshData meshData; - Spheref squaredBoundingSphere; + Spheref obbSphere; const Material* material; }; @@ -165,10 +166,11 @@ namespace Nz struct Layer { BillboardPipelineBatches billboards; - SpritePipelineBatches basicSprites; + SpritePipelineBatches opaqueSprites; + SpritePipelineBatches depthSortedSprites; MeshPipelineBatches opaqueModels; - TransparentModelContainer transparentModels; - std::vector transparentModelData; + TransparentModelContainer depthSortedMeshes; + std::vector depthSortedMeshData; std::vector otherDrawables; unsigned int clearCount = 0; }; @@ -179,6 +181,10 @@ namespace Nz BillboardData* GetBillboardData(int renderOrder, const Material* material, unsigned int count); Layer& GetLayer(int i); ///TODO: Inline + void SortBillboards(Layer& layer, const Planef& nearPlane); + void SortForOrthographic(const AbstractViewer* viewer); + void SortForPerspective(const AbstractViewer* viewer); + void OnIndexBufferInvalidation(const IndexBuffer* indexBuffer); void OnMaterialInvalidation(const Material* material); void OnTextureInvalidation(const Texture* texture); diff --git a/src/Nazara/Graphics/DeferredRenderQueue.cpp b/src/Nazara/Graphics/DeferredRenderQueue.cpp index 22ead6ec0..a0455fe11 100644 --- a/src/Nazara/Graphics/DeferredRenderQueue.cpp +++ b/src/Nazara/Graphics/DeferredRenderQueue.cpp @@ -191,8 +191,8 @@ namespace Nz void DeferredRenderQueue::AddMesh(int renderOrder, const Material* material, const MeshData& meshData, const Boxf& meshAABB, const Matrix4f& transformMatrix) { - if (material->IsBlendingEnabled()) - // One transparent material ? I don't like it, go see if I'm in the forward queue + if (material->IsBlendingEnabled() || material->IsDepthSortingEnabled()) //< Fixme: Deferred Shading should be able to handle depth sorting + // Deferred Shading cannot handle blended objects, put them in the forward list m_forwardQueue->AddMesh(renderOrder, material, meshData, meshAABB, transformMatrix); else { @@ -254,7 +254,7 @@ namespace Nz * \param overlay Texture of the sprites */ - void DeferredRenderQueue::AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, unsigned int spriteCount, const Texture* overlay) + void DeferredRenderQueue::AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, std::size_t spriteCount, const Texture* overlay) { m_forwardQueue->AddSprites(renderOrder, material, vertices, spriteCount, overlay); } diff --git a/src/Nazara/Graphics/DepthRenderQueue.cpp b/src/Nazara/Graphics/DepthRenderQueue.cpp index 46096e33f..a2acdf4bb 100644 --- a/src/Nazara/Graphics/DepthRenderQueue.cpp +++ b/src/Nazara/Graphics/DepthRenderQueue.cpp @@ -351,7 +351,7 @@ namespace Nz * \remark Produces a NazaraAssert if material is invalid */ - void DepthRenderQueue::AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, unsigned int spriteCount, const Texture* overlay) + void DepthRenderQueue::AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, std::size_t spriteCount, const Texture* overlay) { NazaraAssert(material, "Invalid material"); NazaraUnused(renderOrder); diff --git a/src/Nazara/Graphics/ForwardRenderQueue.cpp b/src/Nazara/Graphics/ForwardRenderQueue.cpp index 8eb70fe7e..c594157b9 100644 --- a/src/Nazara/Graphics/ForwardRenderQueue.cpp +++ b/src/Nazara/Graphics/ForwardRenderQueue.cpp @@ -376,23 +376,23 @@ namespace Nz { NazaraAssert(material, "Invalid material"); - if (material->IsBlendingEnabled()) + if (material->IsDepthSortingEnabled()) { Layer& currentLayer = GetLayer(renderOrder); - auto& transparentModels = currentLayer.transparentModels; - auto& transparentModelData = currentLayer.transparentModelData; + auto& transparentMeshes = currentLayer.depthSortedMeshes; + auto& transparentData = currentLayer.depthSortedMeshData; - // The material is transparent, we must draw this mesh using another way (after the rendering of opages objects while sorting them) - std::size_t index = transparentModelData.size(); - transparentModelData.resize(index+1); + // The material is marked for depth sorting, we must draw this mesh using another way (after the rendering of opaques objects while sorting them) + std::size_t index = transparentData.size(); + transparentData.resize(index+1); - TransparentModelData& data = transparentModelData.back(); + TransparentModelData& data = transparentData.back(); data.material = material; data.meshData = meshData; - data.squaredBoundingSphere = Spheref(transformMatrix.GetTranslation() + meshAABB.GetCenter(), meshAABB.GetSquaredRadius()); + data.obbSphere = Spheref(transformMatrix.GetTranslation() + meshAABB.GetCenter(), meshAABB.GetSquaredRadius()); data.transformMatrix = transformMatrix; - transparentModels.push_back(index); + transparentMeshes.push_back(index); } else { @@ -457,12 +457,12 @@ namespace Nz * * \remark Produces a NazaraAssert if material is invalid */ - void ForwardRenderQueue::AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, unsigned int spriteCount, const Texture* overlay) + void ForwardRenderQueue::AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, std::size_t spriteCount, const Texture* overlay) { NazaraAssert(material, "Invalid material"); Layer& currentLayer = GetLayer(renderOrder); - SpritePipelineBatches& basicSprites = currentLayer.basicSprites; + SpritePipelineBatches& basicSprites = currentLayer.opaqueSprites; const MaterialPipeline* materialPipeline = material->GetPipeline(); @@ -545,7 +545,7 @@ namespace Nz pipelineEntry.enabled = false; } - for (auto& pipelinePair : layer.basicSprites) + for (auto& pipelinePair : layer.opaqueSprites) { auto& pipelineEntry = pipelinePair.second; @@ -597,8 +597,8 @@ namespace Nz } layer.otherDrawables.clear(); - layer.transparentModels.clear(); - layer.transparentModelData.clear(); + layer.depthSortedMeshes.clear(); + layer.depthSortedMeshData.clear(); ++it; } } @@ -613,44 +613,10 @@ namespace Nz void ForwardRenderQueue::Sort(const AbstractViewer* viewer) { - Planef nearPlane = viewer->GetFrustum().GetPlane(FrustumPlane_Near); - Vector3f viewerPos = viewer->GetEyePosition(); - Vector3f viewerNormal = viewer->GetForward(); - - for (auto& pair : layers) - { - Layer& layer = pair.second; - - std::sort(layer.transparentModels.begin(), layer.transparentModels.end(), [&layer, &nearPlane, &viewerNormal] (std::size_t index1, std::size_t index2) - { - const Spheref& sphere1 = layer.transparentModelData[index1].squaredBoundingSphere; - const Spheref& sphere2 = layer.transparentModelData[index2].squaredBoundingSphere; - - Vector3f position1 = sphere1.GetNegativeVertex(viewerNormal); - Vector3f position2 = sphere2.GetNegativeVertex(viewerNormal); - - return nearPlane.Distance(position1) > nearPlane.Distance(position2); - }); - - for (auto& pipelinePair : layer.billboards) - { - for (auto& matPair : pipelinePair.second.materialMap) - { - const Material* mat = matPair.first; - - if (mat->IsDepthSortingEnabled()) - { - BatchedBillboardEntry& entry = matPair.second; - auto& billboardVector = entry.billboards; - - std::sort(billboardVector.begin(), billboardVector.end(), [&viewerPos] (const BillboardData& data1, const BillboardData& data2) - { - return viewerPos.SquaredDistance(data1.center) > viewerPos.SquaredDistance(data2.center); - }); - } - } - } - } + if (viewer->GetProjectionType() == ProjectionType_Orthogonal) + SortForOrthographic(viewer); + else + SortForPerspective(viewer); } /*! @@ -715,12 +681,75 @@ namespace Nz return layer; } + void ForwardRenderQueue::SortBillboards(Layer& layer, const Planef& nearPlane) + { + for (auto& pipelinePair : layer.billboards) + { + for (auto& matPair : pipelinePair.second.materialMap) + { + const Material* mat = matPair.first; + + if (mat->IsDepthSortingEnabled()) + { + BatchedBillboardEntry& entry = matPair.second; + auto& billboardVector = entry.billboards; + + std::sort(billboardVector.begin(), billboardVector.end(), [&nearPlane] (const BillboardData& data1, const BillboardData& data2) + { + return nearPlane.Distance(data1.center) > nearPlane.Distance(data2.center); + }); + } + } + } + } + + void ForwardRenderQueue::SortForOrthographic(const AbstractViewer * viewer) + { + Planef nearPlane = viewer->GetFrustum().GetPlane(FrustumPlane_Near); + Vector3f viewerPos = viewer->GetEyePosition(); + + for (auto& pair : layers) + { + Layer& layer = pair.second; + + std::sort(layer.depthSortedMeshes.begin(), layer.depthSortedMeshes.end(), [&layer, &nearPlane] (std::size_t index1, std::size_t index2) + { + const Spheref& sphere1 = layer.depthSortedMeshData[index1].obbSphere; + const Spheref& sphere2 = layer.depthSortedMeshData[index2].obbSphere; + + return nearPlane.Distance(sphere1.GetPosition()) > nearPlane.Distance(sphere2.GetPosition()); + }); + + SortBillboards(layer, nearPlane); + } + } + + void ForwardRenderQueue::SortForPerspective(const AbstractViewer* viewer) + { + Planef nearPlane = viewer->GetFrustum().GetPlane(FrustumPlane_Near); + Vector3f viewerPos = viewer->GetEyePosition(); + + for (auto& pair : layers) + { + Layer& layer = pair.second; + + std::sort(layer.depthSortedMeshes.begin(), layer.depthSortedMeshes.end(), [&layer, &viewerPos] (std::size_t index1, std::size_t index2) + { + const Spheref& sphere1 = layer.depthSortedMeshData[index1].obbSphere; + const Spheref& sphere2 = layer.depthSortedMeshData[index2].obbSphere; + + return viewerPos.SquaredDistance(sphere1.GetPosition()) > viewerPos.SquaredDistance(sphere2.GetPosition()); + }); + + SortBillboards(layer, nearPlane); + } + } + /*! * \brief Handle the invalidation of an index buffer * * \param indexBuffer Index buffer being invalidated */ - void ForwardRenderQueue::OnIndexBufferInvalidation(const IndexBuffer* indexBuffer) { for (auto& pair : layers) @@ -757,7 +786,7 @@ namespace Nz { Layer& layer = pair.second; - for (auto& pipelineEntry : layer.basicSprites) + for (auto& pipelineEntry : layer.opaqueSprites) pipelineEntry.second.materialMap.erase(material); for (auto& pipelineEntry : layer.billboards) @@ -779,7 +808,7 @@ namespace Nz for (auto& pair : layers) { Layer& layer = pair.second; - for (auto& pipelineEntry : layer.basicSprites) + for (auto& pipelineEntry : layer.opaqueSprites) { for (auto& materialEntry : pipelineEntry.second.materialMap) materialEntry.second.overlayMap.erase(texture); diff --git a/src/Nazara/Graphics/ForwardRenderTechnique.cpp b/src/Nazara/Graphics/ForwardRenderTechnique.cpp index 2f9319ca0..910e2bd1f 100644 --- a/src/Nazara/Graphics/ForwardRenderTechnique.cpp +++ b/src/Nazara/Graphics/ForwardRenderTechnique.cpp @@ -101,7 +101,7 @@ namespace Nz if (!layer.opaqueModels.empty()) DrawOpaqueModels(sceneData, layer); - if (!layer.transparentModels.empty()) + if (!layer.depthSortedMeshes.empty()) DrawTransparentModels(sceneData, layer); if (!layer.basicSprites.empty()) @@ -796,9 +796,9 @@ namespace Nz const ShaderUniforms* shaderUniforms = nullptr; unsigned int lightCount = 0; - for (unsigned int index : layer.transparentModels) + for (unsigned int index : layer.depthSortedMeshes) { - const ForwardRenderQueue::TransparentModelData& modelData = layer.transparentModelData[index]; + const ForwardRenderQueue::TransparentModelData& modelData = layer.depthSortedMeshData[index]; // Material const Material* material = modelData.material; @@ -865,8 +865,8 @@ namespace Nz if (shaderUniforms->hasLightUniforms && lightCount < NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS) { // Compute the closest lights - Vector3f position = matrix.GetTranslation() + modelData.squaredBoundingSphere.GetPosition(); - float radius = modelData.squaredBoundingSphere.radius; + Vector3f position = matrix.GetTranslation() + modelData.obbSphere.GetPosition(); + float radius = modelData.obbSphere.radius; ChooseLights(Spheref(position, radius), false); for (std::size_t i = lightCount; i < NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS; ++i) diff --git a/src/Nazara/Graphics/MaterialPipeline.cpp b/src/Nazara/Graphics/MaterialPipeline.cpp index 761bc500f..e9a100ad2 100644 --- a/src/Nazara/Graphics/MaterialPipeline.cpp +++ b/src/Nazara/Graphics/MaterialPipeline.cpp @@ -197,6 +197,7 @@ namespace Nz pipelineInfo.blending = true; pipelineInfo.depthWrite = false; pipelineInfo.faceCulling = false; + pipelineInfo.depthSorting = true; pipelineInfo.dstBlend = BlendFunc_InvSrcAlpha; pipelineInfo.srcBlend = BlendFunc_SrcAlpha; @@ -207,6 +208,7 @@ namespace Nz pipelineInfo.depthBuffer = true; pipelineInfo.depthWrite = false; pipelineInfo.faceCulling = false; + pipelineInfo.depthSorting = true; pipelineInfo.dstBlend = BlendFunc_InvSrcAlpha; pipelineInfo.srcBlend = BlendFunc_SrcAlpha; From b5191d142a922d4982b087f0092e9dc21078902f Mon Sep 17 00:00:00 2001 From: Lynix Date: Sat, 22 Apr 2017 17:13:56 +0200 Subject: [PATCH 19/33] Fix warning --- src/Nazara/Utility/SimpleTextDrawer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nazara/Utility/SimpleTextDrawer.cpp b/src/Nazara/Utility/SimpleTextDrawer.cpp index 1901a6ca2..1b53f808f 100644 --- a/src/Nazara/Utility/SimpleTextDrawer.cpp +++ b/src/Nazara/Utility/SimpleTextDrawer.cpp @@ -354,7 +354,7 @@ namespace Nz { glyph.atlas = nullptr; - glyph.bounds.Set(m_drawPos.x, m_drawPos.y, float(advance), float(sizeInfo.lineHeight)); + glyph.bounds.Set(float(m_drawPos.x), float(m_drawPos.y), float(advance), float(sizeInfo.lineHeight)); glyph.corners[0].Set(glyph.bounds.GetCorner(RectCorner_LeftTop)); glyph.corners[1].Set(glyph.bounds.GetCorner(RectCorner_RightTop)); From 3e9ae9856a920f64185e88cd26750963f35c5b92 Mon Sep 17 00:00:00 2001 From: Lynix Date: Sat, 22 Apr 2017 17:14:56 +0200 Subject: [PATCH 20/33] Forgot to save files.. --- src/Nazara/Graphics/DepthRenderTechnique.cpp | 4 ++-- src/Nazara/Graphics/ForwardRenderTechnique.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Nazara/Graphics/DepthRenderTechnique.cpp b/src/Nazara/Graphics/DepthRenderTechnique.cpp index 13f8b30af..4acbbc56d 100644 --- a/src/Nazara/Graphics/DepthRenderTechnique.cpp +++ b/src/Nazara/Graphics/DepthRenderTechnique.cpp @@ -95,7 +95,7 @@ namespace Nz if (!layer.opaqueModels.empty()) DrawOpaqueModels(sceneData, layer); - if (!layer.basicSprites.empty()) + if (!layer.opaqueSprites.empty()) DrawBasicSprites(sceneData, layer); if (!layer.billboards.empty()) @@ -219,7 +219,7 @@ namespace Nz Renderer::SetMatrix(MatrixType_World, Matrix4f::Identity()); Renderer::SetVertexBuffer(&m_spriteBuffer); - for (auto& pipelinePair : layer.basicSprites) + for (auto& pipelinePair : layer.opaqueSprites) { const MaterialPipeline* pipeline = pipelinePair.first; auto& pipelineEntry = pipelinePair.second; diff --git a/src/Nazara/Graphics/ForwardRenderTechnique.cpp b/src/Nazara/Graphics/ForwardRenderTechnique.cpp index 910e2bd1f..e450c06a4 100644 --- a/src/Nazara/Graphics/ForwardRenderTechnique.cpp +++ b/src/Nazara/Graphics/ForwardRenderTechnique.cpp @@ -104,7 +104,7 @@ namespace Nz if (!layer.depthSortedMeshes.empty()) DrawTransparentModels(sceneData, layer); - if (!layer.basicSprites.empty()) + if (!layer.opaqueSprites.empty()) DrawBasicSprites(sceneData, layer); if (!layer.billboards.empty()) @@ -301,7 +301,7 @@ namespace Nz Renderer::SetMatrix(MatrixType_World, Matrix4f::Identity()); Renderer::SetVertexBuffer(&m_spriteBuffer); - for (auto& pipelinePair : layer.basicSprites) + for (auto& pipelinePair : layer.opaqueSprites) { const MaterialPipeline* pipeline = pipelinePair.first; auto& pipelineEntry = pipelinePair.second; From af41b240adc1238b43fa25691726c915a313627e Mon Sep 17 00:00:00 2001 From: Lynix Date: Sun, 23 Apr 2017 13:40:13 +0200 Subject: [PATCH 21/33] Graphics: Add support for depth-sorted sprites --- .../Nazara/Graphics/ForwardRenderQueue.hpp | 17 +- .../Graphics/ForwardRenderTechnique.hpp | 1 + src/Nazara/Graphics/ForwardRenderQueue.cpp | 117 ++++++++----- .../Graphics/ForwardRenderTechnique.cpp | 154 +++++++++++++++++- 4 files changed, 238 insertions(+), 51 deletions(-) diff --git a/include/Nazara/Graphics/ForwardRenderQueue.hpp b/include/Nazara/Graphics/ForwardRenderQueue.hpp index b081641c1..bc5c2456e 100644 --- a/include/Nazara/Graphics/ForwardRenderQueue.hpp +++ b/include/Nazara/Graphics/ForwardRenderQueue.hpp @@ -153,7 +153,7 @@ namespace Nz using MeshPipelineBatches = std::map; - struct TransparentModelData + struct UnbatchedModelData { Matrix4f transformMatrix; MeshData meshData; @@ -161,16 +161,23 @@ namespace Nz const Material* material; }; - using TransparentModelContainer = std::vector; + struct UnbatchedSpriteData + { + std::size_t spriteCount; + const Material* material; + const Texture* overlay; + const VertexStruct_XYZ_Color_UV* vertices; + }; struct Layer { BillboardPipelineBatches billboards; SpritePipelineBatches opaqueSprites; - SpritePipelineBatches depthSortedSprites; MeshPipelineBatches opaqueModels; - TransparentModelContainer depthSortedMeshes; - std::vector depthSortedMeshData; + std::vector depthSortedMeshes; + std::vector depthSortedSprites; + std::vector depthSortedMeshData; + std::vector depthSortedSpriteData; std::vector otherDrawables; unsigned int clearCount = 0; }; diff --git a/include/Nazara/Graphics/ForwardRenderTechnique.hpp b/include/Nazara/Graphics/ForwardRenderTechnique.hpp index 9b5c152df..cadb60473 100644 --- a/include/Nazara/Graphics/ForwardRenderTechnique.hpp +++ b/include/Nazara/Graphics/ForwardRenderTechnique.hpp @@ -43,6 +43,7 @@ namespace Nz void DrawBasicSprites(const SceneData& sceneData, ForwardRenderQueue::Layer& layer) const; void DrawBillboards(const SceneData& sceneData, ForwardRenderQueue::Layer& layer) const; void DrawOpaqueModels(const SceneData& sceneData, ForwardRenderQueue::Layer& layer) const; + void DrawOrderedSprites(const SceneData& sceneData, ForwardRenderQueue::Layer& layer) const; void DrawTransparentModels(const SceneData& sceneData, ForwardRenderQueue::Layer& layer) const; const ShaderUniforms* GetShaderUniforms(const Shader* shader) const; void OnShaderInvalidated(const Shader* shader) const; diff --git a/src/Nazara/Graphics/ForwardRenderQueue.cpp b/src/Nazara/Graphics/ForwardRenderQueue.cpp index c594157b9..ba8c68358 100644 --- a/src/Nazara/Graphics/ForwardRenderQueue.cpp +++ b/src/Nazara/Graphics/ForwardRenderQueue.cpp @@ -386,7 +386,7 @@ namespace Nz std::size_t index = transparentData.size(); transparentData.resize(index+1); - TransparentModelData& data = transparentData.back(); + UnbatchedModelData& data = transparentData.back(); data.material = material; data.meshData = meshData; data.obbSphere = Spheref(transformMatrix.GetTranslation() + meshAABB.GetCenter(), meshAABB.GetSquaredRadius()); @@ -462,48 +462,69 @@ namespace Nz NazaraAssert(material, "Invalid material"); Layer& currentLayer = GetLayer(renderOrder); - SpritePipelineBatches& basicSprites = currentLayer.opaqueSprites; - const MaterialPipeline* materialPipeline = material->GetPipeline(); - - auto pipelineIt = basicSprites.find(materialPipeline); - if (pipelineIt == basicSprites.end()) + if (material->IsDepthSortingEnabled()) { - BatchedSpritePipelineEntry materialEntry; - pipelineIt = basicSprites.insert(SpritePipelineBatches::value_type(materialPipeline, std::move(materialEntry))).first; + auto& transparentSprites = currentLayer.depthSortedSprites; + auto& transparentData = currentLayer.depthSortedSpriteData; + + // The material is marked for depth sorting, we must draw this mesh using another way (after the rendering of opaques objects while sorting them) + std::size_t index = transparentData.size(); + transparentData.resize(index + 1); + + UnbatchedSpriteData& data = transparentData.back(); + data.material = material; + data.overlay = overlay; + data.spriteCount = spriteCount; + data.vertices = vertices; + + transparentSprites.push_back(index); } - - BatchedSpritePipelineEntry& pipelineEntry = pipelineIt->second; - pipelineEntry.enabled = true; - - SpriteMaterialBatches& materialMap = pipelineEntry.materialMap; - - auto matIt = materialMap.find(material); - if (matIt == materialMap.end()) + else { - BatchedBasicSpriteEntry entry; - entry.materialReleaseSlot.Connect(material->OnMaterialRelease, this, &ForwardRenderQueue::OnMaterialInvalidation); + SpritePipelineBatches& sprites = currentLayer.opaqueSprites; - matIt = materialMap.insert(SpriteMaterialBatches::value_type(material, std::move(entry))).first; + const MaterialPipeline* materialPipeline = material->GetPipeline(); + + auto pipelineIt = sprites.find(materialPipeline); + if (pipelineIt == sprites.end()) + { + BatchedSpritePipelineEntry materialEntry; + pipelineIt = sprites.insert(SpritePipelineBatches::value_type(materialPipeline, std::move(materialEntry))).first; + } + + BatchedSpritePipelineEntry& pipelineEntry = pipelineIt->second; + pipelineEntry.enabled = true; + + SpriteMaterialBatches& materialMap = pipelineEntry.materialMap; + + auto matIt = materialMap.find(material); + if (matIt == materialMap.end()) + { + BatchedBasicSpriteEntry entry; + entry.materialReleaseSlot.Connect(material->OnMaterialRelease, this, &ForwardRenderQueue::OnMaterialInvalidation); + + matIt = materialMap.insert(SpriteMaterialBatches::value_type(material, std::move(entry))).first; + } + + BatchedBasicSpriteEntry& entry = matIt->second; + entry.enabled = true; + + auto& overlayMap = entry.overlayMap; + + auto overlayIt = overlayMap.find(overlay); + if (overlayIt == overlayMap.end()) + { + BatchedSpriteEntry overlayEntry; + if (overlay) + overlayEntry.textureReleaseSlot.Connect(overlay->OnTextureRelease, this, &ForwardRenderQueue::OnTextureInvalidation); + + overlayIt = overlayMap.insert(std::make_pair(overlay, std::move(overlayEntry))).first; + } + + auto& spriteVector = overlayIt->second.spriteChains; + spriteVector.push_back(SpriteChain_XYZ_Color_UV({vertices, spriteCount})); } - - BatchedBasicSpriteEntry& entry = matIt->second; - entry.enabled = true; - - auto& overlayMap = entry.overlayMap; - - auto overlayIt = overlayMap.find(overlay); - if (overlayIt == overlayMap.end()) - { - BatchedSpriteEntry overlayEntry; - if (overlay) - overlayEntry.textureReleaseSlot.Connect(overlay->OnTextureRelease, this, &ForwardRenderQueue::OnTextureInvalidation); - - overlayIt = overlayMap.insert(std::make_pair(overlay, std::move(overlayEntry))).first; - } - - auto& spriteVector = overlayIt->second.spriteChains; - spriteVector.push_back(SpriteChain_XYZ_Color_UV({vertices, spriteCount})); } /*! @@ -596,9 +617,11 @@ namespace Nz } } - layer.otherDrawables.clear(); layer.depthSortedMeshes.clear(); layer.depthSortedMeshData.clear(); + layer.depthSortedSpriteData.clear(); + layer.depthSortedSprites.clear(); + layer.otherDrawables.clear(); ++it; } } @@ -717,7 +740,15 @@ namespace Nz const Spheref& sphere1 = layer.depthSortedMeshData[index1].obbSphere; const Spheref& sphere2 = layer.depthSortedMeshData[index2].obbSphere; - return nearPlane.Distance(sphere1.GetPosition()) > nearPlane.Distance(sphere2.GetPosition()); + return nearPlane.Distance(sphere1.GetPosition()) < nearPlane.Distance(sphere2.GetPosition()); + }); + + std::sort(layer.depthSortedSprites.begin(), layer.depthSortedSprites.end(), [&layer, &nearPlane] (std::size_t index1, std::size_t index2) + { + const Vector3f& pos1 = layer.depthSortedSpriteData[index1].vertices[0].position; + const Vector3f& pos2 = layer.depthSortedSpriteData[index2].vertices[0].position; + + return nearPlane.Distance(pos1) < nearPlane.Distance(pos2); }); SortBillboards(layer, nearPlane); @@ -741,6 +772,14 @@ namespace Nz return viewerPos.SquaredDistance(sphere1.GetPosition()) > viewerPos.SquaredDistance(sphere2.GetPosition()); }); + std::sort(layer.depthSortedSprites.begin(), layer.depthSortedSprites.end(), [&layer, &viewerPos] (std::size_t index1, std::size_t index2) + { + const Vector3f& pos1 = layer.depthSortedSpriteData[index1].vertices[0].position; + const Vector3f& pos2 = layer.depthSortedSpriteData[index2].vertices[0].position; + + return viewerPos.SquaredDistance(pos1) > viewerPos.SquaredDistance(pos2); + }); + SortBillboards(layer, nearPlane); } } diff --git a/src/Nazara/Graphics/ForwardRenderTechnique.cpp b/src/Nazara/Graphics/ForwardRenderTechnique.cpp index e450c06a4..1d202c837 100644 --- a/src/Nazara/Graphics/ForwardRenderTechnique.cpp +++ b/src/Nazara/Graphics/ForwardRenderTechnique.cpp @@ -107,6 +107,9 @@ namespace Nz if (!layer.opaqueSprites.empty()) DrawBasicSprites(sceneData, layer); + if (!layer.depthSortedSprites.empty()) + DrawOrderedSprites(sceneData, layer); + if (!layer.billboards.empty()) DrawBillboards(sceneData, layer); @@ -301,6 +304,9 @@ namespace Nz Renderer::SetMatrix(MatrixType_World, Matrix4f::Identity()); Renderer::SetVertexBuffer(&m_spriteBuffer); + const unsigned int overlayTextureUnit = Material::GetTextureUnit(TextureMap_Overlay); + const std::size_t maxSpriteCount = std::min(s_maxQuads, m_spriteBuffer.GetVertexCount() / 4); + for (auto& pipelinePair : layer.opaqueSprites) { const MaterialPipeline* pipeline = pipelinePair.first; @@ -323,6 +329,9 @@ namespace Nz // Position of the camera shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition()); + // Overlay texture unit + shader->SendInteger(shaderUniforms->textureOverlay, overlayTextureUnit); + lastShader = shader; } @@ -335,10 +344,6 @@ namespace Nz { material->Apply(pipelineInstance); - unsigned int overlayTextureUnit = Material::GetTextureUnit(TextureMap_Overlay); - - shader->SendInteger(shaderUniforms->textureOverlay, overlayTextureUnit); - Renderer::SetTextureSampler(overlayTextureUnit, material->GetDiffuseSampler()); auto& overlayMap = matEntry.overlayMap; @@ -362,7 +367,6 @@ namespace Nz VertexStruct_XYZ_Color_UV* vertices = static_cast(vertexMapper.GetPointer()); std::size_t spriteCount = 0; - std::size_t maxSpriteCount = std::min(s_maxQuads, m_spriteBuffer.GetVertexCount() / 4); do { @@ -777,6 +781,142 @@ namespace Nz } } + void ForwardRenderTechnique::DrawOrderedSprites(const SceneData & sceneData, ForwardRenderQueue::Layer & layer) const + { + NazaraAssert(sceneData.viewer, "Invalid viewer"); + + Renderer::SetIndexBuffer(&s_quadIndexBuffer); + Renderer::SetMatrix(MatrixType_World, Matrix4f::Identity()); + Renderer::SetVertexBuffer(&m_spriteBuffer); + + const Material* lastMaterial = nullptr; + const MaterialPipeline* lastPipeline = nullptr; + const Shader* lastShader = nullptr; + const Texture* lastOverlay = nullptr; + const MaterialPipeline::Instance* pipelineInstance = nullptr; + + const unsigned int overlayTextureUnit = Material::GetTextureUnit(TextureMap_Overlay); + + bool updateVertexBuffer = true; + const std::size_t maxSpriteCount = std::min(s_maxQuads, m_spriteBuffer.GetVertexCount() / 4); + + std::size_t alreadyDrawnCount = 0; + std::size_t spriteIndex = 0; + std::size_t spriteChainOffset = 0; + auto splitChainIt = layer.depthSortedSprites.end(); + + for (auto it = layer.depthSortedSprites.begin(); it != layer.depthSortedSprites.end();) + { + if (updateVertexBuffer) + { + // We open the buffer in writing mode + BufferMapper vertexMapper(m_spriteBuffer, BufferAccess_DiscardAndWrite); + VertexStruct_XYZ_Color_UV* vertices = static_cast(vertexMapper.GetPointer()); + + std::size_t availableSpriteSpace = maxSpriteCount; + bool split = false; + for (auto it2 = it; it2 != layer.depthSortedSprites.end(); ++it2) + { + const ForwardRenderQueue::UnbatchedSpriteData& spriteData = layer.depthSortedSpriteData[*it2]; + + std::size_t count = std::min(availableSpriteSpace, spriteData.spriteCount - spriteChainOffset); + + std::memcpy(vertices, spriteData.vertices + spriteChainOffset * 4, 4 * count * sizeof(VertexStruct_XYZ_Color_UV)); + vertices += count * 4; + + availableSpriteSpace -= count; + + // Have we treated the entire chain ? + if (count != spriteData.spriteCount) + { + // Oops, not enough space to store current chain + spriteChainOffset += count; + splitChainIt = it2; + split = true; + break; + } + + // Switch to next sprite chain, if any + spriteChainOffset = 0; + } + + spriteIndex = 0; + updateVertexBuffer = false; + + if (!split) + splitChainIt = layer.depthSortedSprites.end(); + } + + std::size_t index = *it; + + const ForwardRenderQueue::UnbatchedSpriteData& spriteData = layer.depthSortedSpriteData[index]; + + const Material* material = spriteData.material; + if (material != lastMaterial) + { + const MaterialPipeline* pipeline = material->GetPipeline(); + if (pipeline != lastPipeline) + { + pipelineInstance = &pipeline->Apply(ShaderFlags_TextureOverlay | ShaderFlags_VertexColor); + + const Shader* shader = pipelineInstance->uberInstance->GetShader(); + + // Uniforms are conserved in our program, there's no point to send them back until they change + if (shader != lastShader) + { + // Index of uniforms in the shader + const ShaderUniforms* shaderUniforms = GetShaderUniforms(shader); + + // Ambient color of the scene + shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor); + // Position of the camera + shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition()); + // Overlay texture unit + shader->SendInteger(shaderUniforms->textureOverlay, overlayTextureUnit); + + lastShader = shader; + } + + lastPipeline = pipeline; + } + + material->Apply(*pipelineInstance); + + Renderer::SetTextureSampler(overlayTextureUnit, material->GetDiffuseSampler()); + + lastMaterial = material; + } + + const Texture* overlay = (spriteData.overlay) ? spriteData.overlay : &m_whiteTexture; + if (overlay != lastOverlay) + { + Renderer::SetTexture(overlayTextureUnit, overlay); + lastOverlay = overlay; + } + + std::size_t spriteCount; + if (it != splitChainIt) + { + spriteCount = spriteData.spriteCount - alreadyDrawnCount; + alreadyDrawnCount = 0; + + ++it; + } + else + { + spriteCount = spriteChainOffset; + + alreadyDrawnCount = spriteCount; + updateVertexBuffer = true; + + // Restart at current iterator next time + } + + Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, spriteIndex * 6, spriteCount * 6); + spriteIndex += spriteCount; + } + } + /*! * \brief Draws transparent models * @@ -796,9 +936,9 @@ namespace Nz const ShaderUniforms* shaderUniforms = nullptr; unsigned int lightCount = 0; - for (unsigned int index : layer.depthSortedMeshes) + for (std::size_t index : layer.depthSortedMeshes) { - const ForwardRenderQueue::TransparentModelData& modelData = layer.depthSortedMeshData[index]; + const ForwardRenderQueue::UnbatchedModelData& modelData = layer.depthSortedMeshData[index]; // Material const Material* material = modelData.material; From 064ffd294505a5dff38b8849df71d36a5e14c4e6 Mon Sep 17 00:00:00 2001 From: Lynix Date: Thu, 11 May 2017 19:46:57 +0200 Subject: [PATCH 22/33] SDK/EntityList: Improve documentation --- SDK/include/NDK/EntityList.cpp | 0 SDK/include/NDK/EntityList.inl | 3 +++ 2 files changed, 3 insertions(+) delete mode 100644 SDK/include/NDK/EntityList.cpp diff --git a/SDK/include/NDK/EntityList.cpp b/SDK/include/NDK/EntityList.cpp deleted file mode 100644 index e69de29bb..000000000 diff --git a/SDK/include/NDK/EntityList.inl b/SDK/include/NDK/EntityList.inl index 6d9447a7f..21bf51bdb 100644 --- a/SDK/include/NDK/EntityList.inl +++ b/SDK/include/NDK/EntityList.inl @@ -63,10 +63,13 @@ namespace Ndk /*! * \brief Inserts the entity into the set * + * Marks an entity as present in this entity list, it must belongs to the same world as others entities contained in this list. + * * \param entity Valid pointer to an entity * * \remark If entity is already contained, no action is performed * \remark If any entity has been inserted since construction (or last Clear call), the entity must belong to the same world as the previously inserted entities + * \remark It's up to the programmer to remove an entity from this list before its deletion */ inline void EntityList::Insert(Entity* entity) { From 19f9191815cefe04dcc5e94a535dbf8908f86257 Mon Sep 17 00:00:00 2001 From: Lynix Date: Thu, 11 May 2017 20:14:58 +0200 Subject: [PATCH 23/33] SDK/World: Improve doc --- SDK/src/NDK/World.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/SDK/src/NDK/World.cpp b/SDK/src/NDK/World.cpp index 88a23060d..81b862df9 100644 --- a/SDK/src/NDK/World.cpp +++ b/SDK/src/NDK/World.cpp @@ -79,6 +79,8 @@ namespace Ndk if (m_entities.capacity() > m_entities.size()) { + NazaraAssert(m_waitingEntities.empty(), "There should be no waiting entities if space is available in main container"); + m_entities.push_back(Entity(this, id)); //< We can't use emplace_back due to the scope entBlock = &m_entities.back(); } @@ -86,7 +88,7 @@ namespace Ndk { // Pushing to entities would reallocate vector and thus, invalidate EntityHandles (which we don't want until world update) // To prevent this, allocate them into a separate vector and move them at update - // For now, we are counting on m_entities grow strategy to prevent + // For now, we are counting on m_entities grow strategy to keep allocation frequency low m_waitingEntities.emplace_back(std::make_unique(Entity(this, id))); entBlock = m_waitingEntities.back().get(); } @@ -167,7 +169,7 @@ namespace Ndk // Move waiting entities to entity list if (!m_waitingEntities.empty()) { - constexpr std::size_t MinEntityCapacity = 10; //< We want to be able to grow entity count by at least ten entities per update without going to the waiting list + constexpr std::size_t MinEntityCapacity = 10; //< We want to be able to grow maximum entity count by at least ten without going to the waiting list m_entities.reserve(m_entities.size() + m_waitingEntities.size() + MinEntityCapacity); for (auto& blockPtr : m_waitingEntities) From c9969d1b579934d2fe742fc4d053be76186a1ff7 Mon Sep 17 00:00:00 2001 From: Lynix Date: Thu, 11 May 2017 21:21:25 +0200 Subject: [PATCH 24/33] Sdk/BaseComponent: Disable implicit copying --- SDK/include/NDK/BaseComponent.hpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/SDK/include/NDK/BaseComponent.hpp b/SDK/include/NDK/BaseComponent.hpp index d9fdcba45..e6dfb5ed9 100644 --- a/SDK/include/NDK/BaseComponent.hpp +++ b/SDK/include/NDK/BaseComponent.hpp @@ -23,7 +23,6 @@ namespace Ndk using Factory = std::function; BaseComponent(ComponentIndex componentIndex); - BaseComponent(const BaseComponent&) = default; BaseComponent(BaseComponent&&) = default; virtual ~BaseComponent(); @@ -34,7 +33,7 @@ namespace Ndk inline static ComponentIndex GetMaxComponentIndex(); - BaseComponent& operator=(const BaseComponent&) = default; + BaseComponent& operator=(const BaseComponent&) = delete; BaseComponent& operator=(BaseComponent&&) = default; protected: @@ -44,6 +43,8 @@ namespace Ndk static ComponentIndex RegisterComponent(ComponentId id, Factory factoryFunc); private: + BaseComponent(const BaseComponent&) = default; + virtual void OnAttached(); virtual void OnComponentAttached(BaseComponent& component); virtual void OnComponentDetached(BaseComponent& component); From 4a0cb4e1f13c7e6082ebda206a9e07b7629915ed Mon Sep 17 00:00:00 2001 From: Lynix Date: Thu, 11 May 2017 21:26:01 +0200 Subject: [PATCH 25/33] Sdk/World: Get rid of now useless trick in Clone method EntityHandle can no longer move in memory until a world update --- SDK/src/NDK/World.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SDK/src/NDK/World.cpp b/SDK/src/NDK/World.cpp index 81b862df9..8b7b51186 100644 --- a/SDK/src/NDK/World.cpp +++ b/SDK/src/NDK/World.cpp @@ -152,7 +152,7 @@ namespace Ndk clone->AddComponent(std::move(component)); } - return GetEntity(clone->GetId()); + return clone; } /*! From 3d25501f9fb8e568fba36a6b3f4cfe79917a0216 Mon Sep 17 00:00:00 2001 From: Lynix Date: Sat, 13 May 2017 20:00:15 +0200 Subject: [PATCH 26/33] Sdk: Fix compilation --- SDK/include/NDK/BaseComponent.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SDK/include/NDK/BaseComponent.hpp b/SDK/include/NDK/BaseComponent.hpp index e6dfb5ed9..427fb017e 100644 --- a/SDK/include/NDK/BaseComponent.hpp +++ b/SDK/include/NDK/BaseComponent.hpp @@ -37,14 +37,14 @@ namespace Ndk BaseComponent& operator=(BaseComponent&&) = default; protected: + BaseComponent(const BaseComponent&) = default; + ComponentIndex m_componentIndex; EntityHandle m_entity; static ComponentIndex RegisterComponent(ComponentId id, Factory factoryFunc); private: - BaseComponent(const BaseComponent&) = default; - virtual void OnAttached(); virtual void OnComponentAttached(BaseComponent& component); virtual void OnComponentDetached(BaseComponent& component); From bb3eebb9ccf21adc9e29ce4f9eb0f99952791888 Mon Sep 17 00:00:00 2001 From: Lynix Date: Sun, 14 May 2017 22:13:31 +0200 Subject: [PATCH 27/33] Sdk/EntityList: Automatically remove entities from lists on destruction --- SDK/include/NDK/Entity.hpp | 8 +++- SDK/include/NDK/Entity.inl | 20 +++++++--- SDK/include/NDK/EntityList.hpp | 5 ++- SDK/include/NDK/EntityList.inl | 55 ++++++++++++++++++++++++---- SDK/src/NDK/Entity.cpp | 9 ++++- SDK/src/NDK/Systems/RenderSystem.cpp | 6 --- 6 files changed, 81 insertions(+), 22 deletions(-) diff --git a/SDK/include/NDK/Entity.hpp b/SDK/include/NDK/Entity.hpp index e386100f8..5419da0d2 100644 --- a/SDK/include/NDK/Entity.hpp +++ b/SDK/include/NDK/Entity.hpp @@ -17,14 +17,17 @@ namespace Ndk { class BaseComponent; + class BaseSystem; class Entity; + class EntityList; class World; using EntityHandle = Nz::ObjectHandle; class NDK_API Entity : public Nz::HandledObject { - friend class BaseSystem; + friend BaseSystem; + friend EntityList; friend World; public: @@ -78,13 +81,16 @@ namespace Ndk inline Nz::Bitset<>& GetRemovedComponentBits(); + inline void RegisterEntityList(EntityList* list); inline void RegisterSystem(SystemIndex index); inline void SetWorld(World* world) noexcept; + inline void UnregisterEntityList(EntityList* list); inline void UnregisterSystem(SystemIndex index); std::vector> m_components; + std::vector m_containedInLists; Nz::Bitset<> m_componentBits; Nz::Bitset<> m_removedComponentBits; Nz::Bitset<> m_systemBits; diff --git a/SDK/include/NDK/Entity.inl b/SDK/include/NDK/Entity.inl index e6f997226..adb2153d8 100644 --- a/SDK/include/NDK/Entity.inl +++ b/SDK/include/NDK/Entity.inl @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -257,11 +258,10 @@ namespace Ndk return m_removedComponentBits; } - /*! - * \brief Registers a system for the entity - * - * \param index Index of the system - */ + inline void Entity::RegisterEntityList(EntityList* list) + { + m_containedInLists.push_back(list); + } inline void Entity::RegisterSystem(SystemIndex index) { @@ -289,6 +289,16 @@ namespace Ndk * \param index Index of the system */ + inline void Entity::UnregisterEntityList(EntityList* list) + { + auto it = std::find(m_containedInLists.begin(), m_containedInLists.end(), list); + assert(it != m_containedInLists.end()); + + // Swap and pop idiom + *it = m_containedInLists.back(); + m_containedInLists.pop_back(); + } + inline void Entity::UnregisterSystem(SystemIndex index) { m_systemBits.UnboundedReset(index); diff --git a/SDK/include/NDK/EntityList.hpp b/SDK/include/NDK/EntityList.hpp index 76097f87b..b659b15fb 100644 --- a/SDK/include/NDK/EntityList.hpp +++ b/SDK/include/NDK/EntityList.hpp @@ -15,6 +15,8 @@ namespace Ndk { class NDK_API EntityList { + friend Entity; + public: class iterator; friend iterator; @@ -23,7 +25,7 @@ namespace Ndk inline EntityList(); inline EntityList(const EntityList& entityList); inline EntityList(EntityList&& entityList) noexcept; - ~EntityList() = default; + inline ~EntityList(); inline void Clear(); @@ -46,6 +48,7 @@ namespace Ndk private: inline std::size_t FindNext(std::size_t currentId) const; inline World* GetWorld() const; + inline void NotifyEntityDestruction(const Entity* entity); Nz::Bitset m_entityBits; World* m_world; diff --git a/SDK/include/NDK/EntityList.inl b/SDK/include/NDK/EntityList.inl index f5a751323..d178d2b09 100644 --- a/SDK/include/NDK/EntityList.inl +++ b/SDK/include/NDK/EntityList.inl @@ -5,7 +5,6 @@ #include #include #include -#include "EntityList.hpp" namespace Ndk { @@ -30,6 +29,8 @@ namespace Ndk m_entityBits(entityList.m_entityBits), m_world(entityList.m_world) { + for (const Ndk::EntityHandle& entity : *this) + entity->RegisterEntityList(this); } /*! @@ -39,6 +40,17 @@ namespace Ndk m_entityBits(std::move(entityList.m_entityBits)), m_world(entityList.m_world) { + for (const Ndk::EntityHandle& entity : *this) + { + entity->UnregisterEntityList(&entityList); + entity->RegisterEntityList(this); + } + } + + inline EntityList::~EntityList() + { + for (const Ndk::EntityHandle& entity : *this) + entity->UnregisterEntityList(this); } @@ -49,6 +61,9 @@ namespace Ndk */ inline void EntityList::Clear() { + for (const Ndk::EntityHandle& entity : *this) + entity->UnregisterEntityList(this); + m_entityBits.Clear(); m_world = nullptr; } @@ -88,15 +103,18 @@ namespace Ndk * * \remark If entity is already contained, no action is performed * \remark If any entity has been inserted since construction (or last Clear call), the entity must belong to the same world as the previously inserted entities - * \remark It's up to the programmer to remove an entity from this list before its deletion */ inline void EntityList::Insert(Entity* entity) { NazaraAssert(entity, "Invalid entity"); - NazaraAssert(!m_world || entity->GetWorld() == m_world, "Incompatible world"); - m_entityBits.UnboundedSet(entity->GetId(), true); - m_world = entity->GetWorld(); + if (!Has(entity)) + { + entity->RegisterEntityList(this); + + m_entityBits.UnboundedSet(entity->GetId(), true); + m_world = entity->GetWorld(); + } } /*! @@ -111,7 +129,12 @@ namespace Ndk */ inline void EntityList::Remove(Entity* entity) { - m_entityBits.UnboundedSet(entity->GetId(), false); + if (Has(entity)) + { + m_entityBits.Reset(entity->GetId()); + + entity->UnregisterEntityList(this); + } } // STL Interface @@ -140,14 +163,23 @@ namespace Ndk m_entityBits = entityList.m_entityBits; m_world = entityList.m_world; + for (const Ndk::EntityHandle& entity : *this) + entity->RegisterEntityList(this); + return *this; } - inline EntityList& EntityList::operator=(EntityList && entityList) noexcept + inline EntityList& EntityList::operator=(EntityList&& entityList) noexcept { m_entityBits = std::move(entityList.m_entityBits); m_world = entityList.m_world; + for (const Ndk::EntityHandle& entity : *this) + { + entity->UnregisterEntityList(&entityList); + entity->RegisterEntityList(this); + } + return *this; } @@ -161,6 +193,13 @@ namespace Ndk return m_world; } + inline void EntityList::NotifyEntityDestruction(const Entity* entity) + { + assert(Has(entity)); + + m_entityBits.Reset(entity->GetId()); + } + inline EntityList::iterator::iterator(const EntityList* list, std::size_t nextId) : m_nextEntityId(nextId), @@ -216,6 +255,6 @@ namespace Ndk using std::swap; - std::swap(lhs.m_nextEntityId, rhs.m_nextEntityId); + swap(lhs.m_nextEntityId, rhs.m_nextEntityId); } } diff --git a/SDK/src/NDK/Entity.cpp b/SDK/src/NDK/Entity.cpp index 7419ac627..fab08b237 100644 --- a/SDK/src/NDK/Entity.cpp +++ b/SDK/src/NDK/Entity.cpp @@ -23,6 +23,7 @@ namespace Ndk Entity::Entity(Entity&& entity) : HandledObject(std::move(entity)), m_components(std::move(entity.m_components)), + m_containedInLists(std::move(entity.m_containedInLists)), m_componentBits(std::move(entity.m_componentBits)), m_removedComponentBits(std::move(entity.m_removedComponentBits)), m_systemBits(std::move(entity.m_systemBits)), @@ -176,9 +177,15 @@ namespace Ndk m_components.clear(); m_componentBits.Reset(); - // And then free every handle + // Free every handle UnregisterAllHandles(); + // Remove from every list + for (EntityList* list : m_containedInLists) + list->NotifyEntityDestruction(this); + + m_containedInLists.clear(); + m_valid = false; } diff --git a/SDK/src/NDK/Systems/RenderSystem.cpp b/SDK/src/NDK/Systems/RenderSystem.cpp index 44b0d1bba..022621596 100644 --- a/SDK/src/NDK/Systems/RenderSystem.cpp +++ b/SDK/src/NDK/Systems/RenderSystem.cpp @@ -57,12 +57,6 @@ namespace Ndk } } - m_directionalLights.Remove(entity); - m_drawables.Remove(entity); - m_lights.Remove(entity); - m_particleGroups.Remove(entity); - m_pointSpotLights.Remove(entity); - if (entity->HasComponent()) { GraphicsComponent& gfxComponent = entity->GetComponent(); From 7425993d2deb725312543deaa205a761f9c99ed4 Mon Sep 17 00:00:00 2001 From: Lynix Date: Mon, 15 May 2017 10:11:42 +0200 Subject: [PATCH 28/33] SDK/World: Fix waiting entities not being cleared on time --- SDK/src/NDK/World.cpp | 1 + tests/SDK/NDK/Systems/ListenerSystem.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/SDK/src/NDK/World.cpp b/SDK/src/NDK/World.cpp index 8b7b51186..3ed1a25d0 100644 --- a/SDK/src/NDK/World.cpp +++ b/SDK/src/NDK/World.cpp @@ -119,6 +119,7 @@ namespace Ndk // This is made to avoid that handle warn uselessly entities before their destruction m_entities.clear(); m_entityBlocks.clear(); + m_waitingEntities.clear(); m_aliveEntities.Clear(); m_dirtyEntities.Clear(); diff --git a/tests/SDK/NDK/Systems/ListenerSystem.cpp b/tests/SDK/NDK/Systems/ListenerSystem.cpp index 2792dbc25..b83b24cd1 100644 --- a/tests/SDK/NDK/Systems/ListenerSystem.cpp +++ b/tests/SDK/NDK/Systems/ListenerSystem.cpp @@ -11,7 +11,7 @@ SCENARIO("ListenerSystem", "[NDK][LISTENERSYSTEM]") GIVEN("A world and an entity with listener & node components") { Ndk::World world; - const Ndk::EntityHandle& entity = world.CreateEntity(); + Ndk::EntityHandle entity = world.CreateEntity(); Ndk::ListenerComponent& listenerComponent = entity->AddComponent(); Ndk::NodeComponent& nodeComponent = entity->AddComponent(); From 65d3b59e03613233bddbf8cf3ec1c716a5e5e124 Mon Sep 17 00:00:00 2001 From: Lynix Date: Tue, 16 May 2017 09:07:28 +0200 Subject: [PATCH 29/33] Network/SocketPoller: Makes it possible to watch read and write states --- include/Nazara/Network/Enums.hpp | 19 +++- include/Nazara/Network/SocketPoller.hpp | 7 +- src/Nazara/Network/Linux/SocketPollerImpl.cpp | 45 ++++++--- src/Nazara/Network/Linux/SocketPollerImpl.hpp | 8 +- src/Nazara/Network/Posix/SocketPollerImpl.cpp | 46 ++++++--- src/Nazara/Network/Posix/SocketPollerImpl.hpp | 5 +- src/Nazara/Network/SocketPoller.cpp | 43 +++++++-- src/Nazara/Network/Win32/SocketPollerImpl.cpp | 96 +++++++++++-------- src/Nazara/Network/Win32/SocketPollerImpl.hpp | 16 ++-- 9 files changed, 192 insertions(+), 93 deletions(-) diff --git a/include/Nazara/Network/Enums.hpp b/include/Nazara/Network/Enums.hpp index f80ba6cd9..39bce0dd0 100644 --- a/include/Nazara/Network/Enums.hpp +++ b/include/Nazara/Network/Enums.hpp @@ -7,7 +7,7 @@ #ifndef NAZARA_ENUMS_NETWORK_HPP #define NAZARA_ENUMS_NETWORK_HPP -#include +#include namespace Nz { @@ -91,6 +91,23 @@ namespace Nz SocketError_Max = SocketError_UnreachableHost }; + enum SocketPollEvent + { + SocketPollEvent_Read, //< One or more sockets is ready for a read operation + SocketPollEvent_Write, //< One or more sockets is ready for a write operation + + SocketPollEvent_Max = SocketPollEvent_Write + }; + + template<> + struct EnumAsFlags + { + static constexpr bool value = true; + static constexpr int max = SocketPollEvent_Max; + }; + + using SocketPollEventFlags = Flags; + enum SocketState { SocketState_Bound, //< The socket is currently bound diff --git a/include/Nazara/Network/SocketPoller.hpp b/include/Nazara/Network/SocketPoller.hpp index 74f90c17f..c801ca5e2 100644 --- a/include/Nazara/Network/SocketPoller.hpp +++ b/include/Nazara/Network/SocketPoller.hpp @@ -24,10 +24,11 @@ namespace Nz void Clear(); - bool IsReady(const AbstractSocket& socket) const; + bool IsReadyToRead(const AbstractSocket& socket) const; + bool IsReadyToWrite(const AbstractSocket& socket) const; bool IsRegistered(const AbstractSocket& socket) const; - bool RegisterSocket(AbstractSocket& socket); + bool RegisterSocket(AbstractSocket& socket, SocketPollEventFlags eventFlags); void UnregisterSocket(AbstractSocket& socket); bool Wait(UInt64 msTimeout); @@ -41,4 +42,4 @@ namespace Nz #include -#endif // NAZARA_SOCKETPOLLER_HPP \ No newline at end of file +#endif // NAZARA_SOCKETPOLLER_HPP diff --git a/src/Nazara/Network/Linux/SocketPollerImpl.cpp b/src/Nazara/Network/Linux/SocketPollerImpl.cpp index c839d7352..6617efd37 100644 --- a/src/Nazara/Network/Linux/SocketPollerImpl.cpp +++ b/src/Nazara/Network/Linux/SocketPollerImpl.cpp @@ -23,13 +23,19 @@ namespace Nz void SocketPollerImpl::Clear() { - m_activeSockets.clear(); + m_readyToReadSockets.clear(); + m_readyToWriteSockets.clear(); m_sockets.clear(); } - bool SocketPollerImpl::IsReady(SocketHandle socket) const + bool SocketPollerImpl::IsReadyToRead(SocketHandle socket) const { - return m_activeSockets.count(socket) != 0; + return m_readyToReadSockets.count(socket) != 0; + } + + bool SocketPollerImpl::IsReadyToWrite(SocketHandle socket) const + { + return m_readyToWriteSockets.count(socket) != 0; } bool SocketPollerImpl::IsRegistered(SocketHandle socket) const @@ -37,15 +43,21 @@ namespace Nz return m_sockets.count(socket) != 0; } - bool SocketPollerImpl::RegisterSocket(SocketHandle socket) + bool SocketPollerImpl::RegisterSocket(SocketHandle socket, SocketPollEventFlags eventFlags) { NazaraAssert(!IsRegistered(socket), "Socket is already registered"); - epoll_event event; - event.events = EPOLLIN; - event.data.fd = socket; + epoll_event entry; + entry.events = 0; + entry.data.fd = socket; - if (epoll_ctl(m_handle, EPOLL_CTL_ADD, socket, &event) != 0) + if (eventFlags & SocketPollEvent_Read) + entry.events |= EPOLLIN; + + if (eventFlags & SocketPollEvent_Write) + entry.events |= EPOLLOUT; + + if (epoll_ctl(m_handle, EPOLL_CTL_ADD, socket, &entry) != 0) { NazaraError("Failed to add socket to epoll structure (errno " + String::Number(errno) + ": " + Error::GetLastSystemError() + ')'); return false; @@ -60,7 +72,8 @@ namespace Nz { NazaraAssert(IsRegistered(socket), "Socket is not registered"); - m_activeSockets.erase(socket); + m_readyToReadSockets.erase(socket); + m_readyToWriteSockets.erase(socket); m_sockets.erase(socket); if (epoll_ctl(m_handle, EPOLL_CTL_DEL, socket, nullptr) != 0) @@ -84,21 +97,27 @@ namespace Nz return 0; } - m_activeSockets.clear(); + m_readyToReadSockets.clear(); + m_readyToWriteSockets.clear(); if (activeSockets > 0) { int socketCount = activeSockets; for (int i = 0; i < socketCount; ++i) { - if (m_events[i].events & (EPOLLIN | EPOLLHUP | EPOLLERR)) + if (m_events[i].events & (EPOLLIN | EPOLLOUT | EPOLLHUP | EPOLLERR)) { - m_activeSockets.insert(m_events[i].data.fd); + if (m_events[i].events & (EPOLLIN | EPOLLHUP | EPOLLERR)) + m_readyToReadSockets.insert(m_events[i].data.fd); + + if (m_events[i].events & (EPOLLOUT | EPOLLERR)) + m_readyToWriteSockets.insert(m_events[i].data.fd); + if (m_events[i].events & EPOLLERR) NazaraWarning("Descriptor " + String::Number(m_events[i].data.fd) + " was returned by epoll with EPOLLERR status"); } else { - NazaraWarning("Descriptor " + String::Number(m_events[i].data.fd) + " was returned by epoll without EPOLLIN (events: 0x" + String::Number(m_events[i].events, 16) + ')'); + NazaraWarning("Descriptor " + String::Number(m_events[i].data.fd) + " was returned by epoll without EPOLLIN nor EPOLLOUT flags (events: 0x" + String::Number(m_events[i].events, 16) + ')'); activeSockets--; } } diff --git a/src/Nazara/Network/Linux/SocketPollerImpl.hpp b/src/Nazara/Network/Linux/SocketPollerImpl.hpp index 1c54471fc..a804102fa 100644 --- a/src/Nazara/Network/Linux/SocketPollerImpl.hpp +++ b/src/Nazara/Network/Linux/SocketPollerImpl.hpp @@ -23,16 +23,18 @@ namespace Nz void Clear(); - bool IsReady(SocketHandle socket) const; + bool IsReadyToRead(SocketHandle socket) const; + bool IsReadyToWrite(SocketHandle socket) const; bool IsRegistered(SocketHandle socket) const; - bool RegisterSocket(SocketHandle socket); + bool RegisterSocket(SocketHandle socket, SocketPollEventFlags eventFlags); void UnregisterSocket(SocketHandle socket); int Wait(UInt64 msTimeout, SocketError* error); private: - std::unordered_set m_activeSockets; + std::unordered_set m_readyToReadSockets; + std::unordered_set m_readyToWriteSockets; std::unordered_set m_sockets; std::vector m_events; int m_handle; diff --git a/src/Nazara/Network/Posix/SocketPollerImpl.cpp b/src/Nazara/Network/Posix/SocketPollerImpl.cpp index 5d2a6c317..59b8a8ca7 100644 --- a/src/Nazara/Network/Posix/SocketPollerImpl.cpp +++ b/src/Nazara/Network/Posix/SocketPollerImpl.cpp @@ -10,14 +10,20 @@ namespace Nz { void SocketPollerImpl::Clear() { - m_activeSockets.clear(); + m_readyToReadSockets.clear(); + m_readyToWriteSockets.clear(); m_allSockets.clear(); m_sockets.clear(); } - bool SocketPollerImpl::IsReady(SocketHandle socket) const + bool SocketPollerImpl::IsReadyToRead(SocketHandle socket) const { - return m_activeSockets.count(socket) != 0; + return m_readyToReadSockets.count(socket) != 0; + } + + bool SocketPollerImpl::IsReadyToWrite(SocketHandle socket) const + { + return m_readyToWriteSockets.count(socket) != 0; } bool SocketPollerImpl::IsRegistered(SocketHandle socket) const @@ -25,16 +31,22 @@ namespace Nz return m_allSockets.count(socket) != 0; } - bool SocketPollerImpl::RegisterSocket(SocketHandle socket) + bool SocketPollerImpl::RegisterSocket(SocketHandle socket, SocketPollEventFlags eventFlags) { NazaraAssert(!IsRegistered(socket), "Socket is already registered"); PollSocket entry = { socket, - POLLRDNORM, + 0, 0 }; + if (eventFlags & SocketPollEvent_Read) + entry.events |= POLLRDNORM; + + if (eventFlags & SocketPollEvent_Write) + entry.events |= POLLWRNORM; + m_allSockets[socket] = m_sockets.size(); m_sockets.emplace_back(entry); @@ -57,10 +69,11 @@ namespace Nz // Now move it properly (lastElement is invalid after the following line) and pop it m_sockets[entry] = std::move(m_sockets.back()); } - m_sockets.pop_back(); - m_activeSockets.erase(socket); + m_allSockets.erase(socket); + m_readyToReadSockets.erase(socket); + m_readyToWriteSockets.erase(socket); } int SocketPollerImpl::Wait(UInt64 msTimeout, SocketError* error) @@ -68,20 +81,25 @@ namespace Nz int activeSockets; // Reset status of sockets - for (PollSocket& entry : m_sockets) - entry.revents = 0; - activeSockets = SocketImpl::Poll(m_sockets.data(), m_sockets.size(), static_cast(msTimeout), error); - m_activeSockets.clear(); - if (activeSockets > 0) + m_readyToReadSockets.clear(); + m_readyToWriteSockets.clear(); + if (activeSockets > 0U) { int socketRemaining = activeSockets; for (PollSocket& entry : m_sockets) { - if (entry.revents & POLLRDNORM) + if (entry.revents != 0) { - m_activeSockets.insert(entry.fd); + if (entry.revents & POLLRDNORM) + m_readyToReadSockets.insert(entry.fd); + + if (entry.revents & POLLWRNORM) + m_readyToWriteSockets.insert(entry.fd); + + entry.revents = 0; + if (--socketRemaining == 0) break; } diff --git a/src/Nazara/Network/Posix/SocketPollerImpl.hpp b/src/Nazara/Network/Posix/SocketPollerImpl.hpp index 3472c3265..3582121d4 100644 --- a/src/Nazara/Network/Posix/SocketPollerImpl.hpp +++ b/src/Nazara/Network/Posix/SocketPollerImpl.hpp @@ -27,13 +27,14 @@ namespace Nz bool IsReady(SocketHandle socket) const; bool IsRegistered(SocketHandle socket) const; - bool RegisterSocket(SocketHandle socket); + bool RegisterSocket(SocketHandle socket, SocketPollEventFlags eventFlags); void UnregisterSocket(SocketHandle socket); int Wait(UInt64 msTimeout, SocketError* error); private: - std::unordered_set m_activeSockets; + std::unordered_set m_readyToReadSockets; + std::unordered_set m_readyToWriteSockets; std::unordered_map m_allSockets; std::vector m_sockets; }; diff --git a/src/Nazara/Network/SocketPoller.cpp b/src/Nazara/Network/SocketPoller.cpp index 43250a84f..7ef261ef9 100644 --- a/src/Nazara/Network/SocketPoller.cpp +++ b/src/Nazara/Network/SocketPoller.cpp @@ -57,12 +57,13 @@ namespace Nz /*! * \brief Checks if a specific socket is ready to read data * - * This function allows you to read the results of the last Wait operation and if a specific socket is ready. + * This function allows you to read the results of the last Wait operation and if a specific socket is ready to read (has incoming data). * - * A socket in the ready state (with the exception of TcpServer) has incoming data and can be read without blocking. + * A socket in the ready to read state (with the exception of TcpServer) has incoming data and can be read without blocking. * - * \remark When used on a TcpServer socket, this function returns true if the server is ready to accept a new client. - * \remark You must call Wait before using this function in order to refresh the state. + * \remark You must call Wait before using this function in order to refresh the read state. + * \remark A socket must be registered with SocketPollerEvent_Read event flag for its read state to be watched + * \remark A TcpServer socket becomes ready to read when it is ready to accept a new client. * * \param socket Reference to the socket to check * @@ -70,11 +71,32 @@ namespace Nz * * \see Wait */ - bool SocketPoller::IsReady(const AbstractSocket& socket) const + bool SocketPoller::IsReadyToRead(const AbstractSocket& socket) const { NazaraAssert(IsRegistered(socket), "Socket is not registered in the poller"); - return m_impl->IsReady(socket.GetNativeHandle()); + return m_impl->IsReadyToRead(socket.GetNativeHandle()); + } + + /*! + * \brief Checks if a specific socket is ready to write data + * + * This function allows you to read the results of the last Wait operation and if a specific socket is ready to write (can be written to without blocking). + * + * \remark You must call Wait before using this function in order to refresh the read state. + * \remark A socket must be registered with SocketPollerEvent_Write event flag for its read state to be watched + * + * \param socket Reference to the socket to check + * + * \return True if the socket is available for writing without blocking, false otherwise + * + * \see Wait + */ + bool SocketPoller::IsReadyToWrite(const AbstractSocket& socket) const + { + NazaraAssert(IsRegistered(socket), "Socket is not registered in the poller"); + + return m_impl->IsReadyToWrite(socket.GetNativeHandle()); } /*! @@ -97,7 +119,7 @@ namespace Nz /*! * \brief Register a socket in the SocketPoller * - * A registered socket is part of the SocketPoller and will be checked by the next Wait operations. + * A registered socket is part of the SocketPoller and will be checked by the next Wait operations according to the event flags passed when registered. * * The SocketPoller keeps a reference to the internal handle of registered socket, which should not be freed while it is registered in the SocketPooler. * @@ -107,17 +129,18 @@ namespace Nz * \remark The socket should not be freed while it is registered in the SocketPooler. * * \param socket Reference to the socket to register + * \param eventFlags Socket events to watch * * \return True if the socket is registered, false otherwise * * \see IsRegistered * \see UnregisterSocket */ - bool SocketPoller::RegisterSocket(AbstractSocket& socket) + bool SocketPoller::RegisterSocket(AbstractSocket& socket, SocketPollEventFlags eventFlags) { NazaraAssert(!IsRegistered(socket), "This socket is already registered in this SocketPoller"); - return m_impl->RegisterSocket(socket.GetNativeHandle()); + return m_impl->RegisterSocket(socket.GetNativeHandle(), eventFlags); } /*! @@ -145,7 +168,7 @@ namespace Nz * \brief Wait until any registered socket switches to a ready state. * * Waits a specific/undetermined amount of time until at least one socket part of the SocketPoller becomes ready. - * To query the ready state of the registered socket, use the IsReady function. + * To query the ready state of the registered socket, use the IsReadyToRead or IsReadyToWrite functions. * * \param msTimeout Maximum time to wait in milliseconds, 0 for infinity * diff --git a/src/Nazara/Network/Win32/SocketPollerImpl.cpp b/src/Nazara/Network/Win32/SocketPollerImpl.cpp index 292e8dfa5..405f80c8f 100644 --- a/src/Nazara/Network/Win32/SocketPollerImpl.cpp +++ b/src/Nazara/Network/Win32/SocketPollerImpl.cpp @@ -10,29 +10,43 @@ namespace Nz SocketPollerImpl::SocketPollerImpl() { #if !NAZARA_NETWORK_POLL_SUPPORT - FD_ZERO(&m_activeSockets); - FD_ZERO(&m_sockets); + FD_ZERO(&m_readSockets); + FD_ZERO(&m_readyToReadSockets); + FD_ZERO(&m_readyToWriteSockets); + FD_ZERO(&m_writeSockets); #endif } void SocketPollerImpl::Clear() { #if NAZARA_NETWORK_POLL_SUPPORT - m_activeSockets.clear(); m_allSockets.clear(); + m_readyToReadSockets.clear(); + m_readyToWriteSockets.clear(); m_sockets.clear(); #else - FD_ZERO(&m_activeSockets); - FD_ZERO(&m_sockets); + FD_ZERO(&m_readSockets); + FD_ZERO(&m_readyToReadSockets); + FD_ZERO(&m_readyToWriteSockets); + FD_ZERO(&m_writeSockets); #endif } - bool SocketPollerImpl::IsReady(SocketHandle socket) const + bool SocketPollerImpl::IsReadyToRead(SocketHandle socket) const { #if NAZARA_NETWORK_POLL_SUPPORT - return m_activeSockets.count(socket) != 0; + return m_readyToReadSockets.count(socket) != 0; #else - return FD_ISSET(socket, &m_activeSockets) != 0; + return FD_ISSET(socket, &m_readyToReadSockets) != 0; + #endif + } + + bool SocketPollerImpl::IsReadyToWrite(SocketHandle socket) const + { + #if NAZARA_NETWORK_POLL_SUPPORT + return m_readyToWriteSockets.count(socket) != 0; + #else + return FD_ISSET(socket, &m_readyToWriteSockets) != 0; #endif } @@ -41,31 +55,45 @@ namespace Nz #if NAZARA_NETWORK_POLL_SUPPORT return m_allSockets.count(socket) != 0; #else - return FD_ISSET(socket, &m_sockets) != 0; + return FD_ISSET(socket, &m_readSockets) != 0 || + FD_ISSET(socket, &m_writeSockets) != 0; #endif } - bool SocketPollerImpl::RegisterSocket(SocketHandle socket) + bool SocketPollerImpl::RegisterSocket(SocketHandle socket, SocketPollEventFlags eventFlags) { NazaraAssert(!IsRegistered(socket), "Socket is already registered"); #if NAZARA_NETWORK_POLL_SUPPORT PollSocket entry = { socket, - POLLRDNORM, + 0, 0 }; + if (eventFlags & SocketPollEvent_Read) + entry.events |= POLLRDNORM; + + if (eventFlags & SocketPollEvent_Write) + entry.events |= POLLWRNORM; + m_allSockets[socket] = m_sockets.size(); m_sockets.emplace_back(entry); #else - if (m_sockets.fd_count > FD_SETSIZE) + for (std::size_t i = 0; i < 2; ++i) { - NazaraError("Socket count exceeding FD_SETSIZE (" + String::Number(FD_SETSIZE) + ")"); - return false; - } + if (eventFlags & ((i == 0) ? SocketPollEvent_Read : SocketPollEvent_Write) == 0) + continue; - FD_SET(socket, &m_sockets); + fd_set& targetSet = (i == 0) ? m_readSockets : m_writeSockets; + if (targetSet.fd_count > FD_SETSIZE) + { + NazaraError("Socket count exceeding hard-coded FD_SETSIZE (" + String::Number(FD_SETSIZE) + ")"); + return false; + } + + FD_SET(socket, &targetSet); + } #endif return true; @@ -88,13 +116,16 @@ namespace Nz // Now move it properly (lastElement is invalid after the following line) and pop it m_sockets[entry] = std::move(m_sockets.back()); } - m_sockets.pop_back(); - m_activeSockets.erase(socket); + m_allSockets.erase(socket); + m_readyToReadSockets.erase(socket); + m_readyToWriteSockets.erase(socket); #else - FD_CLR(socket, &m_activeSockets); - FD_CLR(socket, &m_sockets); + FD_CLR(socket, &m_readSockets); + FD_CLR(socket, &m_readyToReadSockets); + FD_CLR(socket, &m_readyToWriteSockets); + FD_CLR(socket, &m_writeSockets); #endif } @@ -103,35 +134,18 @@ namespace Nz int activeSockets; #if NAZARA_NETWORK_POLL_SUPPORT - // Reset status of sockets - for (PollSocket& entry : m_sockets) - entry.revents = 0; - activeSockets = SocketImpl::Poll(m_sockets.data(), m_sockets.size(), static_cast(msTimeout), error); + x - m_activeSockets.clear(); - if (activeSockets > 0U) - { - int socketRemaining = activeSockets; - for (PollSocket& entry : m_sockets) - { - if (entry.revents & POLLRDNORM) - { - m_activeSockets.insert(entry.fd); - if (--socketRemaining == 0) - break; - } - } - } #else - - m_activeSockets = m_sockets; + m_readyToReadSockets = m_readSockets; + m_readyToWriteSockets = m_writeSockets; timeval tv; tv.tv_sec = static_cast(msTimeout / 1000ULL); tv.tv_usec = static_cast((msTimeout % 1000ULL) * 1000ULL); - activeSockets = ::select(0xDEADBEEF, &m_activeSockets, nullptr, nullptr, (msTimeout > 0) ? &tv : nullptr); //< The first argument is ignored on Windows + activeSockets = ::select(0xDEADBEEF, &m_readyToReadSockets, &m_readyToWriteSockets, nullptr, (msTimeout > 0) ? &tv : nullptr); //< The first argument is ignored on Windows if (activeSockets == SOCKET_ERROR) { if (error) diff --git a/src/Nazara/Network/Win32/SocketPollerImpl.hpp b/src/Nazara/Network/Win32/SocketPollerImpl.hpp index 0a745878d..a94af1372 100644 --- a/src/Nazara/Network/Win32/SocketPollerImpl.hpp +++ b/src/Nazara/Network/Win32/SocketPollerImpl.hpp @@ -25,24 +25,28 @@ namespace Nz void Clear(); - bool IsReady(SocketHandle socket) const; + bool IsReadyToRead(SocketHandle socket) const; + bool IsReadyToWrite(SocketHandle socket) const; bool IsRegistered(SocketHandle socket) const; - bool RegisterSocket(SocketHandle socket); + bool RegisterSocket(SocketHandle socket, SocketPollEventFlags eventFlags); void UnregisterSocket(SocketHandle socket); int Wait(UInt64 msTimeout, SocketError* error); private: #if NAZARA_NETWORK_POLL_SUPPORT - std::unordered_set m_activeSockets; + std::unordered_set m_readyToReadSockets; + std::unordered_set m_readyToWriteSockets; std::unordered_map m_allSockets; std::vector m_sockets; #else - fd_set m_sockets; - fd_set m_activeSockets; + fd_set m_readSockets; + fd_set m_readyToReadSockets; + fd_set m_readyToWriteSockets; + fd_set m_writeSockets; #endif }; } -#endif // NAZARA_SOCKETPOLLERIMPL_HPP \ No newline at end of file +#endif // NAZARA_SOCKETPOLLERIMPL_HPP From d516b0907b8247c61255b8c6041ad9bb67bb5742 Mon Sep 17 00:00:00 2001 From: Lynix Date: Tue, 16 May 2017 09:11:02 +0200 Subject: [PATCH 30/33] Network/ENetHost: Fix usage of SocketPoller --- src/Nazara/Network/ENetHost.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Nazara/Network/ENetHost.cpp b/src/Nazara/Network/ENetHost.cpp index d4d1be9f9..e12f80454 100644 --- a/src/Nazara/Network/ENetHost.cpp +++ b/src/Nazara/Network/ENetHost.cpp @@ -280,7 +280,7 @@ namespace Nz m_serviceTime = GetElapsedMilliseconds(); } - while (m_poller.IsReady(m_socket)); + while (m_poller.IsReadyToRead(m_socket)); return 0; } @@ -327,7 +327,7 @@ namespace Nz } } - m_poller.RegisterSocket(m_socket); + m_poller.RegisterSocket(m_socket, SocketPollEvent_Read); return true; } From 999cb13f000918cbefd598dc3b265d28179aa539 Mon Sep 17 00:00:00 2001 From: Lynix Date: Tue, 16 May 2017 09:19:37 +0200 Subject: [PATCH 31/33] Fix test compilation --- tests/Engine/Network/SocketPoller.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/Engine/Network/SocketPoller.cpp b/tests/Engine/Network/SocketPoller.cpp index 7081c1b30..2e880cf5c 100644 --- a/tests/Engine/Network/SocketPoller.cpp +++ b/tests/Engine/Network/SocketPoller.cpp @@ -27,7 +27,7 @@ SCENARIO("SocketPoller", "[NETWORK][SOCKETPOLLER]") WHEN("We register the server socket to the poller") { - REQUIRE(serverPoller.RegisterSocket(server)); + REQUIRE(serverPoller.RegisterSocket(server, Nz::SocketPollEvent_Read)); THEN("The poller should have registered our socket") { @@ -48,7 +48,7 @@ SCENARIO("SocketPoller", "[NETWORK][SOCKETPOLLER]") WHEN("We register the client socket to the poller") { - REQUIRE(serverPoller.RegisterSocket(serverToClient)); + REQUIRE(serverPoller.RegisterSocket(serverToClient, Nz::SocketPollEvent_Read)); THEN("The poller should have registered our socket") { @@ -65,7 +65,7 @@ SCENARIO("SocketPoller", "[NETWORK][SOCKETPOLLER]") REQUIRE(serverPoller.Wait(1000)); - CHECK(serverPoller.IsReady(serverToClient)); + CHECK(serverPoller.IsReadyToRead(serverToClient)); CHECK(serverToClient.Read(buffer.data(), buffer.size()) == sent); @@ -73,7 +73,7 @@ SCENARIO("SocketPoller", "[NETWORK][SOCKETPOLLER]") { REQUIRE_FALSE(serverPoller.Wait(100)); - REQUIRE_FALSE(serverPoller.IsReady(serverToClient)); + REQUIRE_FALSE(serverPoller.IsReadyToRead(serverToClient)); } } } From 1cdac50af215da6d033099ded414716fd08f3c26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Leclercq?= Date: Tue, 16 May 2017 10:09:09 +0200 Subject: [PATCH 32/33] Network/SocketPoller: Fix RegisterSocket on Windows --- src/Nazara/Network/Win32/SocketPollerImpl.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Nazara/Network/Win32/SocketPollerImpl.cpp b/src/Nazara/Network/Win32/SocketPollerImpl.cpp index 405f80c8f..e228da789 100644 --- a/src/Nazara/Network/Win32/SocketPollerImpl.cpp +++ b/src/Nazara/Network/Win32/SocketPollerImpl.cpp @@ -82,7 +82,7 @@ namespace Nz #else for (std::size_t i = 0; i < 2; ++i) { - if (eventFlags & ((i == 0) ? SocketPollEvent_Read : SocketPollEvent_Write) == 0) + if ((eventFlags & ((i == 0) ? SocketPollEvent_Read : SocketPollEvent_Write)) == 0) continue; fd_set& targetSet = (i == 0) ? m_readSockets : m_writeSockets; @@ -135,8 +135,6 @@ namespace Nz #if NAZARA_NETWORK_POLL_SUPPORT activeSockets = SocketImpl::Poll(m_sockets.data(), m_sockets.size(), static_cast(msTimeout), error); - x - #else m_readyToReadSockets = m_readSockets; m_readyToWriteSockets = m_writeSockets; From ba24181fd69d900881e71277bbecaee2bb7dbf67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Leclercq?= Date: Wed, 17 May 2017 11:29:55 +0200 Subject: [PATCH 33/33] Network/SocketPollerImpl: Fix possible weird behavior with SocketPoller --- src/Nazara/Network/Win32/SocketPollerImpl.cpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/Nazara/Network/Win32/SocketPollerImpl.cpp b/src/Nazara/Network/Win32/SocketPollerImpl.cpp index e228da789..3513ce722 100644 --- a/src/Nazara/Network/Win32/SocketPollerImpl.cpp +++ b/src/Nazara/Network/Win32/SocketPollerImpl.cpp @@ -136,14 +136,26 @@ namespace Nz #if NAZARA_NETWORK_POLL_SUPPORT activeSockets = SocketImpl::Poll(m_sockets.data(), m_sockets.size(), static_cast(msTimeout), error); #else - m_readyToReadSockets = m_readSockets; - m_readyToWriteSockets = m_writeSockets; + fd_set* readSet = nullptr; + fd_set* writeSet = nullptr; + + if (m_readSockets.fd_count > 0) + { + m_readyToReadSockets = m_readSockets; + readSet = &m_readyToReadSockets; + } + + if (m_writeSockets.fd_count > 0) + { + m_readyToWriteSockets = m_writeSockets; + readSet = &m_readyToWriteSockets; + } timeval tv; tv.tv_sec = static_cast(msTimeout / 1000ULL); tv.tv_usec = static_cast((msTimeout % 1000ULL) * 1000ULL); - activeSockets = ::select(0xDEADBEEF, &m_readyToReadSockets, &m_readyToWriteSockets, nullptr, (msTimeout > 0) ? &tv : nullptr); //< The first argument is ignored on Windows + activeSockets = ::select(0xDEADBEEF, readSet, writeSet, nullptr, (msTimeout > 0) ? &tv : nullptr); //< The first argument is ignored on Windows if (activeSockets == SOCKET_ERROR) { if (error)