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.
This commit is contained in:
Stephen Seo 2017-11-15 15:36:04 +09:00
parent 6b0e950c84
commit 506b027655
5 changed files with 290 additions and 141 deletions

View file

@ -30,6 +30,8 @@
#include "Meta/Combine.hpp" #include "Meta/Combine.hpp"
#include "Meta/Matching.hpp" #include "Meta/Matching.hpp"
#include "Meta/ForEachWithIndex.hpp"
#include "Meta/ForEachDoubleTuple.hpp"
#include "Bitset.hpp" #include "Bitset.hpp"
namespace EC namespace EC
@ -1216,20 +1218,20 @@ namespace EC
the order of entities called is not guaranteed. Otherwise entities the order of entities called is not guaranteed. Otherwise entities
will be called in consecutive order by their ID. will be called in consecutive order by their ID.
*/ */
template <typename SigList, typename FuncTuple> template <typename SigList, typename FTuple>
void forMatchingSignatures(FuncTuple funcTuple, void forMatchingSignatures(
std::size_t threadCount = 1) FTuple fTuple, const std::size_t threadCount = 1)
{ {
std::vector<std::vector<std::size_t> > multiMatchingEntities( std::vector<std::vector<std::size_t> > multiMatchingEntities(
SigList::size); SigList::size);
BitsetType signatureBitsets[SigList::size]; BitsetType signatureBitsets[SigList::size];
// generate bitsets for each signature // generate bitsets for each signature
EC::Meta::forEach<SigList>( EC::Meta::forEachWithIndex<SigList>(
[this, &signatureBitsets] (auto signature) { [this, &signatureBitsets] (auto signature, const auto index) {
signatureBitsets[ signatureBitsets[index] =
EC::Meta::IndexOf<decltype(signature), SigList>{} ] = BitsetType::template generateBitset
BitsetType::template generateBitset<decltype(signature)>(); <decltype(signature)>();
}); });
// find and store entities matching signatures // find and store entities matching signatures
@ -1255,8 +1257,8 @@ namespace EC
else else
{ {
std::vector<std::thread> threads(threadCount); std::vector<std::thread> threads(threadCount);
std::mutex mutexes[SigList::size];
std::size_t s = currentSize / threadCount; std::size_t s = currentSize / threadCount;
std::mutex sigsMutexes[SigList::size];
for(std::size_t i = 0; i < threadCount; ++i) for(std::size_t i = 0; i < threadCount; ++i)
{ {
std::size_t begin = s * i; std::size_t begin = s * i;
@ -1270,30 +1272,28 @@ namespace EC
end = s * (i + 1); end = s * (i + 1);
} }
threads[i] = std::thread( threads[i] = std::thread(
[this, &signatureBitsets, &multiMatchingEntities, [this, &mutexes, &multiMatchingEntities, &signatureBitsets]
&sigsMutexes] (std::size_t begin, std::size_t end)
(std::size_t begin, std::size_t end) { {
for(std::size_t eid = begin; eid < end; ++eid) for(std::size_t j = begin; j < end; ++j)
{ {
if(!isAlive(eid)) if(!isAlive(j))
{ {
continue; continue;
} }
for(std::size_t i = 0; i < SigList::size; ++i) for(std::size_t k = 0; k < SigList::size; ++k)
{ {
if((signatureBitsets[i] if((signatureBitsets[k]
& std::get<BitsetType>(entities[eid])) & std::get<BitsetType>(entities[j]))
== signatureBitsets[i]) == signatureBitsets[k])
{ {
std::lock_guard<std::mutex> guard( std::lock_guard<std::mutex> guard(
sigsMutexes[i]); mutexes[k]);
multiMatchingEntities[i].push_back(eid); multiMatchingEntities[k].push_back(j);
} }
} }
} }
}, }, begin, end);
begin, end);
} }
for(std::size_t i = 0; i < threadCount; ++i) for(std::size_t i = 0; i < threadCount; ++i)
{ {
@ -1302,62 +1302,64 @@ namespace EC
} }
// call functions on matching entities // call functions on matching entities
EC::Meta::forEach<SigList>( EC::Meta::forEachDoubleTuple(
[this, &multiMatchingEntities, &funcTuple, &threadCount] EC::Meta::Morph<SigList, std::tuple<> >{},
(auto signature) { fTuple,
using SignatureComponents = [this, &multiMatchingEntities, &threadCount]
typename EC::Meta::Matching< (auto sig, auto func, auto index)
decltype(signature), ComponentsList>::type;
using Helper =
EC::Meta::Morph<
SignatureComponents,
ForMatchingSignatureHelper<> >;
using Index = EC::Meta::IndexOf<decltype(signature),
SigList>;
constexpr std::size_t index = Index{};
if(threadCount <= 1)
{ {
for(auto iter = multiMatchingEntities[index].begin(); using SignatureComponents =
iter != multiMatchingEntities[index].end(); ++iter) typename EC::Meta::Matching<
decltype(sig), ComponentsList>::type;
using Helper =
EC::Meta::Morph<
SignatureComponents,
ForMatchingSignatureHelper<> >;
if(threadCount <= 1)
{ {
Helper::call(*iter, *this, for(const auto& id : multiMatchingEntities[index])
std::get<index>(funcTuple)); {
Helper::call(id, *this, func);
}
} }
} else
else
{
std::vector<std::thread> threads(threadCount);
std::size_t s = multiMatchingEntities[index].size()
/ threadCount;
for(std::size_t i = 0; i < threadCount; ++i)
{ {
std::size_t begin = s * i; std::vector<std::thread> threads(threadCount);
std::size_t end; std::size_t s = multiMatchingEntities[index].size()
if(i == threadCount - 1) / threadCount;
for(std::size_t i = 0; i < threadCount; ++i)
{ {
end = multiMatchingEntities[index].size(); std::size_t begin = s * i;
} std::size_t end;
else if(i == threadCount - 1)
{
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)
{ {
Helper::call(multiMatchingEntities[index][j], end = multiMatchingEntities[index].size();
*this, std::get<index>(funcTuple));
} }
}, 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, &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 the order of entities called is not guaranteed. Otherwise entities
will be called in consecutive order by their ID. will be called in consecutive order by their ID.
*/ */
template <typename SigList, typename FuncTuple> template <typename SigList, typename FTuple>
void forMatchingSignaturesPtr(FuncTuple funcTuple, void forMatchingSignaturesPtr(FTuple fTuple,
std::size_t threadCount = 1) std::size_t threadCount = 1)
{ {
std::vector<std::vector<std::size_t> > multiMatchingEntities( std::vector<std::vector<std::size_t> > multiMatchingEntities(
@ -1409,11 +1411,11 @@ namespace EC
BitsetType signatureBitsets[SigList::size]; BitsetType signatureBitsets[SigList::size];
// generate bitsets for each signature // generate bitsets for each signature
EC::Meta::forEach<SigList>( EC::Meta::forEachWithIndex<SigList>(
[this, &signatureBitsets] (auto signature) { [this, &signatureBitsets] (auto signature, const auto index) {
signatureBitsets[ signatureBitsets[index] =
EC::Meta::IndexOf<decltype(signature), SigList>{} ] = BitsetType::template generateBitset
BitsetType::template generateBitset<decltype(signature)>(); <decltype(signature)>();
}); });
// find and store entities matching signatures // find and store entities matching signatures
@ -1439,8 +1441,8 @@ namespace EC
else else
{ {
std::vector<std::thread> threads(threadCount); std::vector<std::thread> threads(threadCount);
std::mutex mutexes[SigList::size];
std::size_t s = currentSize / threadCount; std::size_t s = currentSize / threadCount;
std::mutex sigsMutexes[SigList::size];
for(std::size_t i = 0; i < threadCount; ++i) for(std::size_t i = 0; i < threadCount; ++i)
{ {
std::size_t begin = s * i; std::size_t begin = s * i;
@ -1454,30 +1456,28 @@ namespace EC
end = s * (i + 1); end = s * (i + 1);
} }
threads[i] = std::thread( threads[i] = std::thread(
[this, &signatureBitsets, &multiMatchingEntities, [this, &mutexes, &multiMatchingEntities, &signatureBitsets]
&sigsMutexes] (std::size_t begin, std::size_t end)
(std::size_t begin, std::size_t end) { {
for(std::size_t eid = begin; eid < end; ++eid) for(std::size_t j = begin; j < end; ++j)
{ {
if(!isAlive(eid)) if(!isAlive(j))
{ {
continue; continue;
} }
for(std::size_t i = 0; i < SigList::size; ++i) for(std::size_t k = 0; k < SigList::size; ++k)
{ {
if((signatureBitsets[i] if((signatureBitsets[k]
& std::get<BitsetType>(entities[eid])) & std::get<BitsetType>(entities[j]))
== signatureBitsets[i]) == signatureBitsets[k])
{ {
std::lock_guard<std::mutex> guard( std::lock_guard<std::mutex> guard(
sigsMutexes[i]); mutexes[k]);
multiMatchingEntities[i].push_back(eid); multiMatchingEntities[k].push_back(j);
} }
} }
} }
}, }, begin, end);
begin, end);
} }
for(std::size_t i = 0; i < threadCount; ++i) for(std::size_t i = 0; i < threadCount; ++i)
{ {
@ -1486,62 +1486,64 @@ namespace EC
} }
// call functions on matching entities // call functions on matching entities
EC::Meta::forEach<SigList>( EC::Meta::forEachDoubleTuple(
[this, &multiMatchingEntities, &funcTuple, &threadCount] EC::Meta::Morph<SigList, std::tuple<> >{},
(auto signature) { fTuple,
using SignatureComponents = [this, &multiMatchingEntities, &threadCount]
typename EC::Meta::Matching< (auto sig, auto func, auto index)
decltype(signature), ComponentsList>::type;
using Helper =
EC::Meta::Morph<
SignatureComponents,
ForMatchingSignatureHelper<> >;
using Index = EC::Meta::IndexOf<decltype(signature),
SigList>;
constexpr std::size_t index = Index{};
if(threadCount <= 1)
{ {
for(auto iter = multiMatchingEntities[index].begin(); using SignatureComponents =
iter != multiMatchingEntities[index].end(); ++iter) typename EC::Meta::Matching<
decltype(sig), ComponentsList>::type;
using Helper =
EC::Meta::Morph<
SignatureComponents,
ForMatchingSignatureHelper<> >;
if(threadCount <= 1)
{ {
Helper::callPtr(*iter, *this, for(const auto& id : multiMatchingEntities[index])
std::get<index>(funcTuple)); {
Helper::callPtr(id, *this, func);
}
} }
} else
else
{
std::vector<std::thread> threads(threadCount);
std::size_t s = multiMatchingEntities[index].size()
/ threadCount;
for(std::size_t i = 0; i < threadCount; ++i)
{ {
std::size_t begin = s * i; std::vector<std::thread> threads(threadCount);
std::size_t end; std::size_t s = multiMatchingEntities[index].size()
if(i == threadCount - 1) / threadCount;
for(std::size_t i = 0; i < threadCount; ++i)
{ {
end = multiMatchingEntities[index].size(); std::size_t begin = s * i;
} std::size_t end;
else if(i == threadCount - 1)
{
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)
{ {
Helper::callPtr(multiMatchingEntities[index][j], end = multiMatchingEntities[index].size();
*this, std::get<index>(funcTuple));
} }
}, 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, &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();
}
} }
} }
}); );
} }
/*! /*!

View file

@ -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 <tuple>
#include <utility>
#include "Morph.hpp"
namespace EC
{
namespace Meta
{
template <typename Function, typename TupleFirst,
typename TupleSecond, std::size_t... Indices>
constexpr void forEachDoubleTupleHelper(
Function&& function, TupleFirst t0, TupleSecond t1,
std::index_sequence<Indices...>)
{
return (void)std::initializer_list<int>{(function(
std::get<Indices>(t0), std::get<Indices>(t1), Indices), 0)...};
}
template <typename TupleFirst, typename TupleSecond, typename Function>
constexpr void forEachDoubleTuple(
TupleFirst&& t0, TupleSecond&& t1, Function&& function)
{
using TTupleSize = std::tuple_size<TupleFirst>;
using IndexSeq = std::make_index_sequence<TTupleSize::value>;
return forEachDoubleTupleHelper(
std::forward<Function>(function),
std::forward<TupleFirst>(t0),
std::forward<TupleSecond>(t1),
IndexSeq{});
}
}
}
#endif

View file

@ -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 <tuple>
#include <utility>
#include "Morph.hpp"
namespace EC
{
namespace Meta
{
template <typename Function, typename TTuple, std::size_t... Indices>
constexpr void forEachWithIndexHelper(
Function&& function, TTuple tuple, std::index_sequence<Indices...>)
{
return (void)std::initializer_list<int>{(function(std::move(
std::get<Indices>(tuple)), Indices), 0)...};
}
template <typename TTypeList, typename Function>
constexpr void forEachWithIndex(Function&& function)
{
using TTuple = EC::Meta::Morph<TTypeList, std::tuple<> >;
using TTupleSize = std::tuple_size<TTuple>;
using IndexSeq = std::make_index_sequence<TTupleSize::value>;
return forEachWithIndexHelper(
std::forward<Function>(function), TTuple{}, IndexSeq{});
}
}
}
#endif

View file

@ -12,5 +12,7 @@
#include "IndexOf.hpp" #include "IndexOf.hpp"
#include "Morph.hpp" #include "Morph.hpp"
#include "ForEach.hpp" #include "ForEach.hpp"
#include "ForEachWithIndex.hpp"
#include "ForEachDoubleTuple.hpp"
#include "Matching.hpp" #include "Matching.hpp"

View file

@ -789,6 +789,35 @@ TEST(EC, ForMatchingSignatures)
EXPECT_EQ(13, manager.getEntityData<C0>(eid).y); EXPECT_EQ(13, manager.getEntityData<C0>(eid).y);
} }
} }
// test duplicate signatures
manager.forMatchingSignatures<TypeList<
TypeList<C0, C1>,
TypeList<C0, C1> > >(
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<C0>(id).x);
EXPECT_EQ(10000, manager.getEntityData<C0>(id).y);
EXPECT_EQ(10000, manager.getEntityData<C1>(id).vx);
EXPECT_EQ(10000, manager.getEntityData<C1>(id).vy);
}
};
} }
TEST(EC, forMatchingPtrs) TEST(EC, forMatchingPtrs)
@ -899,5 +928,36 @@ TEST(EC, forMatchingPtrs)
c.y = 0; 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<
TypeList<C0, C1>,
TypeList<C0, C1> > >(
std::make_tuple(
&setTo9999,
&setTo10000
));
for(auto id : e)
{
if(id != first && id != last)
{
EXPECT_EQ(10000, manager.getEntityData<C0>(id).x);
EXPECT_EQ(10000, manager.getEntityData<C0>(id).y);
EXPECT_EQ(10000, manager.getEntityData<C1>(id).vx);
EXPECT_EQ(10000, manager.getEntityData<C1>(id).vy);
}
};
} }