Skip to content

Commit

Permalink
Added: Accessing iterator data via query item index
Browse files Browse the repository at this point in the history
  • Loading branch information
richardbiely committed Mar 4, 2024
1 parent 9364cf7 commit 8d35525
Show file tree
Hide file tree
Showing 9 changed files with 1,024 additions and 616 deletions.
22 changes: 17 additions & 5 deletions include/gaia/cnt/bitset.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,18 @@ namespace gaia {
using type = std::conditional_t<Use32Bit, uint32_t, uint64_t>;
};

public:
static constexpr uint32_t BitsPerItem = (NBits / 64) > 0 ? 64 : 32;
static constexpr uint32_t Items = (NBits + BitsPerItem - 1) / BitsPerItem;

using size_type = typename size_type_selector<BitsPerItem == 32>::type;

private:
static constexpr bool HasTrailingBits = (NBits % BitsPerItem) != 0;
static constexpr size_type LastItemMask = ((size_type)1 << (NBits % BitsPerItem)) - 1;

size_type m_data[Items]{};

//! Returns the number of words used by the bitset internally
GAIA_NODISCARD constexpr uint32_t items() const {
return Items;
}

//! Returns the word stored at the index \param wordIdx
size_type data(uint32_t wordIdx) const {
return m_data[wordIdx];
Expand All @@ -45,6 +44,19 @@ namespace gaia {
using const_iterator_inverse = bitset_const_iterator<bitset<NBits>, true>;
friend const_iterator_inverse;

size_type* data() {
return &m_data[0];
}

const size_type* data() const {
return &m_data[0];
}

//! Returns the number of words used by the bitset internally
GAIA_NODISCARD constexpr uint32_t items() const {
return Items;
}

const_iterator begin() const {
return const_iterator(*this, 0, true);
}
Expand Down
22 changes: 22 additions & 0 deletions include/gaia/ecs/chunk.h
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,12 @@ namespace gaia {
return view<T>(0, size());
}

template <typename T>
GAIA_NODISCARD decltype(auto) view(void* ptr) const {
using U = typename actual_type_t<T>::Type;
return mem::auto_view_policy_get<U>{std::span{(const uint8_t*)ptr, size()}};
}

/*!
Returns a mutable entity or component view.
\warning If \tparam T is a component it is expected it is present. Undefined behavior otherwise.
Expand All @@ -453,6 +459,14 @@ namespace gaia {
return view_mut<TT>(0, size());
}

template <typename T>
GAIA_NODISCARD decltype(auto) view_mut(void* ptr) const {
using U = typename actual_type_t<T>::Type;
static_assert(!std::is_same_v<U, Entity>, "Modifying chunk entities via view_mut is forbidden");

return mem::auto_view_policy_set<U>{std::span{(uint8_t*)ptr, size()}};
}

/*!
Returns a mutable component view.
Doesn't update the world version when the access is aquired.
Expand All @@ -470,6 +484,14 @@ namespace gaia {
return mem::auto_view_policy_set<U>{view_mut_inter<T, false>(from, to)};
}

template <typename T>
GAIA_NODISCARD decltype(auto) sview_mut(void* ptr) const {
using U = typename actual_type_t<T>::Type;
static_assert(!std::is_same_v<U, Entity>, "Modifying chunk entities via view_mut is forbidden");

return mem::auto_view_policy_set<U>{std::span{(uint8_t*)ptr, size()}};
}

template <typename T>
GAIA_NODISCARD decltype(auto) sview_mut() {
return sview_mut<T>(0, size());
Expand Down
49 changes: 49 additions & 0 deletions include/gaia/ecs/chunk_iterator.h
Original file line number Diff line number Diff line change
@@ -1,29 +1,57 @@
#pragma once
#include "../config/config.h"

#include <cinttypes>
#include <cstdint>
#include <type_traits>

#include "../core/bit_utils.h"
#include "../core/iterator.h"
#include "archetype.h"
#include "chunk.h"
#include "component.h"
#include "component_cache_item.h"
#include "query_common.h"

namespace gaia {
namespace ecs {
class World;

template <typename T>
const ComponentCacheItem& comp_cache_add(World& world);

//! QueryImpl constraints
enum class Constraints : uint8_t { EnabledOnly, DisabledOnly, AcceptAll };

namespace detail {
template <Constraints IterConstraint>
class ChunkIterImpl {
protected:
using CompIndicesBitView = core::bit_view<Chunk::MAX_COMPONENTS_BITS>;

Archetype* m_pArchetype = nullptr;
Chunk* m_pChunk = nullptr;

//! uint8_t m_compIdxMapping[MAX_ITEMS_IN_QUERY] compressed.
CompIndicesBitView m_compIdxMapping;

public:
ChunkIterImpl() = default;
~ChunkIterImpl() = default;
ChunkIterImpl(ChunkIterImpl&&) noexcept = default;
ChunkIterImpl& operator=(ChunkIterImpl&&) noexcept = default;
ChunkIterImpl(const ChunkIterImpl&) = delete;
ChunkIterImpl& operator=(const ChunkIterImpl&) = delete;

void set_remapping_indices(CompIndicesBitView compIndicesMapping) {
m_compIdxMapping = compIndicesMapping;
}

void set_archetype(Archetype* pArchetype) {
GAIA_ASSERT(pArchetype != nullptr);
m_pArchetype = pArchetype;
}

void set_chunk(Chunk* pChunk) {
GAIA_ASSERT(pChunk != nullptr);
m_pChunk = pChunk;
Expand All @@ -38,6 +66,13 @@ namespace gaia {
return m_pChunk->view<T>(from(), to());
}

template <typename T>
GAIA_NODISCARD auto view(uint32_t termIdx) {
const auto compIdx = m_compIdxMapping.get(termIdx);
const auto dataOffset = m_pArchetype->comp_offs()[compIdx];
return m_pChunk->view_mut<T>((void*)&m_pChunk->data(dataOffset));
}

//! Returns a mutable entity or component view.
//! \warning If \tparam T is a component it is expected it is present. Undefined behavior otherwise.
//! \tparam T Component or Entity
Expand All @@ -47,6 +82,13 @@ namespace gaia {
return m_pChunk->view_mut<T>(from(), to());
}

template <typename T>
GAIA_NODISCARD auto view_mut(uint32_t termIdx) {
const auto compIdx = m_compIdxMapping.get(termIdx);
const auto dataOffset = m_pArchetype->comp_offs()[compIdx];
return m_pChunk->view_mut<T>((void*)&m_pChunk->data(dataOffset));
}

//! Returns a mutable component view.
//! Doesn't update the world version when the access is aquired.
//! \warning It is expected the component \tparam T is present. Undefined behavior otherwise.
Expand All @@ -57,6 +99,13 @@ namespace gaia {
return m_pChunk->sview_mut<T>(from(), to());
}

template <typename T>
GAIA_NODISCARD auto sview_mut(uint32_t termIdx) {
const auto compIdx = m_compIdxMapping.get(termIdx);
const auto dataOffset = m_pArchetype->comp_offs()[compIdx];
return m_pChunk->view_mut<T>((void*)&m_pChunk->data(dataOffset));
}

//! Returns either a mutable or immutable entity/component view based on the requested type.
//! Value and const types are considered immutable. Anything else is mutable.
//! \warning If \tparam T is a component it is expected to be present. Undefined behavior otherwise.
Expand Down
12 changes: 10 additions & 2 deletions include/gaia/ecs/query.h
Original file line number Diff line number Diff line change
Expand Up @@ -439,14 +439,20 @@ namespace gaia {
ChunkBatchedList chunkBatch;
TIter it;

uint32_t aid = 0;
for (auto* pArchetype: queryInfo) {
if GAIA_UNLIKELY (!can_process_archetype(*pArchetype))
if GAIA_UNLIKELY (!can_process_archetype(*pArchetype)) {
++aid;
continue;
}

GAIA_PROF_SCOPE(query::run_query); // batch preparation + chunk processing

it.set_remapping_indices(queryInfo.indices_mapping(aid));
it.set_archetype(pArchetype);

const auto& chunks = pArchetype->chunks();

uint32_t chunkOffset = 0;
uint32_t itemsLeft = chunks.size();
while (itemsLeft > 0) {
Expand All @@ -473,6 +479,8 @@ namespace gaia {
itemsLeft -= batchSize;
chunkOffset += batchSize;
}

++aid;
}

// Take care of any leftovers not processed during run_query
Expand Down
16 changes: 15 additions & 1 deletion include/gaia/ecs/query_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "../cnt/sarray_ext.h"
#include "../core/bit_utils.h"
#include "../core/hashing_policy.h"
#include "../core/utility.h"
#include "component.h"
#include "component_utils.h"
#include "id.h"
Expand Down Expand Up @@ -172,20 +173,33 @@ namespace gaia {
inline void sort(QueryCtx& ctx) {
auto& data = ctx.data;

auto remappingCopy = data.remapping;

// Sort data. Necessary for correct hash calculation.
// Without sorting query.all<XXX, YYY> would be different than query.all<YYY, XXX>.
// Also makes sure data is in optimal order for query processing.
core::sort(data.pairs, query_sort_cond{}, [&](uint32_t left, uint32_t right) {
core::swap(data.ids[left], data.ids[right]);
core::swap(data.pairs[left], data.pairs[right]);
core::swap(data.remapping[left], data.remapping[right]);
core::swap(remappingCopy[left], remappingCopy[right]);

// Make sure masks remains correct after sorting
core::swap_bits(data.readWriteMask, left, right);
core::swap_bits(data.as_mask, left, right);
core::swap_bits(data.as_mask_2, left, right);
});

// Update remapping indices.
// E.g., let us have ids 0, 14, 15, with indices 0, 1, 2.
// After sorting they become 14, 15, 0, with indices 1, 2, 0.
// So indices mapping is as follows: 0 -> 1, 1 -> 2, 2 -> 0.
// After remapping update, indices become 0 -> 2, 1 -> 0, 2 -> 1.
// Therefore, if we want to see where 15 was located originaly (curr index 1), we do look at index 2 and get 1.
GAIA_EACH(data.pairs) {
const auto idxBeforeRemapping = (uint8_t)core::get_index_unsafe(remappingCopy, (uint8_t)i);
data.remapping[i] = idxBeforeRemapping;
}

auto& pairs = data.pairs;
if (!pairs.empty()) {
uint32_t i = 0;
Expand Down
48 changes: 45 additions & 3 deletions include/gaia/ecs/query_info.h
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
#pragma once
#include "../config/config.h"

#include "../cnt/bitset.h"
#include "../cnt/darray.h"
#include "../cnt/sarray_ext.h"
#include "../cnt/set.h"
#include "../config/profiler.h"
#include "../core/bit_utils.h"
#include "../core/hashing_policy.h"
#include "../core/utility.h"
#include "archetype.h"
Expand All @@ -21,6 +23,8 @@ namespace gaia {
class World;

using EntityToArchetypeMap = cnt::map<EntityLookupKey, ArchetypeList>;
using CompIndicesBitView = core::bit_view<Chunk::MAX_COMPONENTS_BITS>;
using CompIndicesBitSet = cnt::bitset<Chunk::MAX_COMPONENTS_BITS * MAX_ITEMS_IN_QUERY>;

Archetype* archetype_from_entity(const World& world, Entity entity);
bool is(const World& world, Entity entity, Entity baseEntity);
Expand All @@ -39,6 +43,7 @@ namespace gaia {
QueryCtx m_lookupCtx;
//! List of archetypes matching the query
ArchetypeList m_archetypeCache;
cnt::darray<CompIndicesBitSet> m_compIndiciesMappings;
//! Id of the last archetype in the world we checked
ArchetypeId m_lastArchetypeId{};
//! Version of the world for which the query has been called most recently
Expand Down Expand Up @@ -812,16 +817,46 @@ namespace gaia {
if (match_one(*pArchetype, std::span{ids_none.data(), ids_none.size()}))
continue;

m_archetypeCache.push_back(pArchetype);
add_archetype_to_cache(pArchetype);
}
}
} else {
// Write the temporary matches to cache
for (auto* pArchetype: s_tmpArchetypeMatchesArr)
m_archetypeCache.push_back(pArchetype);
add_archetype_to_cache(pArchetype);
}
}

void add_archetype_to_cache(Archetype* pArchetype) {
// Add archetype to cache
m_archetypeCache.push_back(pArchetype);

// Update id mappings
CompIndicesBitSet compIndicesStorage;
constexpr auto CompIndicesBitSetBytes = CompIndicesBitSet::Items * sizeof(CompIndicesBitSet::size_type);
CompIndicesBitView bv{{(uint8_t*)compIndicesStorage.data(), CompIndicesBitSetBytes}};
GAIA_EACH(ids()) {
// We add 1 from the given index because there is a hidden .add<Core>(no) for each query.
// Doing it here is faster than doing it in ChunkIterImpl::view.
uint32_t termIdx = (i + 1);
if (termIdx >= ids().size())
termIdx = 0;

auto compIdx = bv.get(termIdx);
if (compIdx == (uint8_t)-1) {
const auto idxBeforeRemapping = m_lookupCtx.data.remapping[termIdx];
compIdx = (uint8_t)core::get_index_unsafe(pArchetype->ids(), ids()[idxBeforeRemapping]);
bv.set((uint8_t)termIdx, compIdx);
}
}
m_compIndiciesMappings.push_back(compIndicesStorage);
}

void del_archetype_from_cache(uint32_t idx) {
core::erase_fast(m_archetypeCache, idx);
core::erase_fast(m_compIndiciesMappings, idx);
}

GAIA_NODISCARD QueryId id() const {
return m_lookupCtx.queryId;
}
Expand Down Expand Up @@ -865,7 +900,8 @@ namespace gaia {
const auto idx = core::get_index(m_archetypeCache, pArchetype);
if (idx == BadIndex)
return;
core::erase_fast(m_archetypeCache, idx);

del_archetype_from_cache(idx);

// An archetype was removed from the world so the last matching archetype index needs to be
// lowered by one for every component context.
Expand All @@ -880,6 +916,12 @@ namespace gaia {
clearMatches(m_lookupCtx.data.lastMatchedArchetypeIdx_Any);
}

CompIndicesBitView indices_mapping(uint32_t idx) const {
const auto& compIndicesStorage = m_compIndiciesMappings[idx];
constexpr auto CompIndicesBitSetBytes = CompIndicesBitSet::Items * sizeof(CompIndicesBitSet::size_type);
return {{(uint8_t*)compIndicesStorage.data(), CompIndicesBitSetBytes}};
}

GAIA_NODISCARD ArchetypeList::iterator begin() {
return m_archetypeCache.begin();
}
Expand Down
Loading

0 comments on commit 8d35525

Please sign in to comment.