Merge branch 'master' into cxx17

This commit is contained in:
Stephen Seo 2019-11-06 16:35:39 +09:00
commit 25c864dd8a
4 changed files with 433 additions and 22 deletions

1
.gitignore vendored
View file

@ -4,3 +4,4 @@ build*/
doxygen_html/ doxygen_html/
compile_commands.json compile_commands.json
tags tags
.clangd/

View file

@ -75,6 +75,28 @@ namespace EC
return bitset; return bitset;
} }
template <typename IntegralType>
auto getCombinedBit(const IntegralType& i) {
static_assert(std::is_integral<IntegralType>::value,
"Parameter must be an integral type");
if(i >= Combined::size || i < 0) {
return (*this)[Combined::size];
} else {
return (*this)[i];
}
}
template <typename IntegralType>
auto getCombinedBit(const IntegralType& i) const {
static_assert(std::is_integral<IntegralType>::value,
"Parameter must be an integral type");
if(i >= Combined::size || i < 0) {
return (*this)[Combined::size];
} else {
return (*this)[i];
}
}
}; };
} }

View file

@ -12,6 +12,7 @@
#include <cstddef> #include <cstddef>
#include <vector> #include <vector>
#include <deque>
#include <tuple> #include <tuple>
#include <utility> #include <utility>
#include <functional> #include <functional>
@ -66,14 +67,14 @@ namespace EC
template <typename... Types> template <typename... Types>
struct Storage struct Storage
{ {
using type = std::tuple<std::vector<Types>...>; using type = std::tuple<std::deque<Types>...>;
}; };
using ComponentsStorage = using ComponentsStorage =
typename EC::Meta::Morph<ComponentsList, Storage<> >::type; typename EC::Meta::Morph<ComponentsList, Storage<> >::type;
// Entity: isAlive, ComponentsTags Info // Entity: isAlive, ComponentsTags Info
using EntitiesTupleType = std::tuple<bool, BitsetType>; using EntitiesTupleType = std::tuple<bool, BitsetType>;
using EntitiesType = std::vector<EntitiesTupleType>; using EntitiesType = std::deque<EntitiesTupleType>;
EntitiesType entities; EntitiesType entities;
ComponentsStorage componentsStorage; ComponentsStorage componentsStorage;
@ -102,7 +103,7 @@ namespace EC
} }
EC::Meta::forEach<ComponentsList>([this, newCapacity] (auto t) { EC::Meta::forEach<ComponentsList>([this, newCapacity] (auto t) {
std::get<std::vector<decltype(t)> >( std::get<std::deque<decltype(t)> >(
this->componentsStorage).resize(newCapacity); this->componentsStorage).resize(newCapacity);
}); });
@ -243,7 +244,7 @@ namespace EC
{ {
if constexpr (EC::Meta::Contains<Component, Components>::value) if constexpr (EC::Meta::Contains<Component, Components>::value)
{ {
return &std::get<std::vector<Component> >(componentsStorage) return &std::get<std::deque<Component> >(componentsStorage)
.at(index); .at(index);
} }
else else
@ -291,7 +292,7 @@ namespace EC
{ {
if constexpr (EC::Meta::Contains<Component, Components>::value) if constexpr (EC::Meta::Contains<Component, Components>::value)
{ {
return &std::get<std::vector<Component> >(componentsStorage) return &std::get<std::deque<Component> >(componentsStorage)
.at(index); .at(index);
} }
else else
@ -400,7 +401,7 @@ namespace EC
entities[entityID] entities[entityID]
).template getComponentBit<Component>() = true; ).template getComponentBit<Component>() = true;
std::get<std::vector<Component> >(componentsStorage)[entityID] std::get<std::deque<Component> >(componentsStorage)[entityID]
= std::move(component); = std::move(component);
} }
} }
@ -491,6 +492,23 @@ namespace EC
} }
} }
/*!
\brief Resets the Manager, removing all entities.
Some data may persist but will be overwritten when new entities
are added. Thus, do not depend on data to persist after a call to
reset().
*/
void reset()
{
clearForMatchingFunctions();
currentSize = 0;
currentCapacity = 0;
deletedSet.clear();
resize(EC_INIT_ENTITIES_SIZE);
}
private: private:
template <typename... Types> template <typename... Types>
struct ForMatchingSignatureHelper struct ForMatchingSignatureHelper
@ -566,7 +584,7 @@ namespace EC
The second parameter is default nullptr and will be passed to the The second parameter is default nullptr and will be passed to the
function call as the second parameter as a means of providing function call as the second parameter as a means of providing
context (useful when the function is not a lambda function). The context (useful when the function is not a lambda function). The
third parameter is default 1 (not multi-threaded). If the third third parameter is default 1 (not multi-threaded). If the third
parameter threadCount is set to a value greater than 1, then parameter threadCount is set to a value greater than 1, then
threadCount threads will be used. Note that multi-threading is threadCount threads will be used. Note that multi-threading is
based on splitting the task of calling the function across sections based on splitting the task of calling the function across sections
@ -867,7 +885,7 @@ namespace EC
std::make_tuple( std::make_tuple(
signatureBitset, signatureBitset,
context, context,
[function, helper, this] [function, helper, this]
(std::size_t threadCount, (std::size_t threadCount,
std::vector<std::size_t> matching, std::vector<std::size_t> matching,
void* context) void* context)
@ -1369,7 +1387,7 @@ namespace EC
threads[i].join(); threads[i].join();
} }
} }
// call functions on matching entities // call functions on matching entities
EC::Meta::forEachDoubleTuple( EC::Meta::forEachDoubleTuple(
EC::Meta::Morph<SigList, std::tuple<> >{}, EC::Meta::Morph<SigList, std::tuple<> >{},
@ -1565,7 +1583,7 @@ namespace EC
threads[i].join(); threads[i].join();
} }
} }
// call functions on matching entities // call functions on matching entities
EC::Meta::forEachDoubleTuple( EC::Meta::forEachDoubleTuple(
EC::Meta::Morph<SigList, std::tuple<> >{}, EC::Meta::Morph<SigList, std::tuple<> >{},
@ -1635,21 +1653,126 @@ namespace EC
); );
} }
typedef void ForMatchingFn(std::size_t, Manager<ComponentsList, TagsList>*, void*);
/*! /*!
\brief Resets the Manager, removing all entities. * \brief A simple version of forMatchingSignature()
*
* This function behaves like forMatchingSignature(), but instead of
* providing a function with each requested component as a parameter,
* the function receives a pointer to the manager itself, with which to
* query component/tag data.
*/
template <typename Signature>
void forMatchingSimple(ForMatchingFn fn, void *userData = nullptr, std::size_t threadCount = 1) {
const BitsetType signatureBitset = BitsetType::template generateBitset<Signature>();
if(threadCount <= 1) {
for(std::size_t i = 0; i < currentSize; ++i) {
if(!std::get<bool>(entities[i])) {
continue;
} else if((signatureBitset & std::get<BitsetType>(entities[i])) == signatureBitset) {
fn(i, this, userData);
}
}
} else {
std::vector<std::thread> threads(threadCount);
const std::size_t s = currentSize / threadCount;
for(std::size_t i = 0; i < threadCount; ++i) {
const std::size_t begin = s * i;
const std::size_t end =
i == threadCount - 1 ?
currentSize :
s * (i + 1);
threads[i] = std::thread(
[this] (const std::size_t begin,
const std::size_t end,
const BitsetType signatureBitset,
ForMatchingFn fn,
void *userData) {
for(std::size_t i = begin; i < end; ++i) {
if(!std::get<bool>(entities[i])) {
continue;
} else if((signatureBitset & std::get<BitsetType>(entities[i])) == signatureBitset) {
fn(i, this, userData);
}
}
},
begin,
end,
signatureBitset,
fn,
userData);
}
for(std::size_t i = 0; i < threadCount; ++i) {
threads[i].join();
}
}
}
Some data may persist but will be overwritten when new entities /*!
are added. Thus, do not depend on data to persist after a call to * \brief Similar to forMatchingSimple(), but with a collection of Component/Tag indices
reset(). *
*/ * This function works like forMatchingSimple(), but instead of
void reset() * providing template types that filter out non-matching entities, an
{ * iterable of indices must be provided which correlate to matching
clearForMatchingFunctions(); * Component/Tag indices. The function given must match the previously
* defined typedef of type ForMatchingFn.
*/
template <typename Iterable>
void forMatchingIterable(Iterable iterable, ForMatchingFn fn, void* userPtr = nullptr, std::size_t threadCount = 1) {
if(threadCount <= 1) {
bool isValid;
for(std::size_t i = 0; i < currentSize; ++i) {
if(!std::get<bool>(entities[i])) {
continue;
}
currentSize = 0; isValid = true;
currentCapacity = 0; for(const auto& integralValue : iterable) {
deletedSet.clear(); if(!std::get<BitsetType>(entities[i]).getCombinedBit(integralValue)) {
resize(EC_INIT_ENTITIES_SIZE); isValid = false;
break;
}
}
if(!isValid) { continue; }
fn(i, this, userPtr);
}
} else {
std::vector<std::thread> threads(threadCount);
std::size_t s = currentSize / threadCount;
for(std::size_t i = 0; i < threadCount; ++i) {
std::size_t begin = s * i;
std::size_t end =
i == threadCount - 1 ?
currentSize :
s * (i + 1);
threads[i] = std::thread(
[this, &fn, &iterable, userPtr] (std::size_t begin, std::size_t end) {
bool isValid;
for(std::size_t i = begin; i < end; ++i) {
if(!std::get<bool>(this->entities[i])) {
continue;
}
isValid = true;
for(const auto& integralValue : iterable) {
if(!std::get<BitsetType>(entities[i]).getCombinedBit(integralValue)) {
isValid = false;
break;
}
}
if(!isValid) { continue; }
fn(i, this, userPtr);
}
},
begin, end);
}
for(std::size_t i = 0; i < threadCount; ++i) {
threads[i].join();
}
}
} }
}; };
} }

