From: Stephen Seo Date: Wed, 15 Nov 2017 06:36:04 +0000 (+0900) Subject: Fix bug with duplicate signatures X-Git-Tag: 1.0~60 X-Git-Url: https://git.seodisparate.com/stephenseo/LD52?a=commitdiff_plain;h=506b027655ef4b8980aebe8256ff984fcc448327;p=EntityComponentMetaSystem Fix bug with duplicate signatures Fixed bug where if forMatchingSignatures and forMatchingSignaturesPtr was called with some signatures in the TypeList being duplicates, then only the first duplicate and function pair would be called, and all other functions paired with other duplicate signatures would not be called. --- 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(); + std::size_t begin = s * i; + std::size_t end; + if(i == threadCount - 1) + { + end = multiMatchingEntities[index].size(); + } + 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); } - else + for(std::size_t i = 0; i < threadCount; ++i) { - end = s * (i + 1); + threads[i].join(); } - threads[i] = std::thread( - [this, &multiMatchingEntities, &funcTuple] - (std::size_t begin, std::size_t end) - { - for(std::size_t j = begin; j < end; ++j) - { - Helper::call(multiMatchingEntities[index][j], - *this, std::get(funcTuple)); - } - }, 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(); + std::size_t begin = s * i; + std::size_t end; + if(i == threadCount - 1) + { + end = multiMatchingEntities[index].size(); + } + 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); } - else + for(std::size_t i = 0; i < threadCount; ++i) { - end = s * (i + 1); + threads[i].join(); } - threads[i] = std::thread( - [this, &multiMatchingEntities, &funcTuple] - (std::size_t begin, std::size_t end) - { - for(std::size_t j = begin; j < end; ++j) - { - Helper::callPtr(multiMatchingEntities[index][j], - *this, std::get(funcTuple)); - } - }, 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); + } + }; }