From 9c4bbd77f86e7b83cfcc0787d947ed361a7b234b Mon Sep 17 00:00:00 2001 From: Chris-plusplus <72704303+Chris-plusplus@users.noreply.github.com> Date: Tue, 8 Oct 2024 12:41:05 +0200 Subject: [PATCH 1/9] Changed View to cache ComponentPools instead of obtaining them each time --- archimedes_bin/main.cpp | 823 +++++++++++++++++++++++++++++++++++++++- include/ecs/Domain.hpp | 20 +- include/ecs/View.h | 51 ++- include/ecs/View.hpp | 256 +++++++------ 4 files changed, 1005 insertions(+), 145 deletions(-) diff --git a/archimedes_bin/main.cpp b/archimedes_bin/main.cpp index 507d013..00d45e4 100644 --- a/archimedes_bin/main.cpp +++ b/archimedes_bin/main.cpp @@ -1,17 +1,826 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include #include #include +#include +#include +#include + +#pragma region iter_tests + +struct iter1 { + static inline constexpr size_t pageSize = 1'024; + + inline iter1(std::vector>* _vec, size_t _offset) noexcept: vec{ _vec }, offset{ _offset } {} + + inline iter1(std::vector>& x) noexcept: iter1(&x, 0) {} + + inline static iter1 begin(std::vector>& x) noexcept { return iter1(x); } + + inline static iter1 end(std::vector>& x) noexcept { return iter1(&x, 1'024 * x.size()); } + + inline size_t& operator*() noexcept { return (*vec)[offset >> 10][offset & ((size_t)1'024 - (size_t)1)]; } + + inline iter1& operator++() noexcept { + ++offset; + return *this; + } + + std::vector>* vec; + size_t offset; +}; + +struct iter2 { + static inline constexpr size_t pageSize = 1'024; + + inline iter2(std::vector* _vec, size_t _offset) noexcept: vec{ _vec }, offset{ _offset } {} + + inline iter2(std::vector>& x) noexcept: iter2(&x.front(), 0) {} + + inline static iter2 begin(std::vector>& x) noexcept { return iter2(x); } + + inline static iter2 end(std::vector>& x) noexcept { return iter2(&x.back() + 1, 0); } + + inline size_t& operator*() noexcept { return (*vec)[offset]; } + + inline iter2& operator++() noexcept { + /*++offset; + if (offset & pageSize) { + offset = 0; + }*/ + //++offset; + if ((++offset) == pageSize) { + offset = 0; + } + if (!offset) { + ++vec; + } + return *this; + } + + std::vector* vec; + size_t offset; +}; + +struct iter3 { + static inline constexpr size_t pageSize = 1'024; + + inline iter3(std::vector* _vec, size_t _offset) noexcept: vec{ _vec }, offset{ _offset } {} + + inline iter3(std::vector>& x) noexcept: iter3(&x.front(), 0) {} + + inline static iter3 begin(std::vector>& x) noexcept { return iter3(x); } + + inline static iter3 end(std::vector>& x) noexcept { return iter3(&x.back() + 1, 0); } + + inline size_t& operator*() noexcept { return (*vec)[offset]; } + + inline iter3& operator++() noexcept { + offset = (offset + 1) & ((size_t)1'023); + if (!offset) { + ++vec; + } + return *this; + } + + std::vector* vec; + size_t offset; +}; + +inline bool operator==(const iter1& one, const iter1& other) noexcept { + return one.vec == other.vec && one.offset == other.offset; +} + +inline bool operator==(const iter2& one, const iter2& other) noexcept { + return one.vec == other.vec && one.offset == other.offset; +} + +inline bool operator==(const iter3& one, const iter3& other) noexcept { + return one.vec == other.vec && one.offset == other.offset; +} + +struct XD { + // static inline constexpr bool in_place_delete = true; + int x; + + XD() = default; + XD(int _x): x{ _x } {}; + + ~XD() { + // + } + + XD(const XD& xd) { + x = xd.x; + std::cout << std::format("copy: {} -> {}\n", (size_t)&xd, (size_t)this); + } + + XD(XD&& xd) { + x = xd.x; + xd.x = 0; + std::cout << std::format("move: {} -> {}\n", (size_t)&xd, (size_t)this); + } + + XD& operator=(const XD& other) { + x = other.x; + std::cout << std::format("copy: {} -> {}\n", (size_t)&other, (size_t)this); + return *this; + } -struct MyApp: arch::Application { - void init() override { arch::Logger::info("Initializing user app!"); } + XD& operator=(XD&& other) { + x = other.x; + other.x = 0; + std::cout << std::format("move: {} -> {}\n", (size_t)&other, (size_t)this); + return *this; + } +}; + +struct XD2 { + int x; + + XD2() = default; + XD2(int _x): x{ _x } {}; + XD2(const XD2&) = default; + XD2(XD2&&) = default; + + XD2& operator=(const XD2& other) { + x = other.x; + return *this; + } + + XD2& operator=(XD2&& other) { + x = other.x; + other.x = 0; + return *this; + } +}; + +// using traits = entt::sparse_set::traits_type; + +#pragma endregion + +namespace arch::ecs { + +enum e16 : u16; +enum e8 : u8; + +template<> +struct EntitySpecs: AutoEntitySpecs {}; + +template<> +struct EntitySpecs: AutoEntitySpecs {}; + +} // namespace arch::ecs + +struct NonMoveable { + static inline constexpr bool in_place_delete = true; + + int val = 0; + + NonMoveable(){}; + NonMoveable(int v): val{ v } {}; + NonMoveable(const NonMoveable&) = default; + NonMoveable(NonMoveable&&) = default; + + NonMoveable& operator=(int v) { + val = v; + return *this; + } + + NonMoveable& operator=(const NonMoveable&) = default; + NonMoveable& operator=(NonMoveable&&) = default; +}; + +struct Empty {}; + +struct NoUniqueAddress { + int x; + float d; + size_t mogus; + int sus; + [[no_unique_address]] Empty empty; + int xd3; +}; + +using clk = std::chrono::high_resolution_clock; + +namespace ecs = arch::ecs; + +struct Flag { + // static inline constexpr bool inPlaceComponent = true; + static inline constexpr bool flagComponent = true; +}; + +struct Flag2 { + static inline constexpr bool inPlaceComponent = true; + // static inline constexpr bool flagComponent = true; +}; + +#include + +struct Pos { + double x, y; +}; + +struct Vel { + double x, y; }; int main() { - arch::Logger::init(arch::LogLevel::trace); + /*auto registry = entt::registry(); + + auto entities = std::vector(); + for (size_t i = 0; i != 8; ++i) { + entities.emplace_back(registry.create()); + registry.emplace(entities.back(), 0.0, 0.0); + if (i % 2) { + registry.emplace(entities.back(), 0.0, 0.0); + } + } + + auto group = registry.group(entt::get);*/ + + arch::Logger::init(arch::LogLevel::info); + + ecs::Domain domain; + + auto e0 = domain.newEntity(); + auto e1 = domain.newEntity(); + auto e2 = domain.newEntity(); + auto e3 = domain.newEntity(); + auto e4 = domain.newEntity(); + auto e5 = domain.newEntity(); + auto e6 = domain.newEntity(); + auto e7 = domain.newEntity(); + auto e8 = domain.newEntity(); + auto e9 = domain.newEntity(); + auto e10 = domain.newEntity(); + + domain.addComponent(e0); + domain.addComponent(e1); + domain.addComponent(e2); + domain.addComponent(e3); + domain.addComponent(e4); + domain.addComponent(e5); + domain.addComponent(e6); + domain.addComponent(e2); + domain.addComponent(e3); + domain.addComponent(e6); + + auto view = domain.view(); + + view.forEach([&view](auto entity) { + auto&& [flag, flag2] = view.getAll(entity); + std::cout << std::format("{}\n", (uint32_t)entity); + }); + + domain.kill(e6); + + view.refresh().forEach([&view](ecs::e32 entity) { + auto&& [flag, flag2] = view.getAll(entity); + std::cout << std::format("{}\n", (uint32_t)entity); + }); + +#pragma region OldTests + + /*arch::ecs::EntityPool pool; + using Traits = decltype(pool)::Traits; + + auto&& [sparse, dense] = pool.debug(); + + for (auto i = 0; i != 10; ++i) { + auto e = pool.newEntity(); + } + + auto e2 = Traits::Entity::fromParts(2, 0); + + pool.kill(e2); + + for (auto&& e : pool) { + std::cout << std::format("{}|{}\n", Traits::Id::part(e), Traits::Version::part(e)); + } + + std::cout << '\n'; + + for (auto&& e : *dense) { + std::cout << std::format("{}|{}\n", Traits::Id::part(e), Traits::Version::part(e)); + } + + std::cout << '\n'; + + std::cout << std::format("{}\n", pool.alive(Traits::Entity::fromParts(2, 1))); + std::cout << std::format("{}\n", pool.alive(Traits::Entity::fromParts(2, 0))); + std::cout << std::format("{}\n", pool.contains(2)); + + std::cout << '\n'; + + e2 = pool.recycleEntity(Traits::Entity::fromParts(2, 254)); + + for (auto&& e : pool) { + std::cout << std::format("{}|{}\n", Traits::Id::part(e), Traits::Version::part(e)); + } + + std::cout << '\n'; + + std::cout << std::format("{}\n", pool.alive(e2)); + std::cout << std::format("{}\n", pool.alive(Traits::Entity::fromParts(2, 0))); + std::cout << std::format("{}\n", pool.contains(2));*/ + + /*entt::registry reg; + + auto e1 = reg.create(); + auto e2 = reg.create(); + auto e3 = reg.create(); + auto e4 = reg.create(); + + reg.emplace(e1, 420); + reg.emplace(e2, 1'337); + reg.emplace(e3, 360); + reg.emplace(e4, 69); + + reg.view().each([](const auto e, auto& c) { std::cout << c.val << '\n'; }); + + reg.sort([](const NonMoveable& lhs, const NonMoveable& rks) { return lhs.val < rks.val; }); + reg.view().each([](const auto e, auto& c) { std::cout << c.val << '\n'; });*/ + + /*entt::entity x = traits::construct(0, std::numeric_limits::max() - 1); + + entt::sparse_set set{ entt::deletion_policy::swap_only }; + + set.push(x); + + for (auto&& e : set) { + std::cout << std::format("{}|{}\n", traits::to_entity(e), traits::to_version(e)); + } + + set.erase(x); + + std::cout << set.contains(x) << '\n'; + + for (auto&& e : set) { + std::cout << std::format("{}|{}\n", traits::to_entity(e), traits::to_version(e)); + }*/ + + /*entt::sparse_set set{ entt::deletion_policy::swap_only }; + + set.push(traits::construct(0, 0));*/ + + /*entt::registry reg; + + auto e0 = reg.create(); + auto e1 = reg.create(); + auto e2 = reg.create(); + auto e3 = reg.create(); + auto e4 = reg.create(); + + reg.emplace(e0, 0); + reg.emplace(e1, 1); + reg.emplace(e2, 2); + reg.emplace(e3, 3); + reg.emplace(e4, 4); + + { + auto view = reg.view(); + + view.each([](const auto entity, auto&& xd) { + std::cout << std::format( + "{}|{} : {}->{}\n", + traits::to_entity(entity), + traits::to_version(entity), + (size_t)&xd, + xd.x + ); + }); + } + std::cout << '\n'; + + reg.erase(e2); + + { + auto view = reg.view(); + + view.each([](const auto entity, auto&& xd) { + std::cout << std::format( + "{}|{} : {}->{}\n", + traits::to_entity(entity), + traits::to_version(entity), + (size_t)&xd, + xd.x + ); + }); + } + + reg.emplace(e2, 2); + std::cout << std::format("\n"); + + { + auto view = reg.view(); + + view.each([](const auto entity, auto&& xd) { + std::cout << std::format( + "{}|{} : {}->{}\n", + traits::to_entity(entity), + traits::to_version(entity), + (size_t)&xd, + xd.x + ); + }); + }*/ + + /*entt::registry reg; + + auto entt1 = reg.create(); + auto entt2 = reg.create(); + auto entt3 = reg.create(); + auto entt4 = reg.create(); + reg.emplace(entt1, 1); + reg.emplace(entt2, 2); + reg.emplace(entt3, 3); + reg.emplace(entt4, 4); + + auto view = reg.view(); + view.each([](auto& xd) { std::cout << std::format("({})->x = {}\n", (void*)&xd, xd.x); }); + + reg.erase(entt3); + + view = reg.view(); + view.each([](auto& xd) { std::cout << std::format("({})->x = {}\n", (void*)&xd, xd.x); });*/ + + /*namespace chr = std::chrono; + namespace rg = std::ranges; + namespace vw = std::views; + using clk = chr::high_resolution_clock; + + constexpr size_t pageSize = 10; + + std::array, pageSize> map; + for (auto&& row : map) { + for (auto&& val : row) { + val = '#'; + } + } + map[0][0] = 'X'; + std::cout << "\033[2J\033[1;1H"; + for (auto&& row : map) { + for (auto&& val : row) { + std::cout << val; + } + std::cout << '\n'; + } + + size_t x = 0; + size_t y = 0; + for (;;) { + long long dx; + std::cin >> dx; + + map[y][x] = '#'; + + x += dx; + if (dx < 0) { + if (x > pageSize) { + x = (size_t)0 - x; + y -= 1 + (x / pageSize); + x = pageSize - (x) % pageSize; + } + } else { + y += x / pageSize; + x %= pageSize; + } + std::cout << y << ' ' << x << '\n'; + getchar(); + getchar(); + + map[y][x] = 'X'; + + std::cout << "\033[2J\033[1;1H"; + for (auto&& row : map) { + for (auto&& val : row) { + std::cout << val; + } + std::cout << '\n'; + } + } + + return 0;*/ + + /*entt::basic_storage storage; + + for (size_t i = 0; i != 1'000'000; ++i) { + storage.emplace((entt::entity)i, (int)i); + }*/ + + /*{ + auto start = clk::now(); + for (auto&& o : storage) { + ++o.x; + } + auto end = clk::now(); + std::cout << std::format("{}\n", end - start); + }*/ + /*{ + using alloc_traits = std::allocator_traits; + using traits_type = entt::component_traits; + auto start = clk::now(); + auto _begin = storage.rbegin(); + auto containter = *(std::vector< + typename alloc_traits::pointer, + typename alloc_traits::template rebind_alloc>**)&_begin; + for (auto&& o : *containter) { + auto end = o + traits_type::page_size; + for (auto i = o; i != end; ++i) { + ++(i->x); + } + } + auto end = clk::now(); + std::cout << std::format("{}\n", end - start); + }*/ + + // size_t iters = 5 * 10'000; + + //{ + // std::vector> nums; + // for (size_t i = 0; i != 1'024; ++i) { + // nums.emplace_back(); + // for (size_t j = 0; j != 1'024; ++j) { + // nums.back().emplace_back(i * 1'024 + j); + // } + // // std::cout << i << ": " << nums.back().size() << '\n'; + // } + // decltype(clk::now() - clk::now()) avg = {}; + // for (size_t n = 0; n != iters; ++n) { + // if (n % (iters / 10) == 0) { + // std::cout << std::format("n = {}\n", n); + // } + + // // std::cout << nums.size() << '\n'; + + // { + // auto start = clk::now(); + // for (auto i = iter1::begin(nums), end = iter1::end(nums); i != end; ++i) { + // ++(*i); + // } + // auto end = clk::now(); + // avg += end - start; + // } + // } + // avg /= iters; + // std::cout << std::format("custom iter1: {}\n", avg); + //} + + /// ITERS TEST + /// ITERS TEST + /// ITERS TEST + /// ITERS TEST + /// ITERS TEST + + // size_t iters = 5 * 10'000; + + //{ + // std::vector> nums; + // for (size_t i = 0; i != 1'024; ++i) { + // nums.emplace_back(); + // for (size_t j = 0; j != 1'024; ++j) { + // nums.back().emplace_back(i * 1'024 + j); + // } + // // std::cout << i << ": " << nums.back().size() << '\n'; + // } + // decltype(clk::now() - clk::now()) avg = {}; + // for (size_t n = 0; n != iters; ++n) { + // if (n % (iters / 10) == 0) { + // std::cout << std::format("n = {}\n", n); + // } + // // std::cout << nums.size() << '\n'; + + // { + // auto start = clk::now(); + // for (auto i = iter2::begin(nums), end = iter2::end(nums); i != end; ++i) { + // ++(*i); + // } + // auto end = clk::now(); + // avg += end - start; + // } + // } + // avg /= iters; + // std::cout << std::format("custom iter2: {}\n", avg); + //} + + //{ + // std::vector> nums; + // for (size_t i = 0; i != 1'024; ++i) { + // nums.emplace_back(); + // for (size_t j = 0; j != 1'024; ++j) { + // nums.back().emplace_back(i * 1'024 + j); + // } + // // std::cout << i << ": " << nums.back().size() << '\n'; + // } + // decltype(clk::now() - clk::now()) avg = {}; + // for (size_t n = 0; n != iters; ++n) { + // if (n % (iters / 10) == 0) { + // std::cout << std::format("n = {}\n", n); + // } + // // std::cout << nums.size() << '\n'; + + // { + // auto start = clk::now(); + // for (auto i = iter3::begin(nums), end = iter3::end(nums); i != end; ++i) { + // ++(*i); + // } + // auto end = clk::now(); + // avg += end - start; + // } + // } + // avg /= iters; + // std::cout << std::format("custom iter3: {}\n", avg); + //} + + //{ + // std::vector> nums; + // for (size_t i = 0; i != 1'024; ++i) { + // nums.emplace_back(); + // for (size_t j = 0; j != 1'024; ++j) { + // nums.back().emplace_back(i * 1'024 + j); + // } + // // std::cout << i << ": " << nums.back().size() << '\n'; + // } + // decltype(clk::now() - clk::now()) avg = {}; + // for (size_t n = 0; n != iters; ++n) { + // if (n % (iters / 10) == 0) { + // std::cout << std::format("n = {}\n", n); + // } + // // std::cout << nums.size() << '\n'; + + // { + // auto start = clk::now(); + // for (size_t i = 0; i != nums.size(); ++i) { + // for (size_t j = 0; j != nums[i].size(); ++j) { + // ++nums[i][j]; + // } + // } + // auto end = clk::now(); + // avg += end - start; + // } + // } + // avg /= iters; + // std::cout << std::format("custom iter3: {}\n", avg); + //} + + //{ + // std::vector> nums; + // for (size_t i = 0; i != 1'024; ++i) { + // nums.emplace_back(); + // for (size_t j = 0; j != 1'024; ++j) { + // nums.back().emplace_back(i * 1'024 + j); + // } + // // std::cout << i << ": " << nums.back().size() << '\n'; + // } + // decltype(clk::now() - clk::now()) avg = {}; + // for (size_t n = 0; n != iters; ++n) { + // if (n % (iters / 10) == 0) { + // std::cout << std::format("n = {}\n", n); + // } + // // std::cout << nums.size() << '\n'; + + // { + // auto start = clk::now(); + // for (auto&& v : nums) { + // for (auto&& o : v) { + // ++o; + // } + // } + // auto end = clk::now(); + // avg += end - start; + // } + // } + // avg /= iters; + // std::cout << std::format("custom iter4: {}\n", avg); + //} + + //{ + // std::vector> nums; + // for (size_t i = 0; i != 1'024; ++i) { + // nums.emplace_back(); + // for (size_t j = 0; j != 1'024; ++j) { + // nums.back().emplace_back(i * 1'024 + j); + // } + // // std::cout << i << ": " << nums.back().size() << '\n'; + // } + // decltype(clk::now() - clk::now()) avg = {}; + // for (size_t n = 0; n != iters; ++n) { + // if (n % (iters / 10) == 0) { + // std::cout << std::format("n = {}\n", n); + // } + // // std::cout << nums.size() << '\n'; + + // { + // auto start = clk::now(); + // auto&& view = nums | vw::join; + // for (auto&& o : view) { + // ++o; + // } + // auto end = clk::now(); + // avg += end - start; + // } + // } + // avg /= iters; + // std::cout << std::format("custom iter5: {}\n", avg); + //} + + //{ + // std::vector> nums; + // for (size_t i = 0; i != 1'024; ++i) { + // nums.emplace_back(); + // for (size_t j = 0; j != 1'024; ++j) { + // nums.back().emplace_back(i * 1'024 + j); + // } + // // std::cout << i << ": " << nums.back().size() << '\n'; + // } + // auto&& view = nums | vw::join; + // decltype(clk::now() - clk::now()) avg = {}; + // for (size_t n = 0; n != iters; ++n) { + // if (n % (iters / 10) == 0) { + // std::cout << std::format("n = {}\n", n); + // } + // // std::cout << nums.size() << '\n'; + + // { + // auto start = clk::now(); + // for (auto&& o : view) { + // ++o; + // } + // auto end = clk::now(); + // avg += end - start; + // } + // } + // avg /= iters; + // std::cout << std::format("custom iter6: {}\n", avg); + //} + + // avg = {}; + // for (size_t n = 0; n != 1'000; ++n) { + // if (n % 100 == 0) { + // std::cout << std::format("n = {}\n", n); + // } + // std::vector> nums; + // for (size_t i = 0; i != 1'024; ++i) { + // nums.emplace_back(); + // for (size_t j = 0; j != 1'024; ++j) { + // nums.back().emplace_back(i * 1'024 + j); + // } + // // std::cout << i << ": " << nums.back().size() << '\n'; + // } + // // std::cout << nums.size() << '\n'; + + // { + // auto start = clk::now(); + // for (size_t i = 0; i != nums.size(); ++i) { + // for (size_t j = 0; j != nums[i].size(); ++j) { + // ++nums[i][j]; + // } + // } + // auto end = clk::now(); + // avg += end - start; + // } + //} + // avg /= 1'000; + // std::cout << std::format("loop: {}\n", avg); - arch::EngineConfig config{ 600, 480, "Archimedes Test", glm::vec4(0, 0, 0, 0) }; + // avg = {}; + // for (size_t n = 0; n != 1'000; ++n) { + // if (n % 100 == 0) { + // std::cout << std::format("n = {}\n", n); + // } + // std::vector> nums; + // for (size_t i = 0; i != 1'024; ++i) { + // nums.emplace_back(); + // for (size_t j = 0; j != 1'024; ++j) { + // nums.back().emplace_back(i * 1'024 + j); + // } + // // std::cout << i << ": " << nums.back().size() << '\n'; + // } + // // std::cout << nums.size() << '\n'; - arch::Ref myApp = arch::createRef(); + // { + // auto start = clk::now(); + // for (auto&& v : nums) { + // for (auto&& o : v) { + // ++o; + // } + // } + // auto end = clk::now(); + // avg += end - start; + // } + //} + // avg /= 1'000; + // std::cout << std::format("iterator: {}\n", avg); - arch::Engine engine{ config, myApp }; - engine.start(); +#pragma endregion } diff --git a/include/ecs/Domain.hpp b/include/ecs/Domain.hpp index 30e3a1f..445f4ca 100644 --- a/include/ecs/Domain.hpp +++ b/include/ecs/Domain.hpp @@ -232,15 +232,7 @@ auto DOMAIN_E::view(ExcludeT) noexcept { ), "One of requested ComponentPools does not exist" ); - return View, TypeList>( - this, - // the less entities, the easier filtering - *std::min( - { dynamic_cast*>(&_getCPool>() - )... }, - [](const auto lhs, const auto rks) { return lhs->count() < rks->count(); } - ) - ); + return View, TypeList>(this); } else { return View, TypeList>(this); } @@ -259,15 +251,7 @@ auto DOMAIN_E::view(ExcludeT) const noexcept { ), "One of requested ComponentPools does not exist" ); - return View, TypeList>( - this, - // the less entities, the easier filtering - *std::min( - { dynamic_cast*>(&_getCPool>() - )... }, - [](const auto lhs, const auto rks) { return lhs->count() < rks->count(); } - ) - ); + return View, TypeList>(this); } else { return View, TypeList>(this); } diff --git a/include/ecs/View.h b/include/ecs/View.h index 864e724..a0e4d66 100644 --- a/include/ecs/View.h +++ b/include/ecs/View.h @@ -35,7 +35,7 @@ auto getAsTuple(const Domain& domain, const E entity) noexcept; /// @param entity - entity to get components from /// @param - TypeList instance to help in deduction template -auto getByTS(Domain& domain, const E entity, TypeList) noexcept; +auto getByTL(Domain& domain, const E entity, TypeList) noexcept; /// @brief Returns std::tuple with specified components from given entity /// @tparam E - entity type /// @tparam Includes - component types @@ -43,7 +43,7 @@ auto getByTS(Domain& domain, const E entity, TypeList) noexcept; /// @param entity - entity to get components from /// @param - TypeList instance to help in deduction template -auto getByTS(const Domain& domain, const E entity, TypeList) noexcept; +auto getByTL(const Domain& domain, const E entity, TypeList) noexcept; /// @brief Predicate wrapper for TypeList to filter out flag-components /// @tparam E - entity type @@ -116,12 +116,12 @@ class View, TypeList> { /// @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 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 @@ -192,18 +192,36 @@ class View, TypeList> { friend Domain; - // filtering function - static bool _filterFn(const Domain& domain, const E entity) noexcept; + using CCPoolPtr = + std::conditional_t*, _details::CommonComponentPool*>; + template + using CPoolPtr = std::conditional_t< + Const, + const ComponentPool, E>*, + ComponentPool, E>*>; + + size_t _minInclude() const noexcept; + + template + auto getAsTuple(const E entity) noexcept requires(!Const); + template + auto getAsTuple(const E entity) const noexcept; + template + auto getByTL(const E entity, TypeList) noexcept requires(!Const); + template + auto getByTL(const E entity, TypeList) const noexcept; // expected type of entities view using EntitesViewT = decltype(std::views::filter( std::declval>()._entitiesForView(), - std::bind(_filterFn, std::cref(*((const Domain*)nullptr)), std::placeholders::_1) + std::bind(&View::contains, std::declval(), std::placeholders::_1) )); - View(DomainT* domain, const _details::CommonComponentPool& minCPool) noexcept; + View(DomainT* domain) noexcept; - DomainT* _domain; + std::array _includedCPools; + std::array _excludedCPools; + size_t _minIdx; EntitesViewT _entities; }; @@ -283,18 +301,27 @@ class View, TypeList> { friend Domain; // filtering function - static bool _filterFn(const Domain& domain, const E entity) noexcept; + bool _containsNoCheck(const E entity) const noexcept; + + using CCPoolPtr = + std::conditional_t*, _details::CommonComponentPool*>; + template + using CPoolPtr = std::conditional_t< + Const, + const ComponentPool, E>*, + ComponentPool, E>*>; // expected type of entities view using EntitesViewT = decltype(std::views::filter( std::declval>().entities(), - std::bind(_filterFn, std::cref(*((const Domain*)nullptr)), std::placeholders::_1) + std::bind(&View::_containsNoCheck, std::declval(), std::placeholders::_1) )); View(DomainT* domain) noexcept; - DomainT* _domain; + std::array _excludedCPools; EntitesViewT _entities; + DomainT* _domain; }; } // namespace arch::ecs diff --git a/include/ecs/View.hpp b/include/ecs/View.hpp index 63ad6e3..bc6574a 100644 --- a/include/ecs/View.hpp +++ b/include/ecs/View.hpp @@ -33,75 +33,142 @@ auto getAsTuple(const Domain& domain, const E entity) noexcept { } template -auto getByTS(Domain& domain, const E entity, TypeList) noexcept { +auto getByTL(Domain& domain, const E entity, TypeList) noexcept { return std::tuple_cat(getAsTuple(domain, entity)...); } template -auto getByTS(const Domain& domain, const E entity, TypeList) noexcept { +auto getByTL(const Domain& domain, const E entity, TypeList) noexcept { return std::tuple_cat(getAsTuple(domain, entity)...); } } // namespace _details TEMPLATE_ECIE -VIEW_ECIE::View(DomainT* domain, const _details::CommonComponentPool& minCPool) noexcept: - _domain{ domain }, - // can't just call refresh(), _entities is not default_initializable - _entities(minCPool._entitiesForView(), std::bind(_filterFn, std::cref(*domain), std::placeholders::_1)) {} +size_t VIEW_ECIE::_minInclude() const noexcept { + ARCH_ASSERT( + std::ranges::none_of(_includedCPools, [](const auto ptr) { return ptr == nullptr; }), + "At least one ComponentPool does not exist" + ); + + if constexpr (includeCount != 1) { + auto dist = std::ranges::distance( + _includedCPools.begin(), + std::ranges::min_element( + _includedCPools, + [](const auto lhs, const auto rks) { return lhs->count() < rks->count(); } + ) + ); + return dist; + } else { + return 0; + } +} TEMPLATE_ECIE -bool VIEW_ECIE::_filterFn(const Domain& domain, const E entity) noexcept { - return (domain.template hasComponent>(entity) && ...) && - !(domain.template hasComponent>(entity) || ...); +template +auto VIEW_ECIE::getAsTuple(const E entity) noexcept requires(!Const) +{ + constexpr auto idx = Include::template find; + + if constexpr (_details::ComponentTraits::flag) { + return std::make_tuple(dynamic_cast>(_includedCPools[idx])->get(entity)); + } else { + return std::tie(dynamic_cast>(_includedCPools[idx])->get(entity)); + } } -TEMPLATE_ECE -VIEW_ECE::View(DomainT* domain) noexcept: - _domain{ domain }, - // can't just call refresh(), _entities is not default_initializable - _entities(domain->entities(), std::bind(_filterFn, std::cref(*domain), std::placeholders::_1)) {} +TEMPLATE_ECIE +template +auto VIEW_ECIE::getAsTuple(const E entity) const noexcept { + constexpr auto idx = Include::template find; -TEMPLATE_ECE -bool VIEW_ECE::_filterFn(const Domain& domain, const E entity) noexcept { - return !(domain.template hasComponent>(entity) || ...); + if constexpr (_details::ComponentTraits::flag) { + return std::make_tuple(dynamic_cast>(_includedCPools[idx])->get(entity)); + } else { + return std::tie(dynamic_cast>(_includedCPools[idx])->get(entity)); + } } TEMPLATE_ECIE -bool VIEW_ECIE::contains(const EntityT entity) const noexcept { - return (_domain->template hasComponent>(entity) && ...) && - !(_domain->template hasComponent>(entity) || ...); +template +auto VIEW_ECIE::getByTL(const E entity, TypeList) noexcept requires(!Const) +{ + return std::tuple_cat(getAsTuple(entity)...); } -TEMPLATE_ECE -bool VIEW_ECE::contains(const EntityT entity) const noexcept { - return _domain.alive(entity) && !(_domain->template hasComponent>(entity) || ...); +TEMPLATE_ECIE +template +auto VIEW_ECIE::getByTL(const E entity, TypeList) const noexcept { + return std::tuple_cat(getAsTuple(entity)...); } TEMPLATE_ECIE -VIEW_ECIE& VIEW_ECIE::refresh() noexcept { +VIEW_ECIE::View(DomainT* domain) noexcept: + _includedCPools{ dynamic_cast(domain->_tryGetCPool())... }, + _excludedCPools{ dynamic_cast(domain->_tryGetCPool())... }, + _minIdx{ _minInclude() }, + // can't just call refresh(), _entities is not default_initializable + _entities( + _includedCPools[_minIdx]->_entitiesForView(), + std::bind(&View::contains, (const View*)this, std::placeholders::_1) + ) {} + +// TEMPLATE_ECIE bool VIEW_ECIE::_filterFn(const Domain& domain, const E entity) noexcept { +// // PRZEROBIC +// return (domain.template hasComponent>(entity) && ...) && +// !(domain.template hasComponent>(entity) || ...); +// } + +TEMPLATE_ECE +VIEW_ECE::View(DomainT* domain) noexcept: + _excludedCPools{ dynamic_cast(domain->_tryGetCPool())... }, + // can't just call refresh(), _entities is not default_initializable + _entities(domain->entities(), std::bind(&View::_containsNoCheck, this, std::placeholders::_1)), + _domain{ domain } { ARCH_ASSERT( - std::ranges::none_of( - // compilers when { cast(...) }: (x_x) - // compilers when list{ cast(...) }: (*o*) - std::initializer_list*>{ - dynamic_cast*>( - _domain->_tryGetCPool>() - )... }, - [](const auto ptr) { return ptr == nullptr; } - ), - "One of requested ComponentPools does not exist" + std::ranges::none_of(_excludedCPools, [](const auto cpool) { return cpool == nullptr; }), + "At least one ComponentPool does not exist" ); +} + +TEMPLATE_ECE +bool VIEW_ECE::_containsNoCheck(const E entity) const noexcept { + return std::ranges::none_of(_excludedCPools, [entity](const auto cpool) { return cpool->contains(entity); }); + /*return !(domain.template hasComponent>(entity) || ...);*/ +} + +TEMPLATE_ECIE bool VIEW_ECIE::contains(const EntityT entity) const noexcept { + return std::all_of( + _includedCPools.begin(), + _includedCPools.begin() + _minIdx, + [entity](const auto cpool) { return cpool->contains(entity); } + ) && + std::all_of( + _includedCPools.begin() + _minIdx + 1, + _includedCPools.end(), + [entity](const auto cpool) { return cpool->contains(entity); } + ) && + std::ranges::none_of(_excludedCPools, [entity](const auto cpool) { return cpool->contains(entity); }); + /*return (_domain->template hasComponent>(entity) && ...) && + !(_domain->template hasComponent>(entity) || ...);*/ +} + +TEMPLATE_ECE +bool VIEW_ECE::contains(const EntityT entity) const noexcept { + return _domain.alive(entity) && _containsNoCheck(entity); + /*return _domain.alive(entity) && !(_domain->template hasComponent>(entity) || ...);*/ +} + +TEMPLATE_ECIE VIEW_ECIE& VIEW_ECIE::refresh() noexcept { // destroy _entities _entities.~EntitesViewT(); - const auto minCPool = std::min( - { dynamic_cast*>(&_domain->_getCPool>() - )... }, - [](const auto lhs, const auto rks) { return lhs->count() < rks->count(); } - ); + _minIdx = _minInclude(); // create new _entites in place of deleted - new (&_entities) - EntitesViewT(minCPool->_entitiesForView(), std::bind(_filterFn, std::cref(*_domain), std::placeholders::_1)); + new (&_entities) EntitesViewT( + _includedCPools[_minIdx]->_entitiesForView(), + std::bind(&View::contains, (const View*)this, std::placeholders::_1) + ); return *this; } @@ -109,8 +176,7 @@ VIEW_ECIE& VIEW_ECIE::refresh() noexcept { TEMPLATE_ECE VIEW_ECE& VIEW_ECE::refresh() noexcept { _entities.~EntitesViewT(); - new (&_entities) - EntitesViewT(_domain->entities(), std::bind(_filterFn, std::cref(*_domain), std::placeholders::_1)); + new (&_entities) EntitesViewT(_domain->entities(), std::bind(&View::_containsNoCheck, this, std::placeholders::_1)); return *this; } @@ -118,25 +184,33 @@ VIEW_ECE& VIEW_ECE::refresh() noexcept { TEMPLATE_ECIE auto VIEW_ECIE::get(const EntityT entity) noexcept requires(!Const) { - return _details::getByTS(*_domain, entity, NoFlags()); + return getByTL(entity, NoFlags()); } TEMPLATE_ECIE auto VIEW_ECIE::get(const EntityT entity) const noexcept { - return _details::getByTS(*_domain, entity, NoFlags()); + return getByTL(entity, NoFlags()); } TEMPLATE_ECIE template auto VIEW_ECIE::get(const EntityT entity) noexcept requires(!Const) { - return _details::getByTS(*_domain, entity, TypeList()); + static_assert( + TypeList::template containsAll, + "One or more specified components is not contained in this view" + ); + return getByTL(entity, TypeList()); } TEMPLATE_ECIE template auto VIEW_ECIE::get(const EntityT entity) const noexcept { - return _details::getByTS(*_domain, entity, TypeList()); + static_assert( + TypeList::template containsAll, + "One or more specified components is not contained in this view" + ); + return getByTL(entity, TypeList()); } TEMPLATE_ECIE @@ -154,70 +228,52 @@ TEMPLATE_ECE template auto VIEW_ECE::get(const EntityT entity) noexcept requires(!Const) { - return _details::getByTS(*_domain, entity, TypeList()); + return _details::getByTL(*_domain, entity, TypeList()); } TEMPLATE_ECE template auto VIEW_ECE::get(const EntityT entity) const noexcept { - return _details::getByTS(*_domain, entity, TypeList()); + return _details::getByTL(*_domain, entity, TypeList()); } TEMPLATE_ECIE auto VIEW_ECIE::all() noexcept requires(!Const) { - return std::views::transform(_entities, [domain = _domain](const EntityT entity) { - return std::tuple_cat(std::make_tuple(entity), _details::getByTS(*domain, entity, NoFlags())); + return std::views::transform(_entities, [this](const EntityT entity) { + return std::tuple_cat(std::make_tuple(entity), get(entity)); }); } TEMPLATE_ECIE auto VIEW_ECIE::all() const noexcept { - return std::views::transform(_entities, [domain = _domain](const EntityT entity) { - return std::tuple_cat(std::make_tuple(entity), _details::getByTS(*domain, entity, NoFlags())); + return std::views::transform(_entities, [this](const EntityT entity) { + return std::tuple_cat(std::make_tuple(entity), get(entity)); }); } TEMPLATE_ECIE auto VIEW_ECIE::components() noexcept requires(!Const) { - return std::views::transform(_entities, [domain = _domain](const EntityT entity) { - return _details::getByTS(*domain, entity, NoFlags()); - }); + return std::views::transform(_entities, [this](const EntityT entity) { return getAll(entity); }); } TEMPLATE_ECIE auto VIEW_ECIE::components() const noexcept { - return std::views::transform(_entities, [domain = _domain](const EntityT entity) { - return _details::getByTS(*domain, entity, NoFlags()); - }); + return std::views::transform(_entities, [this](const EntityT entity) { return getAll(entity); }); } TEMPLATE_ECIE template auto VIEW_ECIE::components() noexcept requires(!Const) { - static_assert( - TypeList::template containsAll, - "One or more specified components is not contained in this view" - ); - - return std::views::transform(_entities, [domain = _domain](const EntityT entity) { - return _details::getByTS(*domain, entity, TypeList()); - }); + return std::views::transform(_entities, [this](const EntityT entity) { return get(entity); }); } TEMPLATE_ECIE template auto VIEW_ECIE::components() const noexcept { - static_assert( - TypeList::template containsAll, - "One or more specified components is not contained in this view" - ); - - return std::views::transform(_entities, [domain = _domain](const EntityT entity) { - return _details::getByTS(*domain, entity, TypeList()); - }); + return std::views::transform(_entities, [this](const EntityT entity) { return get(entity); }); } TEMPLATE_ECIE @@ -226,12 +282,10 @@ void VIEW_ECIE::forEach(Fn&& fn) noexcept requires(!Const) { // call modes using EntityTuple = decltype(std::tuple()); - using EntityNoFlagsTuple = - decltype(std::tuple_cat(std::tuple(), _details::getByTS(*_domain, EntityT(), NoFlags()))); - using EntityComponentsTuple = - decltype(std::tuple_cat(std::tuple(), _details::getByTS(*_domain, EntityT(), Include()))); - using NoFlagsTuple = decltype(_details::getByTS(*_domain, EntityT(), NoFlags())); - using ComponentsTuple = decltype(_details::getByTS(*_domain, EntityT(), Include())); + using EntityNoFlagsTuple = decltype(std::tuple_cat(std::tuple(), get(EntityT()))); + using EntityComponentsTuple = decltype(std::tuple_cat(std::tuple(), getAll(EntityT()))); + using NoFlagsTuple = decltype(get(EntityT())); + using ComponentsTuple = decltype(getAll(EntityT())); for (const auto entity : _entities) { if constexpr (tUtils::isApplicableV) { @@ -240,23 +294,17 @@ void VIEW_ECIE::forEach(Fn&& fn) noexcept requires(!Const) } else if constexpr (tUtils::isApplicableV) { // [...](entity, comp1&, comp2&, ...){ ... } // non-flags ------ ^ ----- ^ ... - std::apply( - std::forward(fn), - std::tuple_cat(std::tuple(entity), _details::getByTS(*_domain, entity, NoFlags())) - ); + std::apply(std::forward(fn), std::tuple_cat(std::tuple(entity), get(entity))); } else if constexpr (tUtils::isApplicableV) { // [...](entity, comp1&, comp2&, ...){ ... } - std::apply( - std::forward(fn), - std::tuple_cat(std::tuple(entity), _details::getByTS(*_domain, entity, Include())) - ); + std::apply(std::forward(fn), std::tuple_cat(std::tuple(entity), getAll(entity))); } else if constexpr (tUtils::isApplicableV) { // [...]( comp1&, comp2&, ...){ ... } // non-flags ---- ^ ----- ^ ... - std::apply(std::forward(fn), _details::getByTS(*_domain, entity, NoFlags())); + std::apply(std::forward(fn), get(entity)); } else if constexpr (tUtils::isApplicableV) { // [...](comp1&, comp2&, ...){ ... } - std::apply(std::forward(fn), _details::getByTS(*_domain, entity, Include())); + std::apply(std::forward(fn), getAll(entity)); } else { static_assert(false, "Given function is not invocable in any forEach mode"); } @@ -268,12 +316,10 @@ template void VIEW_ECIE::forEach(Fn&& fn) const noexcept { // call modes using EntityTuple = decltype(std::tuple()); - using EntityNoFlagsTuple = - decltype(std::tuple_cat(std::tuple(), _details::getByTS(*_domain, EntityT(), NoFlags()))); - using EntityComponentsTuple = - decltype(std::tuple_cat(std::tuple(), _details::getByTS(*_domain, EntityT(), Include()))); - using NoFlagsTuple = decltype(_details::getByTS(*_domain, EntityT(), NoFlags())); - using ComponentsTuple = decltype(_details::getByTS(*_domain, EntityT(), Include())); + using EntityNoFlagsTuple = decltype(std::tuple_cat(std::tuple(), get(EntityT()))); + using EntityComponentsTuple = decltype(std::tuple_cat(std::tuple(), getAll(EntityT()))); + using NoFlagsTuple = decltype(get(EntityT())); + using ComponentsTuple = decltype(getAll(EntityT())); for (const auto entity : _entities) { if constexpr (tUtils::isApplicableV) { @@ -282,25 +328,19 @@ void VIEW_ECIE::forEach(Fn&& fn) const noexcept { } else if constexpr (tUtils::isApplicableV) { // [...](entity, comp1&, comp2&, ...){ ... } // non-flags ------ ^ ----- ^ ... - std::apply( - std::forward(fn), - std::tuple_cat(std::tuple(entity), _details::getByTS(*_domain, entity, NoFlags())) - ); + std::apply(std::forward(fn), std::tuple_cat(std::tuple(entity), get(entity))); } else if constexpr (tUtils::isApplicableV) { // [...](entity, comp1&, comp2&, ...){ ... } - std::apply( - std::forward(fn), - std::tuple_cat(std::tuple(entity), _details::getByTS(*_domain, entity, Include())) - ); + std::apply(std::forward(fn), std::tuple_cat(std::tuple(entity), getAll(entity))); } else if constexpr (tUtils::isApplicableV) { // [...]( comp1&, comp2&, ...){ ... } // non-flags ---- ^ ----- ^ ... - std::apply(std::forward(fn), _details::getByTS(*_domain, entity, NoFlags())); + std::apply(std::forward(fn), get(entity)); } else if constexpr (tUtils::isApplicableV) { // [...](comp1&, comp2&, ...){ ... } - std::apply(std::forward(fn), _details::getByTS(*_domain, entity, Include())); + std::apply(std::forward(fn), getAll(entity)); } else { - static_assert(false, "Given function is not invocable in any forEach mode, check if argument counts match"); + static_assert(false, "Given function is not invocable in any forEach mode"); } } } From 2fb20ebf43e2c3d45decce7b221cee8a8b3129ec Mon Sep 17 00:00:00 2001 From: Chris-plusplus <72704303+Chris-plusplus@users.noreply.github.com> Date: Tue, 8 Oct 2024 12:41:21 +0200 Subject: [PATCH 2/9] temp fix --- conanfile.py | 1 + 1 file changed, 1 insertion(+) diff --git a/conanfile.py b/conanfile.py index f995d36..c4b4856 100644 --- a/conanfile.py +++ b/conanfile.py @@ -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") # Vulkan SDK self.requires("volk/1.3.268.0") From 334b0f5ffb73de0f09d5124b961f1603b0f5575e Mon Sep 17 00:00:00 2001 From: Chris-plusplus <72704303+Chris-plusplus@users.noreply.github.com> Date: Tue, 8 Oct 2024 13:04:24 +0200 Subject: [PATCH 3/9] Added `template` keywords here and there --- include/ecs/View.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/ecs/View.hpp b/include/ecs/View.hpp index bc6574a..beb00b7 100644 --- a/include/ecs/View.hpp +++ b/include/ecs/View.hpp @@ -105,8 +105,8 @@ auto VIEW_ECIE::getByTL(const E entity, TypeList) const noexcept { TEMPLATE_ECIE VIEW_ECIE::View(DomainT* domain) noexcept: - _includedCPools{ dynamic_cast(domain->_tryGetCPool())... }, - _excludedCPools{ dynamic_cast(domain->_tryGetCPool())... }, + _includedCPools{ dynamic_cast(domain->template _tryGetCPool())... }, + _excludedCPools{ dynamic_cast(domain->template _tryGetCPool())... }, _minIdx{ _minInclude() }, // can't just call refresh(), _entities is not default_initializable _entities( @@ -122,7 +122,7 @@ VIEW_ECIE::View(DomainT* domain) noexcept: TEMPLATE_ECE VIEW_ECE::View(DomainT* domain) noexcept: - _excludedCPools{ dynamic_cast(domain->_tryGetCPool())... }, + _excludedCPools{ dynamic_cast(domain->template _tryGetCPool())... }, // can't just call refresh(), _entities is not default_initializable _entities(domain->entities(), std::bind(&View::_containsNoCheck, this, std::placeholders::_1)), _domain{ domain } { From 5d13b59fe69771ba94d418746085f98f971a5fa1 Mon Sep 17 00:00:00 2001 From: Chris-plusplus <72704303+Chris-plusplus@users.noreply.github.com> Date: Fri, 11 Oct 2024 15:08:46 +0200 Subject: [PATCH 4/9] Bug fixes for ECS + explicit template instantiation --- include/ecs/CommonComponentPool.h | 1 + include/ecs/CommonComponentPool.hpp | 6 ++++ include/ecs/ComponentTraits.h | 10 +++---- include/ecs/Domain.h | 12 ++++++++ include/ecs/Domain.hpp | 16 +++++++---- include/ecs/EntityPool.h | 3 ++ include/ecs/EntityPool.hpp | 14 ++++++---- include/ecs/EntityTraits.h | 5 ++++ include/ecs/SparseSet.h | 3 ++ include/ecs/View.h | 9 +++--- include/ecs/View.hpp | 43 ++++++++++++++--------------- include/utils/ReadonlyCounter.h | 1 + src/ecs/ExplicitInstantiation.cpp | 23 +++++++++++++++ 13 files changed, 103 insertions(+), 43 deletions(-) create mode 100644 src/ecs/ExplicitInstantiation.cpp diff --git a/include/ecs/CommonComponentPool.h b/include/ecs/CommonComponentPool.h index 9874e7c..5501322 100644 --- a/include/ecs/CommonComponentPool.h +++ b/include/ecs/CommonComponentPool.h @@ -41,6 +41,7 @@ class CommonComponentPool: public _details::SparseSet, public utils::Readonly // range with all valid entities in _dense auto _entitiesForView() const noexcept; + static auto _emptyEntitiesForView() noexcept; }; } // namespace arch::ecs::_details diff --git a/include/ecs/CommonComponentPool.hpp b/include/ecs/CommonComponentPool.hpp index b2aa1a1..41d2359 100644 --- a/include/ecs/CommonComponentPool.hpp +++ b/include/ecs/CommonComponentPool.hpp @@ -10,4 +10,10 @@ auto CommonComponentPool::_entitiesForView() const noexcept { return std::views::filter(_dense, _details::EntityTraits::Version::hasNotNull); } +template +auto CommonComponentPool::_emptyEntitiesForView() noexcept { + static const std::vector empty; + return std::views::filter(empty, _details::EntityTraits::Version::hasNotNull); +} + } // namespace arch::ecs::_details diff --git a/include/ecs/ComponentTraits.h b/include/ecs/ComponentTraits.h index d2f6afd..7842c69 100644 --- a/include/ecs/ComponentTraits.h +++ b/include/ecs/ComponentTraits.h @@ -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), "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; static_assert(!(!movable && !inPlace), "Non-movable components cannot be marked as not-in-place"); @@ -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), "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 diff --git a/include/ecs/Domain.h b/include/ecs/Domain.h index abe3545..09e6f33 100644 --- a/include/ecs/Domain.h +++ b/include/ecs/Domain.h @@ -41,6 +41,8 @@ class Domain { using ConstGetReference = std:: conditional_t<_details::ComponentTraits, E>::flag, bool, const std::remove_const_t&>; + static inline constexpr EntityT null = Traits::Entity::null; + /// @brief Default constructor Domain() noexcept = default; /// @brief Destructor @@ -106,6 +108,13 @@ class Domain { /// @return Reference to new entity or old one template GetReference> 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 + GetReference> 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 @@ -222,6 +231,9 @@ class Domain { std::unordered_map _cpoolDestroyers; }; +extern template class Domain; +extern template class Domain; + } // namespace arch::ecs #include "Domain.hpp" diff --git a/include/ecs/Domain.hpp b/include/ecs/Domain.hpp index 445f4ca..c475bd4 100644 --- a/include/ecs/Domain.hpp +++ b/include/ecs/Domain.hpp @@ -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 @@ -149,6 +149,12 @@ DOMAIN_E::GetReference> DOMAIN_E::addComponent(const Enti return _assureCPool>().addComponent(entity, std::forward(args)...); } +TEMPLATE_E +template +DOMAIN_E::GetReference> DOMAIN_E::addComponent(const EntityT entity, C&& component) noexcept { + return _assureCPool>().addComponent(entity, std::forward(component)); +} + TEMPLATE_E template requires(!std::is_const_v) @@ -223,7 +229,7 @@ TEMPLATE_E template auto DOMAIN_E::view(ExcludeT) noexcept { if constexpr (sizeof...(Includes) != 0) { - ARCH_ASSERT( + /*ARCH_ASSERT( std::ranges::none_of( std::initializer_list*>{ dynamic_cast*>(_tryGetCPool>() @@ -231,7 +237,7 @@ auto DOMAIN_E::view(ExcludeT) noexcept { [](const auto ptr) { return ptr == nullptr; } ), "One of requested ComponentPools does not exist" - ); + );*/ return View, TypeList>(this); } else { return View, TypeList>(this); @@ -242,7 +248,7 @@ TEMPLATE_E template auto DOMAIN_E::view(ExcludeT) const noexcept { if constexpr (sizeof...(Includes) != 0) { - ARCH_ASSERT( + /*ARCH_ASSERT( std::ranges::none_of( std::initializer_list*>{ dynamic_cast*>(_tryGetCPool>() @@ -250,7 +256,7 @@ auto DOMAIN_E::view(ExcludeT) const noexcept { [](const auto ptr) { return ptr == nullptr; } ), "One of requested ComponentPools does not exist" - ); + );*/ return View, TypeList>(this); } else { return View, TypeList>(this); diff --git a/include/ecs/EntityPool.h b/include/ecs/EntityPool.h index 8a73c5a..f778b29 100644 --- a/include/ecs/EntityPool.h +++ b/include/ecs/EntityPool.h @@ -141,6 +141,9 @@ class EntityPool: public _details::SparseSet { size_t _size = 0; }; +extern template class EntityPool; +extern template class EntityPool; + } // namespace arch::ecs #include "EntityPool.hpp" diff --git a/include/ecs/EntityPool.hpp b/include/ecs/EntityPool.hpp index ff4e7a3..45d3bf0 100644 --- a/include/ecs/EntityPool.hpp +++ b/include/ecs/EntityPool.hpp @@ -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); @@ -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); @@ -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); @@ -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); diff --git a/include/ecs/EntityTraits.h b/include/ecs/EntityTraits.h index c9baba0..746df43 100644 --- a/include/ecs/EntityTraits.h +++ b/include/ecs/EntityTraits.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include "EntitySpecs.h" @@ -133,6 +134,10 @@ struct EntityTraits { }; }; +extern template class EntityTraits; +extern template class EntityTraits; + } // namespace arch::ecs::_details #include "EntityTraits.hpp" + diff --git a/include/ecs/SparseSet.h b/include/ecs/SparseSet.h index acd01af..a6d684a 100644 --- a/include/ecs/SparseSet.h +++ b/include/ecs/SparseSet.h @@ -66,6 +66,9 @@ class SparseSet { DenseContainer _dense; }; +extern template class SparseSet; +extern template class SparseSet; + } // namespace arch::ecs::_details #include "SparseSet.hpp" diff --git a/include/ecs/View.h b/include/ecs/View.h index a0e4d66..0b9c8b8 100644 --- a/include/ecs/View.h +++ b/include/ecs/View.h @@ -4,7 +4,6 @@ #include #include "ComponentPool.h" -// #include "ContainChecks.h" #include "ExcludeT.h" namespace arch::ecs { @@ -192,11 +191,11 @@ class View, TypeList> { friend Domain; - using CCPoolPtr = - std::conditional_t*, _details::CommonComponentPool*>; + using CCPoolPtr = const _details::CommonComponentPool*; + //, _details::CommonComponentPool* > ; template using CPoolPtr = std::conditional_t< - Const, + Const || std::is_const_v, const ComponentPool, E>*, ComponentPool, E>*>; @@ -307,7 +306,7 @@ class View, TypeList> { std::conditional_t*, _details::CommonComponentPool*>; template using CPoolPtr = std::conditional_t< - Const, + Const || std::is_const_v, const ComponentPool, E>*, ComponentPool, E>*>; diff --git a/include/ecs/View.hpp b/include/ecs/View.hpp index beb00b7..94dd84e 100644 --- a/include/ecs/View.hpp +++ b/include/ecs/View.hpp @@ -46,22 +46,23 @@ auto getByTL(const Domain& domain, const E entity, TypeList) noe TEMPLATE_ECIE size_t VIEW_ECIE::_minInclude() const noexcept { - ARCH_ASSERT( + /*ARCH_ASSERT( std::ranges::none_of(_includedCPools, [](const auto ptr) { return ptr == nullptr; }), "At least one ComponentPool does not exist" - ); + );*/ if constexpr (includeCount != 1) { - auto dist = std::ranges::distance( - _includedCPools.begin(), - std::ranges::min_element( - _includedCPools, - [](const auto lhs, const auto rks) { return lhs->count() < rks->count(); } - ) - ); + const auto min = std::ranges::min_element(_includedCPools, [](const auto lhs, const auto rks) { + return !lhs ? true : (!rks ? true : lhs->count() < rks->count()); + }); + if (!(*min)) { // at least one cpool is empty + return (size_t)-1; + } + + auto dist = std::ranges::distance(_includedCPools.begin(), min); return dist; } else { - return 0; + return _includedCPools[0] ? 0 : (size_t)-1; } } @@ -74,7 +75,14 @@ auto VIEW_ECIE::getAsTuple(const E entity) noexcept requires(!Const) if constexpr (_details::ComponentTraits::flag) { return std::make_tuple(dynamic_cast>(_includedCPools[idx])->get(entity)); } else { - return std::tie(dynamic_cast>(_includedCPools[idx])->get(entity)); + return std::tie( + dynamic_cast>( + const_cast, CCPoolPtr, _details::CommonComponentPool*>>( + _includedCPools[idx] + ) + ) + ->get(entity) + ); } } @@ -110,16 +118,11 @@ VIEW_ECIE::View(DomainT* domain) noexcept: _minIdx{ _minInclude() }, // can't just call refresh(), _entities is not default_initializable _entities( - _includedCPools[_minIdx]->_entitiesForView(), + _minIdx == (size_t)-1 ? _details::CommonComponentPool::_emptyEntitiesForView() : + _includedCPools[_minIdx]->_entitiesForView(), std::bind(&View::contains, (const View*)this, std::placeholders::_1) ) {} -// TEMPLATE_ECIE bool VIEW_ECIE::_filterFn(const Domain& domain, const E entity) noexcept { -// // PRZEROBIC -// return (domain.template hasComponent>(entity) && ...) && -// !(domain.template hasComponent>(entity) || ...); -// } - TEMPLATE_ECE VIEW_ECE::View(DomainT* domain) noexcept: _excludedCPools{ dynamic_cast(domain->template _tryGetCPool())... }, @@ -135,7 +138,6 @@ VIEW_ECE::View(DomainT* domain) noexcept: TEMPLATE_ECE bool VIEW_ECE::_containsNoCheck(const E entity) const noexcept { return std::ranges::none_of(_excludedCPools, [entity](const auto cpool) { return cpool->contains(entity); }); - /*return !(domain.template hasComponent>(entity) || ...);*/ } TEMPLATE_ECIE bool VIEW_ECIE::contains(const EntityT entity) const noexcept { @@ -150,14 +152,11 @@ TEMPLATE_ECIE bool VIEW_ECIE::contains(const EntityT entity) const noexcept { [entity](const auto cpool) { return cpool->contains(entity); } ) && std::ranges::none_of(_excludedCPools, [entity](const auto cpool) { return cpool->contains(entity); }); - /*return (_domain->template hasComponent>(entity) && ...) && - !(_domain->template hasComponent>(entity) || ...);*/ } TEMPLATE_ECE bool VIEW_ECE::contains(const EntityT entity) const noexcept { return _domain.alive(entity) && _containsNoCheck(entity); - /*return _domain.alive(entity) && !(_domain->template hasComponent>(entity) || ...);*/ } TEMPLATE_ECIE VIEW_ECIE& VIEW_ECIE::refresh() noexcept { diff --git a/include/utils/ReadonlyCounter.h b/include/utils/ReadonlyCounter.h index ea26cf3..4f18940 100644 --- a/include/utils/ReadonlyCounter.h +++ b/include/utils/ReadonlyCounter.h @@ -13,6 +13,7 @@ class ReadonlyCounter { protected: T _counter{}; }; +extern template class ReadonlyCounter; } // namespace arch::utils diff --git a/src/ecs/ExplicitInstantiation.cpp b/src/ecs/ExplicitInstantiation.cpp new file mode 100644 index 0000000..8aea233 --- /dev/null +++ b/src/ecs/ExplicitInstantiation.cpp @@ -0,0 +1,23 @@ +#include + +namespace arch::ecs { + +namespace _details { + +template class EntityTraits; +template class EntityTraits; +template class SparseSet; +template class SparseSet; +template class CommonComponentPool; +template class CommonComponentPool; + +} // namespace _details + +template class Domain; +template class Domain; +template class EntityPool; +template class EntityPool; + +} // namespace arch::ecs + +template class arch::utils::ReadonlyCounter; From ee40ae4779cf8627ce4b03f2fbe30a350e4bf11e Mon Sep 17 00:00:00 2001 From: Chris-plusplus <72704303+Chris-plusplus@users.noreply.github.com> Date: Fri, 11 Oct 2024 15:34:59 +0200 Subject: [PATCH 5/9] add #include to View.hpp --- include/ecs/View.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/ecs/View.hpp b/include/ecs/View.hpp index 94dd84e..a01c744 100644 --- a/include/ecs/View.hpp +++ b/include/ecs/View.hpp @@ -1,3 +1,5 @@ +#include + #include "View.h" // #include "Domain.h" From b8a0b3c700642e6290c4f373b19e0951d7a73eac Mon Sep 17 00:00:00 2001 From: Chris-plusplus <72704303+Chris-plusplus@users.noreply.github.com> Date: Fri, 11 Oct 2024 16:13:03 +0200 Subject: [PATCH 6/9] "... casts away constness..." blah blah blah --- include/ecs/View.hpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/include/ecs/View.hpp b/include/ecs/View.hpp index a01c744..2c7e138 100644 --- a/include/ecs/View.hpp +++ b/include/ecs/View.hpp @@ -75,7 +75,14 @@ auto VIEW_ECIE::getAsTuple(const E entity) noexcept requires(!Const) constexpr auto idx = Include::template find; if constexpr (_details::ComponentTraits::flag) { - return std::make_tuple(dynamic_cast>(_includedCPools[idx])->get(entity)); + return std::make_tuple( + dynamic_cast>( + const_cast, CCPoolPtr, _details::CommonComponentPool*>>( + _includedCPools[idx] + ) + ) + ->get(entity) + ); } else { return std::tie( dynamic_cast>( From 8ace7aed63ae431bfa7c5fe043a051f97fe035f5 Mon Sep 17 00:00:00 2001 From: Chris-plusplus <72704303+Chris-plusplus@users.noreply.github.com> Date: Fri, 11 Oct 2024 16:14:53 +0200 Subject: [PATCH 7/9] add #include to View.hpp --- include/ecs/View.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/ecs/View.hpp b/include/ecs/View.hpp index 2c7e138..8b65276 100644 --- a/include/ecs/View.hpp +++ b/include/ecs/View.hpp @@ -1,3 +1,4 @@ +#include #include #include "View.h" From 8fab261e1c7c5d7c0d727e4fb4d2978ccfa6f076 Mon Sep 17 00:00:00 2001 From: Chris-plusplus <72704303+Chris-plusplus@users.noreply.github.com> Date: Tue, 15 Oct 2024 12:04:08 +0200 Subject: [PATCH 8/9] Added ECS tests with examples --- tests/ecs/Component.cpp | 276 ++++++++++++++++++++++++++++++++++++++++ tests/ecs/Entity.cpp | 186 +++++++++++++++++++++++++++ tests/ecs/View.cpp | 91 +++++++++++++ 3 files changed, 553 insertions(+) create mode 100644 tests/ecs/Component.cpp create mode 100644 tests/ecs/Entity.cpp create mode 100644 tests/ecs/View.cpp diff --git a/tests/ecs/Component.cpp b/tests/ecs/Component.cpp new file mode 100644 index 0000000..698d1f5 --- /dev/null +++ b/tests/ecs/Component.cpp @@ -0,0 +1,276 @@ +#include + +#include +#include + +namespace ecs = arch::ecs; + +TEST(ECS, ComponentSimple) { + // to create your own component you need to perform very specific steps: + // 1. just create them + // 2. Lorem ipsum + // 3. You: Wait, it's that easy? + // 4. Me: Why would it be any different? + + struct Pos { + float x; + float y; + }; + + struct Vel { + float x; + float y; + }; + + // the above components are the simplest that can be + // plain structs like those model many crucial concepts (on them later) + + ecs::Domain domain; + + // components can be then added to entites: + auto e0 = domain.newEntity(); + + // create component Pos for e0 + domain.addComponent(e0); + + // they also are accessible by e0 handle: + domain.getComponent(e0).x = 4; + domain.getComponent(e0).y = 5; + + // each call to getComponent is O(1): + // 1 hash-map find, where hash result is compile-time + // 2 vector find, by index + // in Unity for example, analogous GetComponent is O(n): + // going through list of components until T is found + + // each entity can only have 1 component of each type + // this limits search time + + // attempting to create another component returns one already existing: + { + auto& oldPos = domain.getComponent(e0); + EXPECT_EQ(&oldPos, &domain.addComponent(e0)); + } + + // let's add another entity + auto e1 = domain.newEntity(); + + // and it's components + { + // addComponent returns reference to created component + auto& e1Pos = domain.addComponent(e1, 1, 2); + // arguments for creating Pos -------------^--^ + + // addComponent without template parameters + auto& e1Vel = domain.addComponent(e1, Vel{ .x = 4, .y = 6 }); + } + + // you can check if entity has component of given type + EXPECT_TRUE(domain.hasComponent(e0)); + EXPECT_FALSE(domain.hasComponent(e0)); + EXPECT_TRUE(domain.hasComponent(e1)); + EXPECT_TRUE(domain.hasComponent(e1)); + + // you can also remove components + domain.removeComponent(e1); + + // what happens when you call getComponent, but entity does not contain it? + // C++ standard calls this: Undefined Behavior (UB) + // basicly whatever can happen (most likely SEGFAULT crash) + + // how can you then shield yourself from UB? + // ofc by using optionals with tryGetComponent + { + auto e1Pos = domain.tryGetComponent(e1); + + // you can check if optional contains value: + EXPECT_FALSE(e1Pos); + EXPECT_FALSE(e1Pos.has_value()); + + // if it contains something, access it: + if (e1Pos or e1Pos.has_value()) { + auto& e1PosReference = e1Pos->get(); + // ... + } + } + + // ECS counts how many components of specific type are there + EXPECT_EQ(domain.count(), 1); + EXPECT_EQ(domain.count(), 1); + + // you can also iterate over all components of any type + for (auto&& [entity, pos] : domain.components()) { + // ... + } + for (auto&& [entity, vel] : domain.components()) { + // ... + } +} + +// what if you wanted to flag some entities? +// creating special component for that purpose is a solution + +struct WorseEnemyFlag {}; + +// however component like this will occupy space (not efficient) +// better way is to explicitly mark this component as a flag + +struct EnemyFlag { + static constexpr bool flagComponent = true; +}; + +// if empty class (no non-static field) has static compile-time constant +// flagComponent/FlagComponent/flag_component = true, then it is considered a flag-component +// flag-components are never instantiated, therefore occupy less space + +TEST(ECS, ComponentFlag) { + ecs::Domain domain; + + auto e0 = domain.newEntity(); + auto e1 = domain.newEntity(); + auto e2 = domain.newEntity(); + auto e3 = domain.newEntity(); + + domain.addComponent(e2); + domain.addComponent(e3); + + // for flag-components, getComponent == hasComponent + EXPECT_EQ(typeid(domain.getComponent(e2)), typeid(domain.hasComponent(e2))); + + // instead of reference to component, getComponent returns bool + EXPECT_TRUE(domain.getComponent(e2)); + EXPECT_TRUE(domain.getComponent(e3)); + // meaning getComponent will never result in an UB + EXPECT_FALSE(domain.getComponent(e0)); + EXPECT_FALSE(domain.getComponent(e1)); + + for (auto&& [entity, vel] : domain.components()) { + // ... + } +} + +// now let's dive deep into implementation details + +// in C++ there is a thing called 'concepts' +// they describe requirements for types, that need to be satisfied +// starting from memory size of type, ending at minute details about its methods + +// if type 'T' satisfies all requirements of a concept 'C', then we say that T models C +// for example float and double model std::floating_point and lambdas model std::invocable + +// concepts refine older C++ concept: Named Requirements +// those are abstract rules that types must follow if thew want to perform specific operaions +// concepts were added in C++20, meaning that for 22 years C++ standard library was written without them +// huge amount of std code does not depend on them, being the reason why C++ errors are so bizzare-looking +// (not to mention that for ~12 years C++ was not standardized) + +// the most important concept used in this ECS is std::movable +// it requires types to: +// be move-constructible (have move-constructor) +// be move-assignable (have move-assign operator) +// be swappable (have method swap() or have specialized std::swap) + +// now that you know what concepts are and understand what std::movable types are capable of +// I can now explain what in-place components are + +// in-place components are components which are not moved in the internal storage on basic operations like add/remove +// this means that any pointers or references to them will not be invalidated while performing +// operationson the other components of that type, in contrast to regular components +// this also means that storage for in-place components might not be random-access +// marking components are in-place components works simmilar to flag components +// you need to make compile-time constant named inPlaceComponent/InPlaceComponent/in_place_component = true + +struct Ship { + static constexpr bool inPlaceComponent = true; + + float health; + float bulletDamage; +}; + +// by default components are not in-place components +// you can still explicitly mark them as not in-place +// however if you do that, while components does not model std::movable +// you will get a compilation error, because non-movable components can only be in-place +// and non-movables will be marked as in-place by default + +// I have mentioned 'internal storage' before, but what does it mean? +// every instance of 'Ship', requires 8 bytes of memory +// this memory is handled internally by ComponentPools +// by default, this memory for components is segmented into pages of size 1024 (instances, not bytes) +// thanks to this, add operation on components will not reallocate nor move any components in memory +// It may only allocate new page if previous pages are full +// just as all the other settings, this also can be customized in class by compile-time constant +// componentPageSize/ComponentPageSize/component_page_size = +// pageSize is ignored if component is marked as flag-component +// ATTENTION! page size must be a power of two, to aquire (2^N) just write (1 << N) + +// those 3 settings can be written in definition of your class +// but it can also be put into specialization of arch::ecs::ComponentSpecs, which will override all other settings +// contrary to in-class settings, this method requires you to explicitly set all settings, not just one + +template<> +struct arch::ecs::ComponentSpecs { + // prolonging in-class setting + // if set to false, in-class setting would be overridden + static constexpr bool inPlace = true; + + // not empty -> not a flag + static constexpr bool flag = false; + + // let's change pageSize to something different + // for example: 2^5 = 32 + static constexpr size_t pageSize = (1 << 5); +}; + +// let's now check if our settings acually work + +TEST(ECS, ComponentInPlaceCustomPageSize) { + ecs::Domain domain; + + std::array entities; + for (size_t i = 0; i != 32 * 3; ++i) { + entities[i] = domain.newEntity(); + + domain.addComponent(entities[i]); + } + + // for later + auto& shipOf0 = domain.getComponent(entities[0]); + auto& shipOf31 = domain.getComponent(entities[31]); + + for (size_t i = 0; i != 32; ++i) { + // getting ships 32 (pageSize) instances apart + auto& ship1 = domain.getComponent(entities[i]); + auto& ship2 = domain.getComponent(entities[i + 32]); + auto& ship3 = domain.getComponent(entities[i + 64]); + + // normally ship2 would be 32 instances furhter than ship2 + // but since they are on different pages, they are not + // (there still is non-zero chance of them being that close, because of that tests below are commented out) + // ASSERT_NE(&ship1 + 32, &ship2); + // ASSERT_NE(&ship1 + 64, &ship3); + // ASSERT_NE(&ship2 + 32, &ship3); + } + + for (size_t i = 1; i != 31; ++i) { + // entities 0-31 have Ship component + // let's remove them from 1-30 + domain.removeComponent(entities[i]); + } + + // now we can be certain that Ships for 0 and 31 are not close in memory + // (by close means one instance after another) + ASSERT_NE(abs(&domain.getComponent(entities[31]) - &domain.getComponent(entities[0])), sizeof(Ship)); + + // also their place in memory has not changed + ASSERT_EQ(&shipOf0, &domain.getComponent(entities[0])); + ASSERT_EQ(&shipOf31, &domain.getComponent(entities[31])); +} + +// this is mostly it for components +// remember that these are just examples +// not every components needs to be full-public struct +// ECS supports classes with private fields/methods +// but doesn't suppot polymorphic classes +// because being polymorphic makes memory of an object +// very complex with virtual tables, and very 'implementation-defined' diff --git a/tests/ecs/Entity.cpp b/tests/ecs/Entity.cpp new file mode 100644 index 0000000..89c93b9 --- /dev/null +++ b/tests/ecs/Entity.cpp @@ -0,0 +1,186 @@ +#include +#include + +namespace ecs = arch::ecs; +using Traits = ecs::_details::EntityTraits; + +TEST(ECS, EntityCreationAlive) { + // e64 - 64-bit entity handle + // there also exists e32, with smaller range + ecs::Domain domain; + + for (size_t i = 0; i != 100; ++i) { + auto e = domain.newEntity(); + + // entities' ids start from 0 + EXPECT_EQ(Traits::Id::part(e), i); + // entities' versions start from 0 + EXPECT_EQ(Traits::Version::part(e), 0); + + EXPECT_TRUE(domain.alive(e)); + } +} + +TEST(ECS, EntityKillAlive) { + ecs::Domain domain; + std::array toDelete; + + for (size_t i = 0; i != 100; ++i) { + auto e = domain.newEntity(); + + // mark some entites for deletion + if (i % 10 == 0) { + toDelete[i / 10] = e; + } + + // entities' ids start from 0 + EXPECT_EQ(Traits::Id::part(e), i); + // entities' versions start from 0 + EXPECT_EQ(Traits::Version::part(e), 0); + + EXPECT_TRUE(domain.alive(e)); + } + for (auto&& e : toDelete) { + domain.kill(e); + + // killed entities are not alive + EXPECT_FALSE(domain.alive(e)); + } + + // discouraged usage vvv + // EXPECT_FALSE(domain.alive(Traits::Entity::fromParts(10, 0))); + // only handles aquired by interactions with domain are guaranteed to give valid results + // but since its C++ you can do whatever you want +} + +TEST(ECS, EntityRecycle) { + ecs::Domain domain; + + // 10 entites + for (size_t i = 0; i != 10; ++i) { + auto e = domain.newEntity(); + } + + // 10 entites, but marked for deletion + std::array toDelete; + for (size_t i = 0; i != 10; ++i) { + toDelete[i] = domain.newEntity(); + } + + // 10 entites + for (size_t i = 0; i != 10; ++i) { + auto e = domain.newEntity(); + } + + // 30 entities total + + for (auto&& td : toDelete) { + domain.kill(td); + + EXPECT_FALSE(domain.alive(td)); + } + + // recycle entities in reverse order + for (auto&& toRecycle : std::views::reverse(toDelete)) { + auto recycled = domain.recycleEntity(toRecycle); + + // recycling entities by handle preserves version + EXPECT_EQ(recycled, toRecycle); + + // meaning both recycled and toRecycle are equal + EXPECT_TRUE(domain.alive(recycled)); + EXPECT_TRUE(domain.alive(toRecycle)); + } + for (auto&& td : toDelete) { + domain.kill(td); + + EXPECT_FALSE(domain.alive(td)); + } + + for (auto&& toAdd : std::views::reverse(toDelete)) { + // recycling entities' ids + auto recycled = domain.recycleId(Traits::Id::part(toAdd)); + + // recycling ids does not preserve version, its incremented + EXPECT_EQ(Traits::Version::part(recycled), 1); + + // that's why recycled is alive, toRecycle is not + EXPECT_TRUE(domain.alive(recycled)); + EXPECT_FALSE(domain.alive(toAdd)); + } +} + +TEST(ECS, EntityRecycleExisting) { + ecs::Domain domain; + + // 10 entites + for (size_t i = 0; i != 10; ++i) { + auto e = domain.newEntity(); + } + + // 10 entites, but marked for deletion + std::array toRecycle; + for (size_t i = 0; i != 10; ++i) { + toRecycle[i] = domain.newEntity(); + } + + // 10 entites + for (size_t i = 0; i != 10; ++i) { + auto e = domain.newEntity(); + } + + // 30 entities total, none deleted + + for (auto&& e : toRecycle) { + // recycling existing entites/ids + auto recycled1 = domain.recycleEntity(e); + auto recycled2 = domain.recycleId(Traits::Id::part(e)); + // returns null entity + EXPECT_EQ(recycled1, domain.null); + EXPECT_EQ(recycled2, domain.null); + } +} + +// normally there are two entity types: e32 and e64 +// they are not special in themselves, just enum types +// ECS allows you to create your own entity types like this: + +// 16-bit entity +enum class Entity16Bit : uint16_t; + +// however, Entity16Bit is not usable yet +// lets create example configuration +struct Entity16BitConfiguration { + // type of entity + using EntityT = Entity16Bit; + // bit lenght of id part of handle, for example 12 + static inline constexpr size_t idLength = 12; + // type of id part of handle, must be big enough to store 12 bits + using IdT = uint16_t; + // type of version part of handle, must be big enough to store 4 bits + using VersionT = uint8_t; + // size of pages in sparse sets, default is 1024 + // ATTENTION! pageSize must be a power of 2 + // tip: (2^N) can be written as (1 << N) + static inline constexpr size_t pageSize = 1'024; +}; + +// to be visible to ECS you need to specialize arch::ecs::EntitySpecs +// you can use AutoEntitySpecs +template<> +struct arch::ecs::EntitySpecs: arch::ecs::AutoEntitySpecs {}; + +// or your previous configuration: Entity16BitConfiguration + +// and thats basically it, ECS will compute rest of the info by itself +// you can now proudly use you entities + +TEST(ECS, EntityCustom) { + ecs::Domain domain; + + auto e0 = domain.newEntity(); + + // ... + + EXPECT_EQ(typeid(e0), typeid(Entity16Bit)); +} diff --git a/tests/ecs/View.cpp b/tests/ecs/View.cpp new file mode 100644 index 0000000..48cf1b2 --- /dev/null +++ b/tests/ecs/View.cpp @@ -0,0 +1,91 @@ +#include + +#include +#include + +namespace ecs = arch::ecs; + +TEST(ECS, ViewsSimple) { + struct Pos { + float x; + float y; + }; + + struct Vel { + float x; + float y; + }; + + ecs::Domain domain; + + // let's create some entities with some components + + for (size_t i = 0; i != 10'000; ++i) { + auto e = domain.newEntity(); + + domain.addComponent(e); + auto ifToAdd = rand(); + if (i % (ifToAdd ? ifToAdd : 1)) { + domain.addComponent(e); + } + } + + // that sure is a lot of entites and components + // now let's update their positions + // + // ... + // + // wait, which ones? + // if only you could find all entites with Pos and Vel + // actually, you can using views + + { auto viewPosVal = domain.view(); } + + // hold on, what if you accidentaly also update Vel? + // add 'const' to Vel to make it readonly, any change to Vel will now be an error + + auto viewPosVel = domain.view(); + + // now you have to choose how you update all positions + + // option 1. manually obtain components + for (ecs::e32 entity : viewPosVel) { + { + // get tuple with references + auto posVelTuple = viewPosVel.get(entity); + // access tuple + auto& pos = std::get<0>(posVelTuple); + auto& vel = std::get<1>(posVelTuple); + } + + // the above can be abbreviated with: + auto&& [pos, vel] = viewPosVel.get(entity); + + pos.x += vel.x; + pos.y += vel.y; + + break; // exit for + } + + // option 2. use view.all() + for (auto&& [entity, pos, vel] : viewPosVel.all()) { + pos.x += vel.x; + pos.y += vel.y; + + break; // exit for + } + + // option 3. use view.forEach(entity) + viewPosVel.forEach([&viewPosVel /* capture view */](ecs::e32 entity) { + // auto&& [pos, vel] = viewPosVel.get(entity); + + // pos.x += vel.x; + // pos.y += vel.y; + }); + + // option 4. use view.forEach(entity, &pos, const &vel) + viewPosVel.forEach([&viewPosVel /* capture view */](ecs::e32 entity, Pos& pos, const Vel& vel) { + // pos.x += vel.x; + // pos.y += vel.y; + }); +} From 38b7feba8625a639266eb227abeb2f2ad797cbf3 Mon Sep 17 00:00:00 2001 From: Chris-plusplus <72704303+Chris-plusplus@users.noreply.github.com> Date: Mon, 21 Oct 2024 23:41:45 +0200 Subject: [PATCH 9/9] Revert main.cpp & conanfile.py --- archimedes_bin/main.cpp | 823 +--------------------------------------- conanfile.py | 1 - 2 files changed, 7 insertions(+), 817 deletions(-) diff --git a/archimedes_bin/main.cpp b/archimedes_bin/main.cpp index 00d45e4..507d013 100644 --- a/archimedes_bin/main.cpp +++ b/archimedes_bin/main.cpp @@ -1,826 +1,17 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -#include #include #include -#include -#include -#include - -#pragma region iter_tests - -struct iter1 { - static inline constexpr size_t pageSize = 1'024; - - inline iter1(std::vector>* _vec, size_t _offset) noexcept: vec{ _vec }, offset{ _offset } {} - - inline iter1(std::vector>& x) noexcept: iter1(&x, 0) {} - - inline static iter1 begin(std::vector>& x) noexcept { return iter1(x); } - - inline static iter1 end(std::vector>& x) noexcept { return iter1(&x, 1'024 * x.size()); } - - inline size_t& operator*() noexcept { return (*vec)[offset >> 10][offset & ((size_t)1'024 - (size_t)1)]; } - - inline iter1& operator++() noexcept { - ++offset; - return *this; - } - - std::vector>* vec; - size_t offset; -}; - -struct iter2 { - static inline constexpr size_t pageSize = 1'024; - - inline iter2(std::vector* _vec, size_t _offset) noexcept: vec{ _vec }, offset{ _offset } {} - - inline iter2(std::vector>& x) noexcept: iter2(&x.front(), 0) {} - - inline static iter2 begin(std::vector>& x) noexcept { return iter2(x); } - - inline static iter2 end(std::vector>& x) noexcept { return iter2(&x.back() + 1, 0); } - - inline size_t& operator*() noexcept { return (*vec)[offset]; } - - inline iter2& operator++() noexcept { - /*++offset; - if (offset & pageSize) { - offset = 0; - }*/ - //++offset; - if ((++offset) == pageSize) { - offset = 0; - } - if (!offset) { - ++vec; - } - return *this; - } - - std::vector* vec; - size_t offset; -}; - -struct iter3 { - static inline constexpr size_t pageSize = 1'024; - - inline iter3(std::vector* _vec, size_t _offset) noexcept: vec{ _vec }, offset{ _offset } {} - - inline iter3(std::vector>& x) noexcept: iter3(&x.front(), 0) {} - - inline static iter3 begin(std::vector>& x) noexcept { return iter3(x); } - - inline static iter3 end(std::vector>& x) noexcept { return iter3(&x.back() + 1, 0); } - - inline size_t& operator*() noexcept { return (*vec)[offset]; } - - inline iter3& operator++() noexcept { - offset = (offset + 1) & ((size_t)1'023); - if (!offset) { - ++vec; - } - return *this; - } - - std::vector* vec; - size_t offset; -}; - -inline bool operator==(const iter1& one, const iter1& other) noexcept { - return one.vec == other.vec && one.offset == other.offset; -} - -inline bool operator==(const iter2& one, const iter2& other) noexcept { - return one.vec == other.vec && one.offset == other.offset; -} - -inline bool operator==(const iter3& one, const iter3& other) noexcept { - return one.vec == other.vec && one.offset == other.offset; -} - -struct XD { - // static inline constexpr bool in_place_delete = true; - int x; - - XD() = default; - XD(int _x): x{ _x } {}; - - ~XD() { - // - } - - XD(const XD& xd) { - x = xd.x; - std::cout << std::format("copy: {} -> {}\n", (size_t)&xd, (size_t)this); - } - - XD(XD&& xd) { - x = xd.x; - xd.x = 0; - std::cout << std::format("move: {} -> {}\n", (size_t)&xd, (size_t)this); - } - - XD& operator=(const XD& other) { - x = other.x; - std::cout << std::format("copy: {} -> {}\n", (size_t)&other, (size_t)this); - return *this; - } - XD& operator=(XD&& other) { - x = other.x; - other.x = 0; - std::cout << std::format("move: {} -> {}\n", (size_t)&other, (size_t)this); - return *this; - } -}; - -struct XD2 { - int x; - - XD2() = default; - XD2(int _x): x{ _x } {}; - XD2(const XD2&) = default; - XD2(XD2&&) = default; - - XD2& operator=(const XD2& other) { - x = other.x; - return *this; - } - - XD2& operator=(XD2&& other) { - x = other.x; - other.x = 0; - return *this; - } -}; - -// using traits = entt::sparse_set::traits_type; - -#pragma endregion - -namespace arch::ecs { - -enum e16 : u16; -enum e8 : u8; - -template<> -struct EntitySpecs: AutoEntitySpecs {}; - -template<> -struct EntitySpecs: AutoEntitySpecs {}; - -} // namespace arch::ecs - -struct NonMoveable { - static inline constexpr bool in_place_delete = true; - - int val = 0; - - NonMoveable(){}; - NonMoveable(int v): val{ v } {}; - NonMoveable(const NonMoveable&) = default; - NonMoveable(NonMoveable&&) = default; - - NonMoveable& operator=(int v) { - val = v; - return *this; - } - - NonMoveable& operator=(const NonMoveable&) = default; - NonMoveable& operator=(NonMoveable&&) = default; -}; - -struct Empty {}; - -struct NoUniqueAddress { - int x; - float d; - size_t mogus; - int sus; - [[no_unique_address]] Empty empty; - int xd3; -}; - -using clk = std::chrono::high_resolution_clock; - -namespace ecs = arch::ecs; - -struct Flag { - // static inline constexpr bool inPlaceComponent = true; - static inline constexpr bool flagComponent = true; -}; - -struct Flag2 { - static inline constexpr bool inPlaceComponent = true; - // static inline constexpr bool flagComponent = true; -}; - -#include - -struct Pos { - double x, y; -}; - -struct Vel { - double x, y; +struct MyApp: arch::Application { + void init() override { arch::Logger::info("Initializing user app!"); } }; int main() { - /*auto registry = entt::registry(); - - auto entities = std::vector(); - for (size_t i = 0; i != 8; ++i) { - entities.emplace_back(registry.create()); - registry.emplace(entities.back(), 0.0, 0.0); - if (i % 2) { - registry.emplace(entities.back(), 0.0, 0.0); - } - } - - auto group = registry.group(entt::get);*/ - - arch::Logger::init(arch::LogLevel::info); - - ecs::Domain domain; - - auto e0 = domain.newEntity(); - auto e1 = domain.newEntity(); - auto e2 = domain.newEntity(); - auto e3 = domain.newEntity(); - auto e4 = domain.newEntity(); - auto e5 = domain.newEntity(); - auto e6 = domain.newEntity(); - auto e7 = domain.newEntity(); - auto e8 = domain.newEntity(); - auto e9 = domain.newEntity(); - auto e10 = domain.newEntity(); - - domain.addComponent(e0); - domain.addComponent(e1); - domain.addComponent(e2); - domain.addComponent(e3); - domain.addComponent(e4); - domain.addComponent(e5); - domain.addComponent(e6); - domain.addComponent(e2); - domain.addComponent(e3); - domain.addComponent(e6); - - auto view = domain.view(); - - view.forEach([&view](auto entity) { - auto&& [flag, flag2] = view.getAll(entity); - std::cout << std::format("{}\n", (uint32_t)entity); - }); - - domain.kill(e6); - - view.refresh().forEach([&view](ecs::e32 entity) { - auto&& [flag, flag2] = view.getAll(entity); - std::cout << std::format("{}\n", (uint32_t)entity); - }); - -#pragma region OldTests - - /*arch::ecs::EntityPool pool; - using Traits = decltype(pool)::Traits; - - auto&& [sparse, dense] = pool.debug(); - - for (auto i = 0; i != 10; ++i) { - auto e = pool.newEntity(); - } - - auto e2 = Traits::Entity::fromParts(2, 0); - - pool.kill(e2); - - for (auto&& e : pool) { - std::cout << std::format("{}|{}\n", Traits::Id::part(e), Traits::Version::part(e)); - } - - std::cout << '\n'; - - for (auto&& e : *dense) { - std::cout << std::format("{}|{}\n", Traits::Id::part(e), Traits::Version::part(e)); - } - - std::cout << '\n'; - - std::cout << std::format("{}\n", pool.alive(Traits::Entity::fromParts(2, 1))); - std::cout << std::format("{}\n", pool.alive(Traits::Entity::fromParts(2, 0))); - std::cout << std::format("{}\n", pool.contains(2)); - - std::cout << '\n'; - - e2 = pool.recycleEntity(Traits::Entity::fromParts(2, 254)); - - for (auto&& e : pool) { - std::cout << std::format("{}|{}\n", Traits::Id::part(e), Traits::Version::part(e)); - } - - std::cout << '\n'; - - std::cout << std::format("{}\n", pool.alive(e2)); - std::cout << std::format("{}\n", pool.alive(Traits::Entity::fromParts(2, 0))); - std::cout << std::format("{}\n", pool.contains(2));*/ - - /*entt::registry reg; - - auto e1 = reg.create(); - auto e2 = reg.create(); - auto e3 = reg.create(); - auto e4 = reg.create(); - - reg.emplace(e1, 420); - reg.emplace(e2, 1'337); - reg.emplace(e3, 360); - reg.emplace(e4, 69); - - reg.view().each([](const auto e, auto& c) { std::cout << c.val << '\n'; }); - - reg.sort([](const NonMoveable& lhs, const NonMoveable& rks) { return lhs.val < rks.val; }); - reg.view().each([](const auto e, auto& c) { std::cout << c.val << '\n'; });*/ - - /*entt::entity x = traits::construct(0, std::numeric_limits::max() - 1); - - entt::sparse_set set{ entt::deletion_policy::swap_only }; - - set.push(x); - - for (auto&& e : set) { - std::cout << std::format("{}|{}\n", traits::to_entity(e), traits::to_version(e)); - } - - set.erase(x); - - std::cout << set.contains(x) << '\n'; - - for (auto&& e : set) { - std::cout << std::format("{}|{}\n", traits::to_entity(e), traits::to_version(e)); - }*/ - - /*entt::sparse_set set{ entt::deletion_policy::swap_only }; - - set.push(traits::construct(0, 0));*/ - - /*entt::registry reg; - - auto e0 = reg.create(); - auto e1 = reg.create(); - auto e2 = reg.create(); - auto e3 = reg.create(); - auto e4 = reg.create(); - - reg.emplace(e0, 0); - reg.emplace(e1, 1); - reg.emplace(e2, 2); - reg.emplace(e3, 3); - reg.emplace(e4, 4); - - { - auto view = reg.view(); - - view.each([](const auto entity, auto&& xd) { - std::cout << std::format( - "{}|{} : {}->{}\n", - traits::to_entity(entity), - traits::to_version(entity), - (size_t)&xd, - xd.x - ); - }); - } - std::cout << '\n'; - - reg.erase(e2); - - { - auto view = reg.view(); - - view.each([](const auto entity, auto&& xd) { - std::cout << std::format( - "{}|{} : {}->{}\n", - traits::to_entity(entity), - traits::to_version(entity), - (size_t)&xd, - xd.x - ); - }); - } - - reg.emplace(e2, 2); - std::cout << std::format("\n"); - - { - auto view = reg.view(); - - view.each([](const auto entity, auto&& xd) { - std::cout << std::format( - "{}|{} : {}->{}\n", - traits::to_entity(entity), - traits::to_version(entity), - (size_t)&xd, - xd.x - ); - }); - }*/ - - /*entt::registry reg; - - auto entt1 = reg.create(); - auto entt2 = reg.create(); - auto entt3 = reg.create(); - auto entt4 = reg.create(); - reg.emplace(entt1, 1); - reg.emplace(entt2, 2); - reg.emplace(entt3, 3); - reg.emplace(entt4, 4); - - auto view = reg.view(); - view.each([](auto& xd) { std::cout << std::format("({})->x = {}\n", (void*)&xd, xd.x); }); - - reg.erase(entt3); - - view = reg.view(); - view.each([](auto& xd) { std::cout << std::format("({})->x = {}\n", (void*)&xd, xd.x); });*/ - - /*namespace chr = std::chrono; - namespace rg = std::ranges; - namespace vw = std::views; - using clk = chr::high_resolution_clock; - - constexpr size_t pageSize = 10; - - std::array, pageSize> map; - for (auto&& row : map) { - for (auto&& val : row) { - val = '#'; - } - } - map[0][0] = 'X'; - std::cout << "\033[2J\033[1;1H"; - for (auto&& row : map) { - for (auto&& val : row) { - std::cout << val; - } - std::cout << '\n'; - } - - size_t x = 0; - size_t y = 0; - for (;;) { - long long dx; - std::cin >> dx; - - map[y][x] = '#'; - - x += dx; - if (dx < 0) { - if (x > pageSize) { - x = (size_t)0 - x; - y -= 1 + (x / pageSize); - x = pageSize - (x) % pageSize; - } - } else { - y += x / pageSize; - x %= pageSize; - } - std::cout << y << ' ' << x << '\n'; - getchar(); - getchar(); - - map[y][x] = 'X'; - - std::cout << "\033[2J\033[1;1H"; - for (auto&& row : map) { - for (auto&& val : row) { - std::cout << val; - } - std::cout << '\n'; - } - } - - return 0;*/ - - /*entt::basic_storage storage; - - for (size_t i = 0; i != 1'000'000; ++i) { - storage.emplace((entt::entity)i, (int)i); - }*/ - - /*{ - auto start = clk::now(); - for (auto&& o : storage) { - ++o.x; - } - auto end = clk::now(); - std::cout << std::format("{}\n", end - start); - }*/ - /*{ - using alloc_traits = std::allocator_traits; - using traits_type = entt::component_traits; - auto start = clk::now(); - auto _begin = storage.rbegin(); - auto containter = *(std::vector< - typename alloc_traits::pointer, - typename alloc_traits::template rebind_alloc>**)&_begin; - for (auto&& o : *containter) { - auto end = o + traits_type::page_size; - for (auto i = o; i != end; ++i) { - ++(i->x); - } - } - auto end = clk::now(); - std::cout << std::format("{}\n", end - start); - }*/ - - // size_t iters = 5 * 10'000; - - //{ - // std::vector> nums; - // for (size_t i = 0; i != 1'024; ++i) { - // nums.emplace_back(); - // for (size_t j = 0; j != 1'024; ++j) { - // nums.back().emplace_back(i * 1'024 + j); - // } - // // std::cout << i << ": " << nums.back().size() << '\n'; - // } - // decltype(clk::now() - clk::now()) avg = {}; - // for (size_t n = 0; n != iters; ++n) { - // if (n % (iters / 10) == 0) { - // std::cout << std::format("n = {}\n", n); - // } - - // // std::cout << nums.size() << '\n'; - - // { - // auto start = clk::now(); - // for (auto i = iter1::begin(nums), end = iter1::end(nums); i != end; ++i) { - // ++(*i); - // } - // auto end = clk::now(); - // avg += end - start; - // } - // } - // avg /= iters; - // std::cout << std::format("custom iter1: {}\n", avg); - //} - - /// ITERS TEST - /// ITERS TEST - /// ITERS TEST - /// ITERS TEST - /// ITERS TEST - - // size_t iters = 5 * 10'000; - - //{ - // std::vector> nums; - // for (size_t i = 0; i != 1'024; ++i) { - // nums.emplace_back(); - // for (size_t j = 0; j != 1'024; ++j) { - // nums.back().emplace_back(i * 1'024 + j); - // } - // // std::cout << i << ": " << nums.back().size() << '\n'; - // } - // decltype(clk::now() - clk::now()) avg = {}; - // for (size_t n = 0; n != iters; ++n) { - // if (n % (iters / 10) == 0) { - // std::cout << std::format("n = {}\n", n); - // } - // // std::cout << nums.size() << '\n'; - - // { - // auto start = clk::now(); - // for (auto i = iter2::begin(nums), end = iter2::end(nums); i != end; ++i) { - // ++(*i); - // } - // auto end = clk::now(); - // avg += end - start; - // } - // } - // avg /= iters; - // std::cout << std::format("custom iter2: {}\n", avg); - //} - - //{ - // std::vector> nums; - // for (size_t i = 0; i != 1'024; ++i) { - // nums.emplace_back(); - // for (size_t j = 0; j != 1'024; ++j) { - // nums.back().emplace_back(i * 1'024 + j); - // } - // // std::cout << i << ": " << nums.back().size() << '\n'; - // } - // decltype(clk::now() - clk::now()) avg = {}; - // for (size_t n = 0; n != iters; ++n) { - // if (n % (iters / 10) == 0) { - // std::cout << std::format("n = {}\n", n); - // } - // // std::cout << nums.size() << '\n'; - - // { - // auto start = clk::now(); - // for (auto i = iter3::begin(nums), end = iter3::end(nums); i != end; ++i) { - // ++(*i); - // } - // auto end = clk::now(); - // avg += end - start; - // } - // } - // avg /= iters; - // std::cout << std::format("custom iter3: {}\n", avg); - //} - - //{ - // std::vector> nums; - // for (size_t i = 0; i != 1'024; ++i) { - // nums.emplace_back(); - // for (size_t j = 0; j != 1'024; ++j) { - // nums.back().emplace_back(i * 1'024 + j); - // } - // // std::cout << i << ": " << nums.back().size() << '\n'; - // } - // decltype(clk::now() - clk::now()) avg = {}; - // for (size_t n = 0; n != iters; ++n) { - // if (n % (iters / 10) == 0) { - // std::cout << std::format("n = {}\n", n); - // } - // // std::cout << nums.size() << '\n'; - - // { - // auto start = clk::now(); - // for (size_t i = 0; i != nums.size(); ++i) { - // for (size_t j = 0; j != nums[i].size(); ++j) { - // ++nums[i][j]; - // } - // } - // auto end = clk::now(); - // avg += end - start; - // } - // } - // avg /= iters; - // std::cout << std::format("custom iter3: {}\n", avg); - //} - - //{ - // std::vector> nums; - // for (size_t i = 0; i != 1'024; ++i) { - // nums.emplace_back(); - // for (size_t j = 0; j != 1'024; ++j) { - // nums.back().emplace_back(i * 1'024 + j); - // } - // // std::cout << i << ": " << nums.back().size() << '\n'; - // } - // decltype(clk::now() - clk::now()) avg = {}; - // for (size_t n = 0; n != iters; ++n) { - // if (n % (iters / 10) == 0) { - // std::cout << std::format("n = {}\n", n); - // } - // // std::cout << nums.size() << '\n'; - - // { - // auto start = clk::now(); - // for (auto&& v : nums) { - // for (auto&& o : v) { - // ++o; - // } - // } - // auto end = clk::now(); - // avg += end - start; - // } - // } - // avg /= iters; - // std::cout << std::format("custom iter4: {}\n", avg); - //} - - //{ - // std::vector> nums; - // for (size_t i = 0; i != 1'024; ++i) { - // nums.emplace_back(); - // for (size_t j = 0; j != 1'024; ++j) { - // nums.back().emplace_back(i * 1'024 + j); - // } - // // std::cout << i << ": " << nums.back().size() << '\n'; - // } - // decltype(clk::now() - clk::now()) avg = {}; - // for (size_t n = 0; n != iters; ++n) { - // if (n % (iters / 10) == 0) { - // std::cout << std::format("n = {}\n", n); - // } - // // std::cout << nums.size() << '\n'; - - // { - // auto start = clk::now(); - // auto&& view = nums | vw::join; - // for (auto&& o : view) { - // ++o; - // } - // auto end = clk::now(); - // avg += end - start; - // } - // } - // avg /= iters; - // std::cout << std::format("custom iter5: {}\n", avg); - //} - - //{ - // std::vector> nums; - // for (size_t i = 0; i != 1'024; ++i) { - // nums.emplace_back(); - // for (size_t j = 0; j != 1'024; ++j) { - // nums.back().emplace_back(i * 1'024 + j); - // } - // // std::cout << i << ": " << nums.back().size() << '\n'; - // } - // auto&& view = nums | vw::join; - // decltype(clk::now() - clk::now()) avg = {}; - // for (size_t n = 0; n != iters; ++n) { - // if (n % (iters / 10) == 0) { - // std::cout << std::format("n = {}\n", n); - // } - // // std::cout << nums.size() << '\n'; - - // { - // auto start = clk::now(); - // for (auto&& o : view) { - // ++o; - // } - // auto end = clk::now(); - // avg += end - start; - // } - // } - // avg /= iters; - // std::cout << std::format("custom iter6: {}\n", avg); - //} - - // avg = {}; - // for (size_t n = 0; n != 1'000; ++n) { - // if (n % 100 == 0) { - // std::cout << std::format("n = {}\n", n); - // } - // std::vector> nums; - // for (size_t i = 0; i != 1'024; ++i) { - // nums.emplace_back(); - // for (size_t j = 0; j != 1'024; ++j) { - // nums.back().emplace_back(i * 1'024 + j); - // } - // // std::cout << i << ": " << nums.back().size() << '\n'; - // } - // // std::cout << nums.size() << '\n'; - - // { - // auto start = clk::now(); - // for (size_t i = 0; i != nums.size(); ++i) { - // for (size_t j = 0; j != nums[i].size(); ++j) { - // ++nums[i][j]; - // } - // } - // auto end = clk::now(); - // avg += end - start; - // } - //} - // avg /= 1'000; - // std::cout << std::format("loop: {}\n", avg); + arch::Logger::init(arch::LogLevel::trace); - // avg = {}; - // for (size_t n = 0; n != 1'000; ++n) { - // if (n % 100 == 0) { - // std::cout << std::format("n = {}\n", n); - // } - // std::vector> nums; - // for (size_t i = 0; i != 1'024; ++i) { - // nums.emplace_back(); - // for (size_t j = 0; j != 1'024; ++j) { - // nums.back().emplace_back(i * 1'024 + j); - // } - // // std::cout << i << ": " << nums.back().size() << '\n'; - // } - // // std::cout << nums.size() << '\n'; + arch::EngineConfig config{ 600, 480, "Archimedes Test", glm::vec4(0, 0, 0, 0) }; - // { - // auto start = clk::now(); - // for (auto&& v : nums) { - // for (auto&& o : v) { - // ++o; - // } - // } - // auto end = clk::now(); - // avg += end - start; - // } - //} - // avg /= 1'000; - // std::cout << std::format("iterator: {}\n", avg); + arch::Ref myApp = arch::createRef(); -#pragma endregion + arch::Engine engine{ config, myApp }; + engine.start(); } diff --git a/conanfile.py b/conanfile.py index c4b4856..f995d36 100644 --- a/conanfile.py +++ b/conanfile.py @@ -14,7 +14,6 @@ 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") # Vulkan SDK self.requires("volk/1.3.268.0")