diff --git a/src/EC/Manager.hpp b/src/EC/Manager.hpp index 51a981f..064feaa 100644 --- a/src/EC/Manager.hpp +++ b/src/EC/Manager.hpp @@ -30,6 +30,8 @@ #include "Meta/Combine.hpp" #include "Meta/Matching.hpp" +#include "Meta/ForEachWithIndex.hpp" +#include "Meta/ForEachDoubleTuple.hpp" #include "Bitset.hpp" namespace EC @@ -1216,20 +1218,20 @@ namespace EC the order of entities called is not guaranteed. Otherwise entities will be called in consecutive order by their ID. */ - template - void forMatchingSignatures(FuncTuple funcTuple, - std::size_t threadCount = 1) + template + void forMatchingSignatures( + FTuple fTuple, const std::size_t threadCount = 1) { std::vector > multiMatchingEntities( SigList::size); BitsetType signatureBitsets[SigList::size]; // generate bitsets for each signature - EC::Meta::forEach( - [this, &signatureBitsets] (auto signature) { - signatureBitsets[ - EC::Meta::IndexOf{} ] = - BitsetType::template generateBitset(); + EC::Meta::forEachWithIndex( + [this, &signatureBitsets] (auto signature, const auto index) { + signatureBitsets[index] = + BitsetType::template generateBitset + (); }); // find and store entities matching signatures @@ -1255,8 +1257,8 @@ namespace EC else { std::vector threads(threadCount); + std::mutex mutexes[SigList::size]; std::size_t s = currentSize / threadCount; - std::mutex sigsMutexes[SigList::size]; for(std::size_t i = 0; i < threadCount; ++i) { std::size_t begin = s * i; @@ -1270,94 +1272,94 @@ namespace EC end = s * (i + 1); } threads[i] = std::thread( - [this, &signatureBitsets, &multiMatchingEntities, - &sigsMutexes] - (std::size_t begin, std::size_t end) { - for(std::size_t eid = begin; eid < end; ++eid) + [this, &mutexes, &multiMatchingEntities, &signatureBitsets] + (std::size_t begin, std::size_t end) + { + for(std::size_t j = begin; j < end; ++j) { - if(!isAlive(eid)) + if(!isAlive(j)) { continue; } - for(std::size_t i = 0; i < SigList::size; ++i) + for(std::size_t k = 0; k < SigList::size; ++k) { - if((signatureBitsets[i] - & std::get(entities[eid])) - == signatureBitsets[i]) + if((signatureBitsets[k] + & std::get(entities[j])) + == signatureBitsets[k]) { std::lock_guard guard( - sigsMutexes[i]); - multiMatchingEntities[i].push_back(eid); - + mutexes[k]); + multiMatchingEntities[k].push_back(j); } } } - }, - begin, end); + }, begin, end); } for(std::size_t i = 0; i < threadCount; ++i) { threads[i].join(); } } - + // call functions on matching entities - EC::Meta::forEach( - [this, &multiMatchingEntities, &funcTuple, &threadCount] - (auto signature) { - using SignatureComponents = - typename EC::Meta::Matching< - decltype(signature), ComponentsList>::type; - using Helper = - EC::Meta::Morph< - SignatureComponents, - ForMatchingSignatureHelper<> >; - using Index = EC::Meta::IndexOf; - constexpr std::size_t index = Index{}; - if(threadCount <= 1) + EC::Meta::forEachDoubleTuple( + EC::Meta::Morph >{}, + fTuple, + [this, &multiMatchingEntities, &threadCount] + (auto sig, auto func, auto index) { - for(auto iter = multiMatchingEntities[index].begin(); - iter != multiMatchingEntities[index].end(); ++iter) + using SignatureComponents = + typename EC::Meta::Matching< + decltype(sig), ComponentsList>::type; + using Helper = + EC::Meta::Morph< + SignatureComponents, + ForMatchingSignatureHelper<> >; + if(threadCount <= 1) { - Helper::call(*iter, *this, - std::get(funcTuple)); + for(const auto& id : multiMatchingEntities[index]) + { + Helper::call(id, *this, func); + } } - } - else - { - std::vector threads(threadCount); - std::size_t s = multiMatchingEntities[index].size() - / threadCount; - for(std::size_t i = 0; i < threadCount; ++i) + else { - std::size_t begin = s * i; - std::size_t end; - if(i == threadCount - 1) + std::vector threads(threadCount); + std::size_t s = multiMatchingEntities[index].size() + / threadCount; + for(std::size_t i = 0; i < threadCount; ++i) { - end = multiMatchingEntities[index].size(); - } - else - { - end = s * (i + 1); - } - threads[i] = std::thread( - [this, &multiMatchingEntities, &funcTuple] - (std::size_t begin, std::size_t end) - { - for(std::size_t j = begin; j < end; ++j) + std::size_t begin = s * i; + std::size_t end; + if(i == threadCount - 1) { - Helper::call(multiMatchingEntities[index][j], - *this, std::get(funcTuple)); + end = multiMatchingEntities[index].size(); } - }, begin, end); - } - for(std::size_t i = 0; i < threadCount; ++i) - { - threads[i].join(); + else + { + end = s * (i + 1); + } + threads[i] = std::thread( + [this, &multiMatchingEntities, &index, &func] + (std::size_t begin, std::size_t end) + { + for(std::size_t j = begin; j < end; + ++j) + { + Helper::call( + multiMatchingEntities[index][j], + *this, + func); + } + }, begin, end); + } + for(std::size_t i = 0; i < threadCount; ++i) + { + threads[i].join(); + } } } - }); + ); } /*! @@ -1400,8 +1402,8 @@ namespace EC the order of entities called is not guaranteed. Otherwise entities will be called in consecutive order by their ID. */ - template - void forMatchingSignaturesPtr(FuncTuple funcTuple, + template + void forMatchingSignaturesPtr(FTuple fTuple, std::size_t threadCount = 1) { std::vector > multiMatchingEntities( @@ -1409,11 +1411,11 @@ namespace EC BitsetType signatureBitsets[SigList::size]; // generate bitsets for each signature - EC::Meta::forEach( - [this, &signatureBitsets] (auto signature) { - signatureBitsets[ - EC::Meta::IndexOf{} ] = - BitsetType::template generateBitset(); + EC::Meta::forEachWithIndex( + [this, &signatureBitsets] (auto signature, const auto index) { + signatureBitsets[index] = + BitsetType::template generateBitset + (); }); // find and store entities matching signatures @@ -1439,8 +1441,8 @@ namespace EC else { std::vector threads(threadCount); + std::mutex mutexes[SigList::size]; std::size_t s = currentSize / threadCount; - std::mutex sigsMutexes[SigList::size]; for(std::size_t i = 0; i < threadCount; ++i) { std::size_t begin = s * i; @@ -1454,94 +1456,94 @@ namespace EC end = s * (i + 1); } threads[i] = std::thread( - [this, &signatureBitsets, &multiMatchingEntities, - &sigsMutexes] - (std::size_t begin, std::size_t end) { - for(std::size_t eid = begin; eid < end; ++eid) + [this, &mutexes, &multiMatchingEntities, &signatureBitsets] + (std::size_t begin, std::size_t end) + { + for(std::size_t j = begin; j < end; ++j) { - if(!isAlive(eid)) + if(!isAlive(j)) { continue; } - for(std::size_t i = 0; i < SigList::size; ++i) + for(std::size_t k = 0; k < SigList::size; ++k) { - if((signatureBitsets[i] - & std::get(entities[eid])) - == signatureBitsets[i]) + if((signatureBitsets[k] + & std::get(entities[j])) + == signatureBitsets[k]) { std::lock_guard guard( - sigsMutexes[i]); - multiMatchingEntities[i].push_back(eid); - + mutexes[k]); + multiMatchingEntities[k].push_back(j); } } } - }, - begin, end); + }, begin, end); } for(std::size_t i = 0; i < threadCount; ++i) { threads[i].join(); } } - + // call functions on matching entities - EC::Meta::forEach( - [this, &multiMatchingEntities, &funcTuple, &threadCount] - (auto signature) { - using SignatureComponents = - typename EC::Meta::Matching< - decltype(signature), ComponentsList>::type; - using Helper = - EC::Meta::Morph< - SignatureComponents, - ForMatchingSignatureHelper<> >; - using Index = EC::Meta::IndexOf; - constexpr std::size_t index = Index{}; - if(threadCount <= 1) + EC::Meta::forEachDoubleTuple( + EC::Meta::Morph >{}, + fTuple, + [this, &multiMatchingEntities, &threadCount] + (auto sig, auto func, auto index) { - for(auto iter = multiMatchingEntities[index].begin(); - iter != multiMatchingEntities[index].end(); ++iter) + using SignatureComponents = + typename EC::Meta::Matching< + decltype(sig), ComponentsList>::type; + using Helper = + EC::Meta::Morph< + SignatureComponents, + ForMatchingSignatureHelper<> >; + if(threadCount <= 1) { - Helper::callPtr(*iter, *this, - std::get(funcTuple)); + for(const auto& id : multiMatchingEntities[index]) + { + Helper::callPtr(id, *this, func); + } } - } - else - { - std::vector threads(threadCount); - std::size_t s = multiMatchingEntities[index].size() - / threadCount; - for(std::size_t i = 0; i < threadCount; ++i) + else { - std::size_t begin = s * i; - std::size_t end; - if(i == threadCount - 1) + std::vector threads(threadCount); + std::size_t s = multiMatchingEntities[index].size() + / threadCount; + for(std::size_t i = 0; i < threadCount; ++i) { - end = multiMatchingEntities[index].size(); - } - else - { - end = s * (i + 1); - } - threads[i] = std::thread( - [this, &multiMatchingEntities, &funcTuple] - (std::size_t begin, std::size_t end) - { - for(std::size_t j = begin; j < end; ++j) + std::size_t begin = s * i; + std::size_t end; + if(i == threadCount - 1) { - Helper::callPtr(multiMatchingEntities[index][j], - *this, std::get(funcTuple)); + end = multiMatchingEntities[index].size(); } - }, begin, end); - } - for(std::size_t i = 0; i < threadCount; ++i) - { - threads[i].join(); + else + { + end = s * (i + 1); + } + threads[i] = std::thread( + [this, &multiMatchingEntities, &index, &func] + (std::size_t begin, std::size_t end) + { + for(std::size_t j = begin; j < end; + ++j) + { + Helper::callPtr( + multiMatchingEntities[index][j], + *this, + func); + } + }, begin, end); + } + for(std::size_t i = 0; i < threadCount; ++i) + { + threads[i].join(); + } } } - }); + ); } /*! diff --git a/src/EC/Meta/ForEachDoubleTuple.hpp b/src/EC/Meta/ForEachDoubleTuple.hpp new file mode 100644 index 0000000..8e71ad6 --- /dev/null +++ b/src/EC/Meta/ForEachDoubleTuple.hpp @@ -0,0 +1,45 @@ + +// This work derives from Vittorio Romeo's code used for cppcon 2015 licensed +// under the Academic Free License. +// His code is available here: https://github.com/SuperV1234/cppcon2015 + + +#ifndef EC_META_FOR_EACH_DOUBLE_TUPLE_HPP +#define EC_META_FOR_EACH_DOUBLE_TUPLE_HPP + +#include +#include +#include "Morph.hpp" + +namespace EC +{ + namespace Meta + { + template + constexpr void forEachDoubleTupleHelper( + Function&& function, TupleFirst t0, TupleSecond t1, + std::index_sequence) + { + return (void)std::initializer_list{(function( + std::get(t0), std::get(t1), Indices), 0)...}; + } + + template + constexpr void forEachDoubleTuple( + TupleFirst&& t0, TupleSecond&& t1, Function&& function) + { + using TTupleSize = std::tuple_size; + using IndexSeq = std::make_index_sequence; + + return forEachDoubleTupleHelper( + std::forward(function), + std::forward(t0), + std::forward(t1), + IndexSeq{}); + } + } +} + +#endif + diff --git a/src/EC/Meta/ForEachWithIndex.hpp b/src/EC/Meta/ForEachWithIndex.hpp new file mode 100644 index 0000000..2c05f6f --- /dev/null +++ b/src/EC/Meta/ForEachWithIndex.hpp @@ -0,0 +1,40 @@ + +// This work derives from Vittorio Romeo's code used for cppcon 2015 licensed +// under the Academic Free License. +// His code is available here: https://github.com/SuperV1234/cppcon2015 + + +#ifndef EC_META_FOR_EACH_WITH_INDEX_HPP +#define EC_META_FOR_EACH_WITH_INDEX_HPP + +#include +#include +#include "Morph.hpp" + +namespace EC +{ + namespace Meta + { + template + constexpr void forEachWithIndexHelper( + Function&& function, TTuple tuple, std::index_sequence) + { + return (void)std::initializer_list{(function(std::move( + std::get(tuple)), Indices), 0)...}; + } + + template + constexpr void forEachWithIndex(Function&& function) + { + using TTuple = EC::Meta::Morph >; + using TTupleSize = std::tuple_size; + using IndexSeq = std::make_index_sequence; + + return forEachWithIndexHelper( + std::forward(function), TTuple{}, IndexSeq{}); + } + } +} + +#endif + diff --git a/src/EC/Meta/Meta.hpp b/src/EC/Meta/Meta.hpp index f7f8ca9..c64e452 100644 --- a/src/EC/Meta/Meta.hpp +++ b/src/EC/Meta/Meta.hpp @@ -12,5 +12,7 @@ #include "IndexOf.hpp" #include "Morph.hpp" #include "ForEach.hpp" +#include "ForEachWithIndex.hpp" +#include "ForEachDoubleTuple.hpp" #include "Matching.hpp" diff --git a/src/test/ECTest.cpp b/src/test/ECTest.cpp index ca084ff..c5d8c9a 100644 --- a/src/test/ECTest.cpp +++ b/src/test/ECTest.cpp @@ -789,6 +789,35 @@ TEST(EC, ForMatchingSignatures) EXPECT_EQ(13, manager.getEntityData(eid).y); } } + + // test duplicate signatures + manager.forMatchingSignatures, + TypeList > >( + std::make_tuple( + [] (std::size_t eid, C0& c0, C1& c1) { + c0.x = 9999; + c0.y = 9999; + c1.vx = 9999; + c1.vy = 9999; + }, + [] (std::size_t eid, C0& c0, C1& c1) { + c0.x = 10000; + c0.y = 10000; + c1.vx = 10000; + c1.vy = 10000; + } + )); + for(auto id : e) + { + if(id != first && id != last) + { + EXPECT_EQ(10000, manager.getEntityData(id).x); + EXPECT_EQ(10000, manager.getEntityData(id).y); + EXPECT_EQ(10000, manager.getEntityData(id).vx); + EXPECT_EQ(10000, manager.getEntityData(id).vy); + } + }; } TEST(EC, forMatchingPtrs) @@ -899,5 +928,36 @@ TEST(EC, forMatchingPtrs) c.y = 0; } } + + // test duplicate signatures + const auto setTo9999 = [] (std::size_t eid, C0& c0, C1& c1) { + c0.x = 9999; + c0.y = 9999; + c1.vx = 9999; + c1.vy = 9999; + }; + const auto setTo10000 = [] (std::size_t eid, C0& c0, C1& c1) { + c0.x = 10000; + c0.y = 10000; + c1.vx = 10000; + c1.vy = 10000; + }; + manager.forMatchingSignaturesPtr, + TypeList > >( + std::make_tuple( + &setTo9999, + &setTo10000 + )); + for(auto id : e) + { + if(id != first && id != last) + { + EXPECT_EQ(10000, manager.getEntityData(id).x); + EXPECT_EQ(10000, manager.getEntityData(id).y); + EXPECT_EQ(10000, manager.getEntityData(id).vx); + EXPECT_EQ(10000, manager.getEntityData(id).vy); + } + }; }