Skip to content

Commit

Permalink
Merge pull request #109 from AGH-Code-Industry/chris_ecs
Browse files Browse the repository at this point in the history
Views cache ComponentPools, bug fixes, and explicit template instantiation for faster compilation
  • Loading branch information
Chris-plusplus authored Oct 21, 2024
2 parents 01617d3 + 38b7feb commit b9d8db2
Show file tree
Hide file tree
Showing 16 changed files with 827 additions and 153 deletions.
1 change: 1 addition & 0 deletions include/ecs/CommonComponentPool.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class CommonComponentPool: public _details::SparseSet<E>, public utils::Readonly

// range with all valid entities in _dense
auto _entitiesForView() const noexcept;
static auto _emptyEntitiesForView() noexcept;
};

} // namespace arch::ecs::_details
Expand Down
6 changes: 6 additions & 0 deletions include/ecs/CommonComponentPool.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,10 @@ auto CommonComponentPool<E>::_entitiesForView() const noexcept {
return std::views::filter(_dense, _details::EntityTraits<E>::Version::hasNotNull);
}

template<class E>
auto CommonComponentPool<E>::_emptyEntitiesForView() noexcept {
static const std::vector<E> empty;
return std::views::filter(empty, _details::EntityTraits<E>::Version::hasNotNull);
}

} // namespace arch::ecs::_details
10 changes: 5 additions & 5 deletions include/ecs/ComponentTraits.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,12 @@ struct ComponentTraits {
/// @brief Type of component
using ComponentT = C;

/// @brief Whether component is a flag component,
static inline constexpr bool flag = Specs::flag;
static_assert(!(flag && !std::is_empty_v<ComponentT>), "Non-empty type cannot be marked as flag-component");

/// @brief Whether component is marked as in-place
static inline constexpr bool inPlace = Specs::inPlace;
static inline constexpr bool inPlace = Specs::inPlace && !flag;
/// @brief Whether component is movable
static inline constexpr bool movable = std::movable<ComponentT>;
static_assert(!(!movable && !inPlace), "Non-movable components cannot be marked as not-in-place");
Expand All @@ -36,10 +40,6 @@ struct ComponentTraits {
/// @brief Alignment of component
static inline constexpr size_t alignment = alignof(ComponentT);

/// @brief Whether component is a flag component,
static inline constexpr bool flag = Specs::flag;
static_assert(!(flag && !std::is_empty_v<ComponentT>), "Non-empty type cannot be marked as flag-component");

/// @brief Creates new page of size pageSize
static inline ComponentT* newPage() noexcept;
/// @brief Destroys all components and deallocates memory of given page
Expand Down
12 changes: 12 additions & 0 deletions include/ecs/Domain.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ class Domain {
using ConstGetReference = std::
conditional_t<_details::ComponentTraits<std::remove_const_t<C>, E>::flag, bool, const std::remove_const_t<C>&>;

static inline constexpr EntityT null = Traits::Entity::null;

/// @brief Default constructor
Domain() noexcept = default;
/// @brief Destructor
Expand Down Expand Up @@ -106,6 +108,13 @@ class Domain {
/// @return Reference to new entity or old one
template<class C, class... Args>
GetReference<std::remove_const_t<C>> addComponent(const EntityT entity, Args&&... args) noexcept;
/// @brief Adds component to entity or returns existing one
/// @tparam C - component type
/// @param entity - entity to add component to
/// @param ...args - arguments to constructor
/// @return Reference to new entity or old one
template<class C>
GetReference<std::remove_const_t<C>> addComponent(const EntityT entity, C&& component) noexcept;
/// @brief Obtains reference to existing component of given entity
/// @details If entity does not contain component, behavior is undefined
/// @tparam C - component type
Expand Down Expand Up @@ -222,6 +231,9 @@ class Domain {
std::unordered_map<TypeDescriptorWrapper, void (*)(CPoolsT&)> _cpoolDestroyers;
};

extern template class Domain<e32>;
extern template class Domain<e64>;

} // namespace arch::ecs

#include "Domain.hpp"
Expand Down
36 changes: 13 additions & 23 deletions include/ecs/Domain.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ void DOMAIN_E::swap(DOMAIN_E& other) noexcept {

TEMPLATE_E
bool DOMAIN_E::alive(const EntityT entity) const noexcept {
return _entityPool.alive(entity);
return _entityPool.contains(entity);
}

TEMPLATE_E
Expand Down Expand Up @@ -149,6 +149,12 @@ DOMAIN_E::GetReference<std::remove_const_t<C>> DOMAIN_E::addComponent(const Enti
return _assureCPool<std::remove_const_t<C>>().addComponent(entity, std::forward<Args>(args)...);
}

TEMPLATE_E
template<class C>
DOMAIN_E::GetReference<std::remove_const_t<C>> DOMAIN_E::addComponent(const EntityT entity, C&& component) noexcept {
return _assureCPool<std::remove_const_t<C>>().addComponent(entity, std::forward<C>(component));
}

TEMPLATE_E
template<class C>
requires(!std::is_const_v<C>)
Expand Down Expand Up @@ -223,24 +229,16 @@ TEMPLATE_E
template<class... Includes, class... Excludes>
auto DOMAIN_E::view(ExcludeT<Excludes...>) noexcept {
if constexpr (sizeof...(Includes) != 0) {
ARCH_ASSERT(
/*ARCH_ASSERT(
std::ranges::none_of(
std::initializer_list<const _details::CommonComponentPool<E>*>{
dynamic_cast<const _details::CommonComponentPool<E>*>(_tryGetCPool<std::remove_const_t<Includes>>()
)... },
[](const auto ptr) { return ptr == nullptr; }
),
"One of requested ComponentPools does not exist"
);
return View<E, false, TypeList<Includes...>, TypeList<Excludes...>>(
this,
// the less entities, the easier filtering
*std::min(
{ dynamic_cast<const _details::CommonComponentPool<E>*>(&_getCPool<std::remove_const_t<Includes>>()
)... },
[](const auto lhs, const auto rks) { return lhs->count() < rks->count(); }
)
);
);*/
return View<E, false, TypeList<Includes...>, TypeList<Excludes...>>(this);
} else {
return View<E, false, TypeList<>, TypeList<Excludes...>>(this);
}
Expand All @@ -250,24 +248,16 @@ TEMPLATE_E
template<class... Includes, class... Excludes>
auto DOMAIN_E::view(ExcludeT<Excludes...>) const noexcept {
if constexpr (sizeof...(Includes) != 0) {
ARCH_ASSERT(
/*ARCH_ASSERT(
std::ranges::none_of(
std::initializer_list<const _details::CommonComponentPool<E>*>{
dynamic_cast<const _details::CommonComponentPool<E>*>(_tryGetCPool<std::remove_const_t<Includes>>()
)... },
[](const auto ptr) { return ptr == nullptr; }
),
"One of requested ComponentPools does not exist"
);
return View<E, true, TypeList<Includes...>, TypeList<Excludes...>>(
this,
// the less entities, the easier filtering
*std::min(
{ dynamic_cast<const _details::CommonComponentPool<E>*>(&_getCPool<std::remove_const_t<Includes>>()
)... },
[](const auto lhs, const auto rks) { return lhs->count() < rks->count(); }
)
);
);*/
return View<E, true, TypeList<Includes...>, TypeList<Excludes...>>(this);
} else {
return View<E, true, TypeList<>, TypeList<Excludes...>>(this);
}
Expand Down
3 changes: 3 additions & 0 deletions include/ecs/EntityPool.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,9 @@ class EntityPool: public _details::SparseSet<E> {
size_t _size = 0;
};

extern template class EntityPool<e32>;
extern template class EntityPool<e64>;

} // namespace arch::ecs

#include "EntityPool.hpp"
14 changes: 8 additions & 6 deletions include/ecs/EntityPool.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,11 @@ POOL_E::EntityT POOL_E::newEntity() noexcept {
TEMPLATE_E
POOL_E::EntityT POOL_E::recycleEntity(const EntityT entity) noexcept {
if (!contains(Traits::Id::part(entity)) /*&& _size <= Traits::Id::max*/) {
auto& wantedSparse = _sparseGet(entity);
auto& wantedSparse = _sparseGet(Traits::Id::part(entity));
auto& toSwapDense = _dense[_size++];

std::swap(_dense[Traits::Id::part(wantedSparse)], toSwapDense);
Traits::Id::swap(wantedSparse, _sparseGet(toSwapDense));
// std::swap(_dense[Traits::Id::part(wantedSparse)], toSwapDense);
Traits::Id::swap(wantedSparse, _sparseGet(Traits::Id::part(toSwapDense)));

toSwapDense = Traits::Entity::fromOthers(toSwapDense, entity);
wantedSparse = Traits::Entity::fromOthers(wantedSparse, toSwapDense);
Expand All @@ -121,7 +121,7 @@ POOL_E::EntityT POOL_E::recycleId(const IdT id) noexcept {
auto& toSwapDense = _dense[_size++];

std::swap(_dense[Traits::Id::part(wantedSparse)], toSwapDense);
Traits::Id::swap(wantedSparse, _sparseGet(toSwapDense));
Traits::Id::swap(wantedSparse, _sparseGet(Traits::Id::part(toSwapDense)));

wantedSparse = Traits::Entity::fromOthers(wantedSparse, toSwapDense);

Expand All @@ -136,8 +136,9 @@ void POOL_E::kill(const EntityT entity) noexcept {
auto& wantedSparse = _sparseGet(Traits::Id::part(entity));
auto& toSwapDense = _dense[--_size];

const auto temp = toSwapDense;
std::swap(_dense[Traits::Id::part(wantedSparse)], toSwapDense);
Traits::Id::swap(wantedSparse, _sparseGet(Traits::Id::part(toSwapDense)));
Traits::Id::swap(wantedSparse, _sparseGet(Traits::Id::part(temp)));

toSwapDense = Traits::Version::withNext(toSwapDense);
wantedSparse = Traits::Version::withNull(wantedSparse);
Expand All @@ -150,8 +151,9 @@ void POOL_E::kill(const IdT id) noexcept {
auto& wantedSparse = _sparseGet(id);
auto& toSwapDense = _dense[--_size];

const auto temp = toSwapDense;
std::swap(_dense[Traits::Id::part(wantedSparse)], toSwapDense);
Traits::Id::swap(wantedSparse, _sparseGet(toSwapDense));
Traits::Id::swap(wantedSparse, _sparseGet(Traits::Id::part(temp)));

toSwapDense = Traits::Version::withNext(toSwapDense);
wantedSparse = Traits::Version::withNull(wantedSparse);
Expand Down
5 changes: 5 additions & 0 deletions include/ecs/EntityTraits.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once

#include <bit>
#include <limits>

#include "EntitySpecs.h"
Expand Down Expand Up @@ -133,6 +134,10 @@ struct EntityTraits {
};
};

extern template class EntityTraits<e32>;
extern template class EntityTraits<e64>;

} // namespace arch::ecs::_details

#include "EntityTraits.hpp"

3 changes: 3 additions & 0 deletions include/ecs/SparseSet.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ class SparseSet {
DenseContainer _dense;
};

extern template class SparseSet<e32>;
extern template class SparseSet<e64>;

} // namespace arch::ecs::_details

#include "SparseSet.hpp"
52 changes: 39 additions & 13 deletions include/ecs/View.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
#include <type_traits>

#include "ComponentPool.h"
// #include "ContainChecks.h"
#include "ExcludeT.h"

namespace arch::ecs {
Expand Down Expand Up @@ -35,15 +34,15 @@ auto getAsTuple(const Domain<E>& domain, const E entity) noexcept;
/// @param entity - entity to get components from
/// @param <unnamed> - TypeList instance to help in deduction
template<class E, class... Includes>
auto getByTS(Domain<E>& domain, const E entity, TypeList<Includes...>) noexcept;
auto getByTL(Domain<E>& domain, const E entity, TypeList<Includes...>) noexcept;
/// @brief Returns std::tuple with specified components from given entity
/// @tparam E - entity type
/// @tparam Includes - component types
/// @param domain - domain to get components from
/// @param entity - entity to get components from
/// @param <unnamed> - TypeList instance to help in deduction
template<class E, class... Includes>
auto getByTS(const Domain<E>& domain, const E entity, TypeList<Includes...>) noexcept;
auto getByTL(const Domain<E>& domain, const E entity, TypeList<Includes...>) noexcept;

/// @brief Predicate wrapper for TypeList to filter out flag-components
/// @tparam E - entity type
Expand Down Expand Up @@ -116,12 +115,12 @@ class View<E, Const, TypeList<Includes...>, TypeList<Excludes...>> {
/// @brief Returns std::tuple with readonly included components, including flag-components
/// @param entity - entity to get components from
auto getAll(const EntityT entity) const noexcept;
/// @brief Returns std::tuple with given components (may be out of view)
/// @brief Returns std::tuple with given components
/// @tparam Cs - components to get
/// @param entity - entity to get components from
template<class... Cs>
auto get(const EntityT entity) noexcept requires(!Const);
/// @brief Returns std::tuple with given readonly components (may be out of view)
/// @brief Returns std::tuple with given readonly components
/// @tparam Cs - components to get
/// @param entity - entity to get components from
template<class... Cs>
Expand Down Expand Up @@ -192,18 +191,36 @@ class View<E, Const, TypeList<Includes...>, TypeList<Excludes...>> {

friend Domain<E>;

// filtering function
static bool _filterFn(const Domain<E>& domain, const E entity) noexcept;
using CCPoolPtr = const _details::CommonComponentPool<E>*;
//, _details::CommonComponentPool<E>* > ;
template<class C>
using CPoolPtr = std::conditional_t<
Const || std::is_const_v<C>,
const ComponentPool<std::remove_const_t<C>, E>*,
ComponentPool<std::remove_const_t<C>, E>*>;

size_t _minInclude() const noexcept;

template<class C>
auto getAsTuple(const E entity) noexcept requires(!Const);
template<class C>
auto getAsTuple(const E entity) const noexcept;
template<class... Cs>
auto getByTL(const E entity, TypeList<Cs...>) noexcept requires(!Const);
template<class... Cs>
auto getByTL(const E entity, TypeList<Cs...>) const noexcept;

// expected type of entities view
using EntitesViewT = decltype(std::views::filter(
std::declval<const _details::CommonComponentPool<E>>()._entitiesForView(),
std::bind(_filterFn, std::cref(*((const Domain<E>*)nullptr)), std::placeholders::_1)
std::bind(&View::contains, std::declval<const View*>(), std::placeholders::_1)
));

View(DomainT* domain, const _details::CommonComponentPool<E>& minCPool) noexcept;
View(DomainT* domain) noexcept;

DomainT* _domain;
std::array<CCPoolPtr, includeCount> _includedCPools;
std::array<CCPoolPtr, excludeCount> _excludedCPools;
size_t _minIdx;
EntitesViewT _entities;
};

Expand Down Expand Up @@ -283,18 +300,27 @@ class View<E, Const, TypeList<>, TypeList<Excludes...>> {
friend Domain<E>;

// filtering function
static bool _filterFn(const Domain<E>& domain, const E entity) noexcept;
bool _containsNoCheck(const E entity) const noexcept;

using CCPoolPtr =
std::conditional_t<Const, const _details::CommonComponentPool<E>*, _details::CommonComponentPool<E>*>;
template<class C>
using CPoolPtr = std::conditional_t<
Const || std::is_const_v<C>,
const ComponentPool<std::remove_const_t<C>, E>*,
ComponentPool<std::remove_const_t<C>, E>*>;

// expected type of entities view
using EntitesViewT = decltype(std::views::filter(
std::declval<const Domain<E>>().entities(),
std::bind(_filterFn, std::cref(*((const Domain<E>*)nullptr)), std::placeholders::_1)
std::bind(&View::_containsNoCheck, std::declval<const View*>(), std::placeholders::_1)
));

View(DomainT* domain) noexcept;

DomainT* _domain;
std::array<CCPoolPtr, excludeCount> _excludedCPools;
EntitesViewT _entities;
DomainT* _domain;
};

} // namespace arch::ecs
Expand Down
Loading

0 comments on commit b9d8db2

Please sign in to comment.