Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Views cache ComponentPools, bug fixes, and explicit template instantiation for faster compilation #109

Merged
merged 9 commits into from
Oct 21, 2024
823 changes: 816 additions & 7 deletions archimedes_bin/main.cpp
Chris-plusplus marked this conversation as resolved.
Show resolved Hide resolved

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ def requirements(self):
self.requires("gtest/1.13.0")
self.requires("stb/cci.20230920", override=True)
self.requires("draco/1.5.6", override=True)
self.requires("entt/3.13.2")
Chris-plusplus marked this conversation as resolved.
Show resolved Hide resolved

# Vulkan SDK
self.requires("volk/1.3.268.0")
Expand Down
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
Loading