Skip to content

Commit

Permalink
gh-36: rework query system for scene ecs updates
Browse files Browse the repository at this point in the history
  • Loading branch information
EgorOrachyov committed Aug 3, 2024
1 parent bfef111 commit c1fa453
Show file tree
Hide file tree
Showing 34 changed files with 240 additions and 2,179 deletions.
26 changes: 0 additions & 26 deletions engine/runtime/ecs/ecs_core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,30 +60,4 @@ namespace wmoge {
return stream.str();
}

std::string EcsQuery::to_string() const {
EcsRegistry* registry = IocContainer::iresolve_v<EcsRegistry>();

if (!read.any() && !write.any()) {
return "'empty'";
}

std::stringstream stream;

const auto affected = read | write;
const int total_affected = int(affected.count());

stream << "<";
stream << "count=" << total_affected << ":";

for (int i = 0; i < EcsLimits::MAX_COMPONENTS; i++) {
if (affected.test(i)) {
const Strid& name = registry->get_component_info(i).name;
stream << (write.test(i) ? "rw-" : "r-") << name << ",";
}
}

stream << ">";
return stream.str();
}

}// namespace wmoge
45 changes: 0 additions & 45 deletions engine/runtime/ecs/ecs_core.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,51 +98,6 @@ namespace wmoge {
return stream;
}

/**
* @class EcsQuery
* @brief Describes read-write dependencies of an system query for processing
*/
struct EcsQuery {
std::bitset<EcsLimits::MAX_COMPONENTS> read;
std::bitset<EcsLimits::MAX_COMPONENTS> write;

EcsQuery() = default;

template<typename Component>
EcsQuery& set_read() {
read.set(Component::IDX);
return *this;
}

template<typename Component>
EcsQuery& set_write() {
read.set(Component::IDX);
write.set(Component::IDX);
return *this;
}

template<typename Component>
[[nodiscard]] bool has_read() const {
return read.test(Component::IDX);
}

template<typename Component>
[[nodiscard]] bool has_write() const {
return write.test(Component::IDX);
}

[[nodiscard]] std::bitset<EcsLimits::MAX_COMPONENTS> affected() const {
return read | write;
}

[[nodiscard]] std::string to_string() const;
};

inline std::ostream& operator<<(std::ostream& stream, const EcsQuery& query) {
stream << query.to_string();
return stream;
}

}// namespace wmoge