View file

@ -39,6 +39,8 @@ using EmptyList = EC::Meta::TypeList<>;
using MixedList = EC::Meta::TypeList<C2, T1>; using MixedList = EC::Meta::TypeList<C2, T1>;
using ListCombinedComponentsTags = EC::Meta::Combine<ListComponentsAll, ListTagsAll>;
typedef std::unique_ptr<C0> C0Ptr; typedef std::unique_ptr<C0> C0Ptr;
struct Base struct Base
@ -1074,3 +1076,266 @@ TEST(EC, FunctionStorageOrder)
EXPECT_EQ(5, v.at(4)); EXPECT_EQ(5, v.at(4));
EXPECT_EQ(6, v.at(5)); EXPECT_EQ(6, v.at(5));
} }
TEST(EC, forMatchingSimple) {
EC::Manager<ListComponentsAll, ListTagsAll> manager;
auto e0 = manager.addEntity();
manager.addComponent<C0>(e0, 0, 1);
auto e1 = manager.addEntity();
manager.addComponent<C0>(e1, 2, 3);
manager.addTag<T0>(e1);
auto e2 = manager.addEntity();
manager.addComponent<C0>(e2, 4, 5);
manager.addTag<T0>(e2);
manager.addTag<T1>(e2);
// add 10 to C0 components
manager.forMatchingSimple<EC::Meta::TypeList<C0>>(
[] (std::size_t id, decltype(manager) *manager, void *) {
C0 *c0 = manager->getEntityData<C0>(id);
c0->x += 10;
c0->y += 10;
}, nullptr, 3);
// verify
{
C0 *c0 = manager.getEntityData<C0>(e0);
EXPECT_EQ(c0->x, 10);
EXPECT_EQ(c0->y, 11);
c0 = manager.getEntityData<C0>(e1);
EXPECT_EQ(c0->x, 12);
EXPECT_EQ(c0->y, 13);
c0 = manager.getEntityData<C0>(e2);
EXPECT_EQ(c0->x, 14);
EXPECT_EQ(c0->y, 15);
}
auto e3 = manager.addEntity();
manager.addComponent<C0>(e3, 6, 7);
manager.addTag<T0>(e3);
manager.addTag<T1>(e3);
// add 100 to entities with C0,T1
manager.forMatchingSimple<EC::Meta::TypeList<C0, T1>>(
[] (std::size_t id, decltype(manager) *manager, void *) {
C0 *c0 = manager->getEntityData<C0>(id);
c0->x += 100;
c0->y += 100;
});
// verify
{
C0 *c0 = manager.getEntityData<C0>(e0);
EXPECT_EQ(c0->x, 10);
EXPECT_EQ(c0->y, 11);
c0 = manager.getEntityData<C0>(e1);
EXPECT_EQ(c0->x, 12);
EXPECT_EQ(c0->y, 13);
c0 = manager.getEntityData<C0>(e2);
EXPECT_EQ(c0->x, 114);
EXPECT_EQ(c0->y, 115);
c0 = manager.getEntityData<C0>(e3);
EXPECT_EQ(c0->x, 106);
EXPECT_EQ(c0->y, 107);
}
}
TEST(EC, forMatchingIterableFn)
{
EC::Manager<ListComponentsAll, ListTagsAll> manager;
auto e0 = manager.addEntity();
manager.addComponent<C0>(e0, 0, 1);
auto e1 = manager.addEntity();
manager.addComponent<C0>(e1, 2, 3);
manager.addTag<T0>(e1);
auto e2 = manager.addEntity();
manager.addComponent<C0>(e2, 4, 5);
manager.addTag<T0>(e2);
manager.addTag<T1>(e2);
auto c0Index = EC::Meta::IndexOf<C0, ListCombinedComponentsTags>::value;
auto c1Index = EC::Meta::IndexOf<C1, ListCombinedComponentsTags>::value;
auto t0Index = EC::Meta::IndexOf<T0, ListCombinedComponentsTags>::value;
auto t1Index = EC::Meta::IndexOf<T1, ListCombinedComponentsTags>::value;
{
// test valid indices
auto iterable = {c0Index};
auto fn = [] (std::size_t i, decltype(manager)* m, void*) {
auto* c = m->getEntityComponent<C0>(i);
c->x += 1;
c->y += 1;
};
manager.forMatchingIterable(iterable, fn, nullptr);
}
{
auto* c = manager.getEntityComponent<C0>(e0);
EXPECT_EQ(c->x, 1);
EXPECT_EQ(c->y, 2);
c = manager.getEntityComponent<C0>(e1);
EXPECT_EQ(c->x, 3);
EXPECT_EQ(c->y, 4);
c = manager.getEntityComponent<C0>(e2);
EXPECT_EQ(c->x, 5);
EXPECT_EQ(c->y, 6);
}
{
// test invalid indices
auto iterable = {c0Index, c1Index};
auto fn = [] (std::size_t i, decltype(manager)* m, void*) {
auto* c = m->getEntityComponent<C0>(i);
c->x += 1;
c->y += 1;
};
manager.forMatchingIterable(iterable, fn, nullptr);
}
{
auto* c = manager.getEntityComponent<C0>(e0);
EXPECT_EQ(c->x, 1);
EXPECT_EQ(c->y, 2);
c = manager.getEntityComponent<C0>(e1);
EXPECT_EQ(c->x, 3);
EXPECT_EQ(c->y, 4);
c = manager.getEntityComponent<C0>(e2);
EXPECT_EQ(c->x, 5);
EXPECT_EQ(c->y, 6);
}
{
// test partially valid indices
auto iterable = {c0Index, t1Index};
auto fn = [] (std::size_t i, decltype(manager)* m, void*) {
auto* c = m->getEntityComponent<C0>(i);
c->x += 1;
c->y += 1;
};
manager.forMatchingIterable(iterable, fn, nullptr);
}
{
auto* c = manager.getEntityComponent<C0>(e0);
EXPECT_EQ(c->x, 1);
EXPECT_EQ(c->y, 2);
c = manager.getEntityComponent<C0>(e1);
EXPECT_EQ(c->x, 3);
EXPECT_EQ(c->y, 4);
c = manager.getEntityComponent<C0>(e2);
EXPECT_EQ(c->x, 6);
EXPECT_EQ(c->y, 7);
}
{
// test partially valid indices
auto iterable = {c0Index, t0Index};
auto fn = [] (std::size_t i, decltype(manager)* m, void*) {
auto* c = m->getEntityComponent<C0>(i);
c->x += 10;
c->y += 10;
};
manager.forMatchingIterable(iterable, fn, nullptr);
}
{
auto* c = manager.getEntityComponent<C0>(e0);
EXPECT_EQ(c->x, 1);
EXPECT_EQ(c->y, 2);
c = manager.getEntityComponent<C0>(e1);
EXPECT_EQ(c->x, 13);
EXPECT_EQ(c->y, 14);
c = manager.getEntityComponent<C0>(e2);
EXPECT_EQ(c->x, 16);
EXPECT_EQ(c->y, 17);
}
{
// test invalid indices
auto iterable = {(unsigned int)c0Index, 1000u};
auto fn = [] (std::size_t i, decltype(manager)* m, void*) {
auto* c = m->getEntityComponent<C0>(i);
c->x += 1000;
c->y += 1000;
};
manager.forMatchingIterable(iterable, fn, nullptr);
}
{
auto* c = manager.getEntityComponent<C0>(e0);
EXPECT_EQ(c->x, 1);
EXPECT_EQ(c->y, 2);
c = manager.getEntityComponent<C0>(e1);
EXPECT_EQ(c->x, 13);
EXPECT_EQ(c->y, 14);
c = manager.getEntityComponent<C0>(e2);
EXPECT_EQ(c->x, 16);
EXPECT_EQ(c->y, 17);
}
{
// test concurrent update
auto iterable = {c0Index};
auto fn = [] (std::size_t i, decltype(manager)* m, void*) {
auto *c = m->getEntityComponent<C0>(i);
c->x += 100;
c->y += 100;
};
manager.forMatchingIterable(iterable, fn, nullptr, 3);
}
{
auto* c = manager.getEntityComponent<C0>(e0);
EXPECT_EQ(c->x, 101);
EXPECT_EQ(c->y, 102);
c = manager.getEntityComponent<C0>(e1);
EXPECT_EQ(c->x, 113);
EXPECT_EQ(c->y, 114);
c = manager.getEntityComponent<C0>(e2);
EXPECT_EQ(c->x, 116);
EXPECT_EQ(c->y, 117);
}
{
// test invalid concurrent update
auto iterable = {(unsigned int)c0Index, 1000u};
auto fn = [] (std::size_t i, decltype(manager)* m, void*) {
auto *c = m->getEntityComponent<C0>(i);
c->x += 1000;
c->y += 1000;
};
manager.forMatchingIterable(iterable, fn, nullptr, 3);
}
{
auto* c = manager.getEntityComponent<C0>(e0);
EXPECT_EQ(c->x, 101);
EXPECT_EQ(c->y, 102);
c = manager.getEntityComponent<C0>(e1);
EXPECT_EQ(c->x, 113);
EXPECT_EQ(c->y, 114);
c = manager.getEntityComponent<C0>(e2);
EXPECT_EQ(c->x, 116);
EXPECT_EQ(c->y, 117);
}
}