Skip to content

Commit

Permalink
Merge pull request #2 from bot-mne-v-rot/ecs
Browse files Browse the repository at this point in the history
Partial implementation of the ECS
  • Loading branch information
SmnTin authored Jul 29, 2021
2 parents db75dea + e3f4b7f commit 5856f2b
Show file tree
Hide file tree
Showing 10 changed files with 317 additions and 22 deletions.
3 changes: 2 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ set(LIB_DIR "${CMAKE_CURRENT_SOURCE_DIR}/lib")
set(UNIVERSAL_LIB_DIR "${CMAKE_CURRENT_SOURCE_DIR}/lib")

# Target
set(TEST_SOURCES test/test.cpp test/ecs/test_world.cpp test/ecs/test_system.cpp test/ecs/test_dispatcher.cpp test/ecs/storages/test_vec_storage.cpp test/ecs/test_id_set.cpp test/ecs/test_join.cpp)
set(SOURCES src/shader.cpp)
set(TEST_SOURCES test/test.cpp test/ecs/test_world.cpp test/ecs/test_system.cpp test/ecs/test_dispatcher.cpp test/ecs/storages/test_vec_storage.cpp test/ecs/test_id_set.cpp test/ecs/test_join.cpp test/ecs/storages/test_inverted_storage.cpp)
set(TEST_TARGET tests)