namespace std {
Expand Down
5 changes: 3 additions & 2 deletions engine/runtime/ecs/ecs_memory.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,9 @@ namespace wmoge {
[[nodiscard]] void* get_component(int entity_idx, int component_idx) const;
[[nodiscard]] EcsEntity get_entity(int entity_idx) const;

[[nodiscard]] int get_size() const { return m_size; }
[[nodiscard]] int get_capacity() const { return m_capacity; }
[[nodiscard]] int get_size() const { return m_size; }
[[nodiscard]] int get_capacity() const { return m_capacity; }
[[nodiscard]] const EcsArch& get_arch() const { return m_arch; }

private:
std::array<EcsPool, EcsLimits::MAX_COMPONENTS + 1> m_pool{};
Expand Down
141 changes: 141 additions & 0 deletions engine/runtime/ecs/ecs_query.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/**********************************************************************************/
/* Wmoge game engine */
/* Available at github https://github.com/EgorOrachyov/wmoge */
/**********************************************************************************/
/* MIT License */
/* */
/* Copyright (c) 2023 Egor Orachyov */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining a copy */
/* of this software and associated documentation files (the "Software"), to deal */
/* in the Software without restriction, including without limitation the rights */
/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */
/* copies of the Software, and to permit persons to whom the Software is */
/* furnished to do so, subject to the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be included in all */
/* copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */
/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */
/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */
/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */
/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */
/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */
/* SOFTWARE. */
/**********************************************************************************/

#pragma once

#include "core/array_view.hpp"
#include "core/string_id.hpp"
#include "ecs/ecs_core.hpp"
#include "ecs/ecs_entity.hpp"
#include "ecs/ecs_memory.hpp"

namespace wmoge {

/** @brief Component access and modification type in query */
enum class EcsComponentAccess {
ReadOnly = 0,
ReadWrite
};

/** @brief Describes presence of components in query */
enum class EcsComponentPresence {
Required = 0,
Optional,
Exclude
};

/**
* @class EcsQuery
* @brief Configures query to iterate over specific type of entities
*/
struct EcsQuery {
using Bitset = std::bitset<EcsLimits::MAX_COMPONENTS>;

Bitset read_only;
Bitset read_write;
Bitset required;
Bitset optional;
Bitset exclude;
Strid name;

EcsQuery() = default;

template<typename Component>
EcsQuery& add(EcsComponentPresence presence = EcsComponentPresence::Required, EcsComponentAccess access = EcsComponentAccess::ReadOnly) {
if (presence == EcsComponentPresence::Exclude) {
exclude.set(Component::IDX);
return;
}
if (presence == EcsComponentPresence::Required) {
required.set(Component::IDX);
}
if (presence == EcsComponentPresence::Optional) {
optional.set(Component::IDX);
}
if (access == EcsComponentAccess::ReadOnly) {
read_only.set(Component::IDX);
}
if (access == EcsComponentAccess::ReadWrite) {
read_write.set(Component::IDX);
}
return *this;
}

bool match(const EcsArch& arch) const {
if ((required & arch) != required) {
return false;
}
if ((exclude & arch).any()) {
return false;
}
return true;
}

[[nodiscard]] std::string to_string() const { return ""; }
};

inline std::ostream& operator<<(std::ostream& stream, const EcsQuery& query) {
stream << query.to_string();
return stream;
}

/**
* @class EcsQueryContext
* @brief Context passed to execute function when query is executed
*/
class EcsQueryContext {
public:
EcsQueryContext(EcsArchStorage& storage, EcsQuery query, int start, int count)
: m_storage(storage),
m_arch(storage.get_arch()),
m_query(query),
m_range_start(start),
m_range_count(count) {
}

template<typename Component>
bool has() { return m_arch.has_component<Component>(); }

template<typename Component>
Component& get_component(int entity_idx) { return *m_storage.get_component<Component>(entity_idx); }
EcsEntity get_entity(int entity_idx) { return m_storage.get_entity(entity_idx); }

[[nodiscard]] int get_start_idx() const { return m_range_start; }
[[nodiscard]] int get_count() const { return m_range_count; }

private:
EcsArchStorage& m_storage;
EcsQuery m_query;
EcsArch m_arch;
int m_range_start = -1;
int m_range_count = 0;
};

/** @brief Function used to execute ecs query */
using EcsQueuryFunction = std::function<void(EcsQueryContext& context)>;

}// namespace wmoge
27 changes: 7 additions & 20 deletions engine/runtime/ecs/ecs_registry.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
#include "ecs/ecs_component.hpp"
#include "ecs/ecs_core.hpp"
#include "ecs/ecs_entity.hpp"
#include "ecs/ecs_system.hpp"
#include "memory/mem_pool.hpp"

#include <array>
Expand All @@ -54,29 +53,24 @@ namespace wmoge {
EcsRegistry(EcsRegistry&&) = delete;
~EcsRegistry() = default;

[[nodiscard]] int get_component_idx(const Strid& name);
[[nodiscard]] const EcsComponentInfo& get_component_info(const Strid& name);
[[nodiscard]] const EcsComponentInfo& get_component_info(int idx);
[[nodiscard]] MemPool& get_component_pool(int idx);
[[nodiscard]] MemPool& get_entity_pool();
[[nodiscard]] int get_chunk_size() const { return m_chunk_size; }
[[nodiscard]] int get_expand_size() const { return m_expand_size; }
[[nodiscard]] array_view<const EcsSystemPtr> get_systems() const { return m_systems; }
[[nodiscard]] int get_component_idx(const Strid& name);
[[nodiscard]] const EcsComponentInfo& get_component_info(const Strid& name);
[[nodiscard]] const EcsComponentInfo& get_component_info(int idx);
[[nodiscard]] MemPool& get_component_pool(int idx);
[[nodiscard]] MemPool& get_entity_pool();
[[nodiscard]] int get_chunk_size() const { return m_chunk_size; }
[[nodiscard]] int get_expand_size() const { return m_expand_size; }

template<typename Component>
void register_component();

template<typename System>
std::shared_ptr<System> register_system();

private:
static const int MAX_CMP = EcsLimits::MAX_COMPONENTS;

std::array<EcsComponentInfo, MAX_CMP> m_components_info; // type info of component, indexed by component id
std::array<std::unique_ptr<MemPool>, MAX_CMP> m_components_pool; // pools to allocate components chunks
flat_map<Strid, int> m_components_name_to_idx;// resolve component name to its idx
std::unique_ptr<MemPool> m_entity_pool; // pool to allocate entities chunks
std::vector<EcsSystemPtr> m_systems; // registered global systems

int m_chunk_size = 16;// num of components of a single type sequentially allocate in one chunk
int m_expand_size = 2; // num of chunks in a single pooled allocation in head
Expand Down Expand Up @@ -117,11 +111,4 @@ namespace wmoge {
m_components_pool[component_info.idx] = std::make_unique<MemPool>(component_info.size * m_chunk_size, m_expand_size);
}

template<typename System>
inline std::shared_ptr<System> EcsRegistry::register_system() {
auto sys = std::make_shared<System>();
m_systems.emplace_back(sys);
return sys;
}

}// namespace wmoge
Loading

0 comments on commit c1fa453

Please sign in to comment.