Archetypes now based on bitsets (performance++)
This commit is contained in:
parent
219af9d803
commit
bda170bf6d
2 changed files with 77 additions and 107 deletions
|
@ -604,8 +604,8 @@ namespace
|
||||||
|
|
||||||
// Benchmark entity counts
|
// Benchmark entity counts
|
||||||
constexpr int SMALL = 1'000;
|
constexpr int SMALL = 1'000;
|
||||||
constexpr int MEDIUM = 5'000;
|
constexpr int MEDIUM = 10'000;
|
||||||
constexpr int LARGE = 10'000;
|
constexpr int LARGE = 50'000;
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
TEST_CASE("Core operations benchmarks", "[benchmark]")
|
TEST_CASE("Core operations benchmarks", "[benchmark]")
|
||||||
|
@ -625,7 +625,7 @@ TEST_CASE("Core operations benchmarks", "[benchmark]")
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
BENCHMARK_ADVANCED("Create entities [5000]")(
|
BENCHMARK_ADVANCED("Create entities [10000]")(
|
||||||
Catch::Benchmark::Chronometer meter)
|
Catch::Benchmark::Chronometer meter)
|
||||||
{
|
{
|
||||||
meter.measure(
|
meter.measure(
|
||||||
|
@ -640,7 +640,7 @@ TEST_CASE("Core operations benchmarks", "[benchmark]")
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
BENCHMARK_ADVANCED("Create entities [10000]")(
|
BENCHMARK_ADVANCED("Create entities [50000]")(
|
||||||
Catch::Benchmark::Chronometer meter)
|
Catch::Benchmark::Chronometer meter)
|
||||||
{
|
{
|
||||||
meter.measure(
|
meter.measure(
|
||||||
|
@ -672,7 +672,7 @@ TEST_CASE("Core operations benchmarks", "[benchmark]")
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
BENCHMARK_ADVANCED("Create entities with components [5000]")(
|
BENCHMARK_ADVANCED("Create entities with components [10000]")(
|
||||||
Catch::Benchmark::Chronometer meter)
|
Catch::Benchmark::Chronometer meter)
|
||||||
{
|
{
|
||||||
meter.measure(
|
meter.measure(
|
||||||
|
@ -689,7 +689,7 @@ TEST_CASE("Core operations benchmarks", "[benchmark]")
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
BENCHMARK_ADVANCED("Create entities with components [10000]")(
|
BENCHMARK_ADVANCED("Create entities with components [50000]")(
|
||||||
Catch::Benchmark::Chronometer meter)
|
Catch::Benchmark::Chronometer meter)
|
||||||
{
|
{
|
||||||
meter.measure(
|
meter.measure(
|
||||||
|
@ -730,7 +730,7 @@ TEST_CASE("Component operations benchmarks", "[benchmark]")
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
BENCHMARK_ADVANCED("Add component to existing entities [5000]")(
|
BENCHMARK_ADVANCED("Add component to existing entities [10000]")(
|
||||||
Catch::Benchmark::Chronometer meter)
|
Catch::Benchmark::Chronometer meter)
|
||||||
{
|
{
|
||||||
world w;
|
world w;
|
||||||
|
@ -751,7 +751,7 @@ TEST_CASE("Component operations benchmarks", "[benchmark]")
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
BENCHMARK_ADVANCED("Add component to existing entities [10000]")(
|
BENCHMARK_ADVANCED("Add component to existing entities [50000]")(
|
||||||
Catch::Benchmark::Chronometer meter)
|
Catch::Benchmark::Chronometer meter)
|
||||||
{
|
{
|
||||||
world w;
|
world w;
|
||||||
|
@ -795,7 +795,7 @@ TEST_CASE("Component operations benchmarks", "[benchmark]")
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
BENCHMARK_ADVANCED("Remove component from entities [5000]")(
|
BENCHMARK_ADVANCED("Remove component from entities [10000]")(
|
||||||
Catch::Benchmark::Chronometer meter)
|
Catch::Benchmark::Chronometer meter)
|
||||||
{
|
{
|
||||||
world w;
|
world w;
|
||||||
|
@ -818,7 +818,7 @@ TEST_CASE("Component operations benchmarks", "[benchmark]")
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
BENCHMARK_ADVANCED("Remove component from entities [10000]")(
|
BENCHMARK_ADVANCED("Remove component from entities [50000]")(
|
||||||
Catch::Benchmark::Chronometer meter)
|
Catch::Benchmark::Chronometer meter)
|
||||||
{
|
{
|
||||||
world w;
|
world w;
|
||||||
|
|
126
zecsy.hpp
126
zecsy.hpp
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <bitset>
|
||||||
#include <concepts>
|
#include <concepts>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
@ -10,6 +11,10 @@
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#ifndef ZECSY_MAX_COMPONENTS
|
||||||
|
#define ZECSY_MAX_COMPONENTS 32
|
||||||
|
#endif // !ZECSY_MAX_COMPONENTS
|
||||||
|
|
||||||
namespace zecsy
|
namespace zecsy
|
||||||
{
|
{
|
||||||
using entity_id = uint64_t;
|
using entity_id = uint64_t;
|
||||||
|
@ -79,8 +84,9 @@ namespace zecsy
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using comp_id = size_t;
|
using comp_id = size_t;
|
||||||
std::set<entity_id> alive_entities;
|
using zecsy_bits = std::bitset<ZECSY_MAX_COMPONENTS>;
|
||||||
std::unordered_map<entity_id, std::set<comp_id>> entity_to_comps;
|
|
||||||
|
std::unordered_map<entity_id, zecsy_bits> entity_to_comps;
|
||||||
entity_id entity_counter = 0;
|
entity_id entity_counter = 0;
|
||||||
|
|
||||||
size_t query_archetypes_checked = 0;
|
size_t query_archetypes_checked = 0;
|
||||||
|
@ -96,16 +102,10 @@ namespace zecsy
|
||||||
|
|
||||||
std::unordered_map<comp_id, component_pool> pools;
|
std::unordered_map<comp_id, component_pool> pools;
|
||||||
|
|
||||||
using archetype_signature = std::vector<comp_id>;
|
using archetype_signature = zecsy_bits;
|
||||||
using entity_group = std::set<entity_id>;
|
using entity_group = std::set<entity_id>;
|
||||||
|
|
||||||
struct archetype_hash
|
std::unordered_map<archetype_signature, entity_group> archetypes;
|
||||||
{
|
|
||||||
size_t operator()(const archetype_signature& vec) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::unordered_map<archetype_signature, entity_group, archetype_hash>
|
|
||||||
archetypes;
|
|
||||||
|
|
||||||
template<Component T>
|
template<Component T>
|
||||||
static comp_id get_component_id();
|
static comp_id get_component_id();
|
||||||
|
@ -113,17 +113,6 @@ namespace zecsy
|
||||||
static comp_id next_component_id;
|
static comp_id next_component_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline size_t world::archetype_hash::operator()(
|
|
||||||
const world::archetype_signature& vec) const
|
|
||||||
{
|
|
||||||
size_t seed = vec.size();
|
|
||||||
for(const auto& id: vec)
|
|
||||||
{
|
|
||||||
seed ^= id + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
|
||||||
}
|
|
||||||
return seed;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<Component T>
|
template<Component T>
|
||||||
inline world::comp_id world::get_component_id()
|
inline world::comp_id world::get_component_id()
|
||||||
{
|
{
|
||||||
|
@ -135,12 +124,12 @@ namespace zecsy
|
||||||
|
|
||||||
inline size_t world::components_in_entity(entity_id e) const
|
inline size_t world::components_in_entity(entity_id e) const
|
||||||
{
|
{
|
||||||
return entity_to_comps.contains(e) ? entity_to_comps.at(e).size() : 0;
|
return entity_to_comps.contains(e) ? entity_to_comps.at(e).count() : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline size_t world::entity_count() const
|
inline size_t world::entity_count() const
|
||||||
{
|
{
|
||||||
return alive_entities.size();
|
return entity_to_comps.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline size_t world::total_component_count() const
|
inline size_t world::total_component_count() const
|
||||||
|
@ -181,10 +170,9 @@ namespace zecsy
|
||||||
inline entity_id world::make_entity()
|
inline entity_id world::make_entity()
|
||||||
{
|
{
|
||||||
auto id = ++entity_counter;
|
auto id = ++entity_counter;
|
||||||
alive_entities.emplace(id);
|
|
||||||
entity_to_comps[id] = {};
|
entity_to_comps[id] = {};
|
||||||
|
|
||||||
std::vector<comp_id> key;
|
archetype_signature key;
|
||||||
auto& group = archetypes[key];
|
auto& group = archetypes[key];
|
||||||
group.emplace(id);
|
group.emplace(id);
|
||||||
|
|
||||||
|
@ -193,20 +181,19 @@ namespace zecsy
|
||||||
|
|
||||||
inline void world::destroy_entity(entity_id e)
|
inline void world::destroy_entity(entity_id e)
|
||||||
{
|
{
|
||||||
alive_entities.erase(e);
|
auto archetype = entity_to_comps[e];
|
||||||
|
|
||||||
auto& comp_set = entity_to_comps[e];
|
auto& group = archetypes[archetype];
|
||||||
std::vector<comp_id> key(comp_set.begin(), comp_set.end());
|
|
||||||
|
|
||||||
auto& group = archetypes[key];
|
|
||||||
group.erase(e);
|
group.erase(e);
|
||||||
|
|
||||||
if(archetypes[key].empty())
|
if(archetypes[archetype].empty())
|
||||||
{
|
{
|
||||||
archetypes.erase(key);
|
archetypes.erase(archetype);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(comp_id id: comp_set)
|
for(int id = 0; id < ZECSY_MAX_COMPONENTS; ++id)
|
||||||
|
{
|
||||||
|
if(archetype.test(id))
|
||||||
{
|
{
|
||||||
auto& pool = pools[id];
|
auto& pool = pools[id];
|
||||||
auto index = pool.entity_to_index[e];
|
auto index = pool.entity_to_index[e];
|
||||||
|
@ -214,13 +201,14 @@ namespace zecsy
|
||||||
pool.index_to_entity.erase(index);
|
pool.index_to_entity.erase(index);
|
||||||
pool.free_list.emplace_back(index);
|
pool.free_list.emplace_back(index);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
entity_to_comps.erase(e);
|
entity_to_comps.erase(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool world::is_alive(entity_id e) const
|
inline bool world::is_alive(entity_id e) const
|
||||||
{
|
{
|
||||||
return alive_entities.contains(e);
|
return entity_to_comps.contains(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<Component T>
|
template<Component T>
|
||||||
|
@ -228,7 +216,7 @@ namespace zecsy
|
||||||
{
|
{
|
||||||
if(entity_to_comps.contains(e))
|
if(entity_to_comps.contains(e))
|
||||||
{
|
{
|
||||||
return entity_to_comps.at(e).contains(get_component_id<T>());
|
return entity_to_comps.at(e).test(get_component_id<T>());
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -270,25 +258,19 @@ namespace zecsy
|
||||||
pool.entity_to_index[e] = index;
|
pool.entity_to_index[e] = index;
|
||||||
pool.index_to_entity[index] = e;
|
pool.index_to_entity[index] = e;
|
||||||
|
|
||||||
auto& comp_set = entity_to_comps[e];
|
auto& archetype = entity_to_comps[e];
|
||||||
std::set<comp_id> old_set = comp_set;
|
auto old_archetype = archetype;
|
||||||
auto [it, inserted] = comp_set.insert(id);
|
archetype.set(id);
|
||||||
|
|
||||||
if(inserted)
|
auto& group = archetypes[old_archetype];
|
||||||
{
|
|
||||||
std::vector<comp_id> old_key(old_set.begin(), old_set.end());
|
|
||||||
|
|
||||||
auto& group = archetypes[old_key];
|
|
||||||
group.erase(e);
|
group.erase(e);
|
||||||
|
|
||||||
if(archetypes[old_key].empty())
|
if(archetypes[old_archetype].empty())
|
||||||
{
|
{
|
||||||
archetypes.erase(old_key);
|
archetypes.erase(old_archetype);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<comp_id> new_key(comp_set.begin(), comp_set.end());
|
archetypes[archetype].emplace(e);
|
||||||
archetypes[new_key].emplace(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<Component T>
|
template<Component T>
|
||||||
|
@ -300,35 +282,31 @@ namespace zecsy
|
||||||
template<Component T>
|
template<Component T>
|
||||||
inline void world::remove(entity_id e)
|
inline void world::remove(entity_id e)
|
||||||
{
|
{
|
||||||
auto id = get_component_id<T>();
|
if(!has<T>(e))
|
||||||
auto& comp_set = entity_to_comps[e];
|
|
||||||
|
|
||||||
if(comp_set.erase(id) > 0)
|
|
||||||
{
|
{
|
||||||
std::vector<comp_id> old_key(comp_set.begin(), comp_set.end());
|
return;
|
||||||
old_key.push_back(id);
|
}
|
||||||
std::sort(old_key.begin(), old_key.end());
|
|
||||||
|
|
||||||
auto& old_group = archetypes[old_key];
|
auto id = get_component_id<T>();
|
||||||
|
auto& archetype = entity_to_comps[e];
|
||||||
|
|
||||||
|
auto& old_group = archetypes[archetype];
|
||||||
old_group.erase(e);
|
old_group.erase(e);
|
||||||
|
|
||||||
std::vector<comp_id> new_key(comp_set.begin(), comp_set.end());
|
if(old_group.empty())
|
||||||
std::sort(new_key.begin(), new_key.end());
|
|
||||||
|
|
||||||
archetypes[new_key].emplace(e);
|
|
||||||
|
|
||||||
if(archetypes[old_key].empty())
|
|
||||||
{
|
{
|
||||||
archetypes.erase(old_key);
|
archetypes.erase(archetype);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
archetype.reset(id);
|
||||||
|
archetypes[archetype].emplace(e);
|
||||||
|
|
||||||
auto& pool = pools[id];
|
auto& pool = pools[id];
|
||||||
auto index = pool.entity_to_index[e];
|
auto index = pool.entity_to_index[e];
|
||||||
pool.free_list.push_back(index);
|
pool.free_list.push_back(index);
|
||||||
pool.entity_to_index.erase(e);
|
pool.entity_to_index.erase(e);
|
||||||
pool.index_to_entity.erase(index);
|
pool.index_to_entity.erase(index);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
template<Component First, Component Second, Component... Rest>
|
template<Component First, Component Second, Component... Rest>
|
||||||
inline bool world::has(entity_id e) const
|
inline bool world::has(entity_id e) const
|
||||||
|
@ -364,8 +342,11 @@ namespace zecsy
|
||||||
template<Component... T>
|
template<Component... T>
|
||||||
inline void world::query(std::invocable<entity_id, T&...> auto&& system)
|
inline void world::query(std::invocable<entity_id, T&...> auto&& system)
|
||||||
{
|
{
|
||||||
std::vector<comp_id> required = {get_component_id<T>()...};
|
/*std::vector<comp_id> required = {get_component_id<T>()...};*/
|
||||||
std::sort(required.begin(), required.end());
|
|
||||||
|
archetype_signature required;
|
||||||
|
|
||||||
|
(required.set(get_component_id<T>()), ...);
|
||||||
|
|
||||||
query_archetypes_checked = 0;
|
query_archetypes_checked = 0;
|
||||||
query_entities_processed = 0;
|
query_entities_processed = 0;
|
||||||
|
@ -374,18 +355,7 @@ namespace zecsy
|
||||||
{
|
{
|
||||||
query_archetypes_checked++;
|
query_archetypes_checked++;
|
||||||
|
|
||||||
bool match = true;
|
if((archetype_key & required) == required)
|
||||||
for(comp_id req_id: required)
|
|
||||||
{
|
|
||||||
if(!std::binary_search(archetype_key.begin(),
|
|
||||||
archetype_key.end(), req_id))
|
|
||||||
{
|
|
||||||
match = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(match)
|
|
||||||
{
|
{
|
||||||
query_entities_processed += entities.size();
|
query_entities_processed += entities.size();
|
||||||
for(entity_id e: entities)
|
for(entity_id e: entities)
|
||||||
|
|
Loading…
Reference in a new issue