include_directories(include)
Expand Down
33 changes: 20 additions & 13 deletions include/ecs/detail/id_set_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,7 @@ namespace ecs {
while (new_cp < n) new_cp <<= 1;
cp = new_cp;

assert(new_cp <= (1 << (shift * levels_num)));
// pow(bits_num, levels_num) = pow(2, log2(bits_num) * levels_num);
assert(new_cp <= max_size);

for (std::size_t i = 0; i + 1 < levels_num; ++i) {
auto &level = levels[i];
Expand Down Expand Up @@ -302,18 +301,18 @@ namespace ecs {

template<IdSetLike S>
inline uint64_t IdSetNot<S>::level_data(std::size_t lvl, std::size_t ind) const {
if (lvl) return ~0ull;
return set.level_data(lvl, ind);
if (lvl) return ~0ull; // upper levels are ignored
return ind < set.level_capacity(0) ? ~set.level_data(0, ind) : ~0ull;
}

template<IdSetLike S>
inline std::size_t IdSetNot<S>::level_capacity(std::size_t lvl) const {
return set.level_capacity(lvl);
inline std::size_t IdSetNot<S>::level_capacity(std::size_t) const {
return IdSet::max_size;
}

template<IdSetLike S>
inline std::size_t IdSetNot<S>::capacity() const {
return set.capacity();
return IdSet::max_size;
}

template<IdSetLike S>
Expand Down Expand Up @@ -358,11 +357,15 @@ namespace ecs {
}

inline std::size_t FullIdSet::level_capacity([[maybe_unused]] std::size_t lvl) const {
return std::numeric_limits<std::size_t>::max();
return IdSet::max_size;
}

inline std::size_t FullIdSet::capacity() const {
return std::numeric_limits<std::size_t>::max();
return IdSet::max_size;
}

inline std::size_t FullIdSet::size() const {
return IdSet::max_size;
}

inline Id FullIdSet::first() const {
Expand All @@ -382,11 +385,11 @@ namespace ecs {
}

inline auto FullIdSet::cbegin() const -> const_iterator {
return const_iterator(this, first());
return {this, first()};
}

inline auto FullIdSet::cend() const -> const_iterator {
return const_iterator(this, capacity());
return {this, static_cast<Id>(capacity())};
}

////////////////// EmptyIdSet
Expand All @@ -408,6 +411,10 @@ namespace ecs {
return 0;
}

inline std::size_t EmptyIdSet::size() const {
return 0;
}

inline Id EmptyIdSet::first() const {
return 0;
}
Expand All @@ -425,11 +432,11 @@ namespace ecs {
}

inline auto EmptyIdSet::cbegin() const -> const_iterator {
return const_iterator(this, first());
return {this, first()};
}

inline auto EmptyIdSet::cend() const -> const_iterator {
return const_iterator(this, capacity());
return {this, static_cast<Id>(capacity())};
}

////////////////// IdSetIterator
Expand Down
15 changes: 14 additions & 1 deletion include/ecs/id_set.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ namespace ecs {

~IdSet();


const_iterator begin() const;
const_iterator end() const;
const_iterator cbegin() const;
Expand All @@ -76,6 +75,9 @@ namespace ecs {
static const std::size_t shift = 6; // = log2(bits_num);
constexpr static std::size_t shifts[levels_num] = {0, shift, shift * 2, shift * 3};

static const std::size_t max_size = (1 << (shift * levels_num));
// pow(bits_num, levels_num) = pow(2, log2(bits_num) * levels_num);

private:
uint64_t level3 = 0; // level3 is preallocated and hardcoded to never grow
uint64_t *levels[levels_num]{nullptr, nullptr, nullptr, &level3};
Expand Down Expand Up @@ -258,6 +260,10 @@ namespace ecs {
* Virtual Id Set that represents Id Set
* complement (bitwise NOT of the bitmask).
*
* As soon as any IdSet is finite (except FullIdSet)
* it's complement is infinite. The implementation
* follows the idea of infinite capacity.
*
* @note It's effectively bitwise NOT of the bottom level
* while others are constant 1s.
*/
Expand Down Expand Up @@ -307,6 +313,7 @@ namespace ecs {
* (all Ids are present).
*
* @note It just returns 1s.
* @note It has infinite capacity.
*/
class FullIdSet {
public:
Expand All @@ -321,6 +328,7 @@ namespace ecs {
Id first() const;

std::size_t capacity() const;
std::size_t size() const;

uint64_t level_data(std::size_t lvl, std::size_t ind) const;
std::size_t level_capacity(std::size_t lvl) const;
Expand All @@ -337,6 +345,8 @@ namespace ecs {

ecs_define_id_set_iterator(FullIdSet);

static_assert(IdSetLike<FullIdSet>);

/**
* Virtual Id Set that represents empty Id Set
* (no Id is present).
Expand All @@ -356,6 +366,7 @@ namespace ecs {
Id first() const;

std::size_t capacity() const;
std::size_t size() const;

uint64_t level_data(std::size_t lvl, std::size_t ind) const;
std::size_t level_capacity(std::size_t lvl) const;
Expand All @@ -371,6 +382,8 @@ namespace ecs {
};

ecs_define_id_set_iterator(EmptyIdSet);

static_assert(IdSetLike<EmptyIdSet>);
}

// Implementation should be visible to make inlining possible.
Expand Down
19 changes: 14 additions & 5 deletions include/ecs/storage.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,26 @@ namespace ecs {
{ a.cend() } -> std::same_as<typename S::const_iterator>;
{ a.size() } -> std::same_as<typename S::size_type>;
{ a.empty() } -> std::same_as<bool>;
} && requires(S a, const S b, Id id,
const typename S::value_type &c_lval_ref, typename S::value_type &&rval_ref) {
} && requires(S a, const S b, Id id) {
//// Other requirements:
{ a[id] } -> std::same_as<typename S::value_type &>;
{ b[id] } -> std::same_as<const typename S::value_type &>;
{ b.contains(id) } -> std::same_as<bool>;
{ b.present() } -> detail::ConstLvalueRefToIdSetLike;
};

/**
* Storage that provides unified interface for
* inserting and erasing components.
*/
template<class S>
concept MutStorage = requires(S a, Id id,
const typename S::value_type &c_lval_ref,
typename S::value_type &&rval_ref) {
{ a.insert(id, c_lval_ref) } -> std::same_as<void>;
{ a.insert(id, rval_ref) } -> std::same_as<void>;
{ a.erase(id) } -> std::same_as<void>;
{ b.contains(id) } -> std::same_as<bool>;
{ b.present() } -> detail::ConstLvalueRefToIdSetLike;
};;
} && Storage<S>;
}

#endif //HIGH_SHIFT_STORAGE_H
98 changes: 98 additions & 0 deletions include/ecs/storages/detail/inverted_storage_impl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
namespace ecs {
template<Storage S>
std::size_t InvertedStorage<S>::size() const {
return mask.capacity() - storage.size();
}

template<Storage S>
std::size_t InvertedStorage<S>::capacity() const {
return mask.capacity();
}

template<Storage S>
bool InvertedStorage<S>::empty() const {
return size() == 0;
}

template<Storage S>
bool InvertedStorage<S>::contains(Id id) const {
return !storage.contains(id);
}

template<Storage S>
auto InvertedStorage<S>::operator[](Id) -> reference {
return dummy;
}

template<Storage S>
auto InvertedStorage<S>::operator[](Id) const -> const_reference {
return dummy;
}

template<Storage S>
auto InvertedStorage<S>::present() const -> const IdSetType & {
return mask;
}

template<Storage S>
template<typename Ref, typename Ptr>
InvertedStorage<S>::iterator_template<Ref, Ptr>::iterator_template(MaskIterator mask_iterator)
: mask_iterator(mask_iterator) {}

template<Storage S>
template<typename Ref, typename Ptr>
auto InvertedStorage<S>::iterator_template<Ref, Ptr>::operator*() const -> reference {
return dummy;
}

template<Storage S>
template<typename Ref, typename Ptr>
auto InvertedStorage<S>::iterator_template<Ref, Ptr>::operator->() const -> pointer {
return &dummy;
}

template<Storage S>
template<typename Ref, typename Ptr>
auto InvertedStorage<S>::iterator_template<Ref, Ptr>::operator++() -> iterator_template & {
++mask_iterator;
return *this;
}

template<Storage S>
template<typename Ref, typename Ptr>
auto InvertedStorage<S>::iterator_template<Ref, Ptr>::operator++(int) -> iterator_template {
auto copy = *this;
++(*this);
return copy;
}

template<Storage S>
auto InvertedStorage<S>::begin() -> iterator {
return { mask.begin() };
}

template<Storage S>
auto InvertedStorage<S>::end() -> iterator {
return { mask.end() };
}

template<Storage S>
auto InvertedStorage<S>::cbegin() const -> const_iterator {
return { mask.cbegin() };
}

template<Storage S>
auto InvertedStorage<S>::cend() const -> const_iterator {
return { mask.cend() };
}

template<Storage S>
auto InvertedStorage<S>::begin() const -> const_iterator {
return cbegin();
}

template<Storage S>
auto InvertedStorage<S>::end() const -> const_iterator {
return cend();
}
}
91 changes: 91 additions & 0 deletions include/ecs/storages/inverted_storage.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#ifndef HIGH_SHIFT_INVERTED_STORAGE_H
#define HIGH_SHIFT_INVERTED_STORAGE_H

#include "ecs/storage.h"
#include "ecs/id_set.h"

namespace ecs {
struct EmptyComponent {};

template<Storage S>
class InvertedStorage {
public:
using IdSetType = IdSetNot<std::remove_cvref_t<decltype(std::declval<S>().present())>>;

using value_type = EmptyComponent;
using reference = value_type &;
using const_reference = const value_type &;
using pointer = value_type *;
using const_pointer = const value_type *;
using difference_type = std::ptrdiff_t;
using size_type = std::size_t;

explicit InvertedStorage(const S &storage) : storage(storage), mask(~storage.present()) {}

std::size_t size() const;
std::size_t capacity() const;
bool empty() const;

bool contains(Id id) const;

reference operator[](Id index);
const_reference operator[](Id index) const;

const IdSetType &present() const;

template<typename Ref, typename Ptr>
class iterator_template {
public:
using reference = Ref;
using pointer = Ptr;
using value_type = EmptyComponent;
using difference_type = std::ptrdiff_t;
using iterator_category = std::forward_iterator_tag;

iterator_template() = default;
iterator_template(const iterator_template &) = default;
iterator_template &operator=(const iterator_template &) = default;

reference operator*() const;
pointer operator->() const;

iterator_template &operator++();
iterator_template operator++(int);

bool operator==(const iterator_template &other) const = default;
bool operator!=(const iterator_template &other) const = default;

private:
using MaskIterator = typename IdSetType::const_iterator;
MaskIterator mask_iterator;
EmptyComponent dummy;

friend InvertedStorage;
explicit iterator_template(MaskIterator mask_iterator);
};

using iterator = iterator_template<reference, pointer>;
using const_iterator = iterator_template<const_reference, const_pointer>;

iterator begin();
iterator end();
const_iterator begin() const;
const_iterator end() const;
const_iterator cbegin() const;
const_iterator cend() const;

private:
IdSetType mask;
const S &storage;
EmptyComponent dummy;
};

template<Storage S>
auto operator!(const S &storage) {
return InvertedStorage<S>(storage);
}
}

#include "ecs/storages/detail/inverted_storage_impl.h"

#endif //HIGH_SHIFT_INVERTED_STORAGE_H
Loading

0 comments on commit 5856f2b

Please sign in to comment.