Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 42 additions & 11 deletions hitagi/asset/transform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,21 @@ void RelationShipSystem::OnUpdate(ecs::Schedule& schedule) {
if (relation_ship.prev_parent) {
auto& prev_parent = relation_ship.prev_parent.Get<RelationShip>();
prev_parent.children.erase(entity);
prev_parent.subtree_dirty = true;
for (auto parent = prev_parent.parent; parent; parent = parent.Get<RelationShip>().parent) {
parent.Get<RelationShip>().subtree_dirty = true;
}
}
if (relation_ship.parent) {
auto& parent = relation_ship.parent.Get<RelationShip>();
parent.children.insert(entity);
parent.subtree_dirty = true;
for (auto ancestor = parent.parent; ancestor; ancestor = ancestor.Get<RelationShip>().parent) {
ancestor.Get<RelationShip>().subtree_dirty = true;
}
}
relation_ship.prev_parent = relation_ship.parent;
relation_ship.subtree_dirty = true;
}
});
}
Expand All @@ -29,25 +38,47 @@ void TransformSystem::OnUpdate(ecs::Schedule& schedule) {
schedule
.Request(
"update_local_matrix",
[](Transform& transform) {
transform.world_matrix = transform.ToMatrix();
[](const ecs::Entity entity, Transform& transform, RelationShip& relation_ship) {
if (transform.position == transform.cached_position &&
transform.rotation == transform.cached_rotation &&
transform.scaling == transform.cached_scaling) {
return;
}

transform.local_matrix = transform.ToMatrix();
transform.cached_position = transform.position;
transform.cached_rotation = transform.rotation;
transform.cached_scaling = transform.scaling;

relation_ship.subtree_dirty = true;
for (auto parent = relation_ship.parent; parent; parent = parent.Get<RelationShip>().parent) {
parent.Get<RelationShip>().subtree_dirty = true;
}
})
.Request(
"update_world_matrix_from_root",
[](const Transform& transform, const RelationShip& relation_ship) {
const std::function<void(ecs::Entity, const math::mat4f)> recursive_update =
[&](ecs::Entity entity, const math::mat4f parent_transform) {
auto& transform = entity.Get<Transform>();
transform.world_matrix = parent_transform * transform.world_matrix;
for (auto child : entity.Get<RelationShip>().GetChildren()) {
recursive_update(child, transform.world_matrix);
[](Transform& transform, RelationShip& relation_ship) {
const std::function<void(ecs::Entity, const math::mat4f, bool)> recursive_update =
[&](ecs::Entity entity, const math::mat4f parent_transform, bool parent_dirty) {
auto& child_relation = entity.Get<RelationShip>();
if (!parent_dirty && !child_relation.subtree_dirty) return;

auto& child_transform = entity.Get<Transform>();
child_transform.world_matrix = parent_transform * child_transform.local_matrix;
const bool subtree_dirty = parent_dirty || child_relation.subtree_dirty;
child_relation.subtree_dirty = false;

for (auto child : child_relation.GetChildren()) {
recursive_update(child, child_transform.world_matrix, subtree_dirty);
}
};

// root entity
if (!relation_ship.parent) {
if (!relation_ship.parent && relation_ship.subtree_dirty) {
transform.world_matrix = transform.local_matrix;
relation_ship.subtree_dirty = false;
for (auto entity : relation_ship.GetChildren()) {
recursive_update(entity, transform.world_matrix);
recursive_update(entity, transform.world_matrix, true);
}
}
});
Expand Down
18 changes: 16 additions & 2 deletions hitagi/asset/transform.cppm
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@ struct RelationShip {

private:
friend struct RelationShipSystem;
friend struct TransformSystem;
ecs::Entity prev_parent = {};
std::pmr::unordered_set<ecs::Entity> children;
bool subtree_dirty = true;
};
static_assert(ecs::Component<RelationShip>);

Expand All @@ -29,19 +31,31 @@ struct Transform {
Transform(math::vec3f position = math::vec3f(0.0f), math::quatf rotation = math::quatf::identity(), math::vec3f scaling = math::vec3f(1.0f))
: position(position),
rotation(rotation),
scaling(scaling) {}
scaling(scaling),
local_matrix(math::translate(position) * math::rotate(rotation) * math::scale(scaling)),
world_matrix(local_matrix),
cached_position(position),
cached_rotation(rotation),
cached_scaling(scaling) {}

math::vec3f position;
math::quatf rotation;
math::vec3f scaling;

math::mat4f world_matrix = math::mat4f::identity();
math::mat4f local_matrix;
math::mat4f world_matrix;

inline void ApplyScale(float value) noexcept { scaling = value; }
inline void Translate(const math::vec3f& value) noexcept { position += value; }
inline void Rotate(const math::quatf& value) noexcept { rotation = value * rotation; }

inline auto ToMatrix() const noexcept { return math::translate(position) * math::rotate(rotation) * math::scale(scaling); }

private:
friend struct TransformSystem;
math::vec3f cached_position;
math::quatf cached_rotation;
math::vec3f cached_scaling;
};

struct TransformSystem {
Expand Down
32 changes: 22 additions & 10 deletions hitagi/ecs/schedule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,23 +27,36 @@ void Schedule::Request(std::shared_ptr<TaskBase>&& task, const ParameterSets& pa
for (auto parameter : read_after_write) {
m_ReadAfterWriteSet[parameter].emplace_back(m_Tasks.size() - 1);
}

m_TaskflowDirty = true;
}

void Schedule::SetOrder(std::string_view first_task, std::string_view second_task) {
m_CustomOrder.emplace(first_task, second_task);
m_TaskflowDirty = true;
}

void Schedule::Run(tf::Executor& executor) {
tf::Taskflow taskflow;
std::pmr::vector<tf::Task> tasks;
if (m_TaskflowDirty) {
BuildTaskflow(executor);
}

ZoneScopedN("ECSFrame");
executor.run(m_Taskflow).wait();
}

void Schedule::BuildTaskflow(tf::Executor& executor) {
m_Taskflow.clear();
m_TaskflowTasks.clear();
m_TaskflowTasks.reserve(m_Tasks.size());

// adjacency list
std::pmr::unordered_map<std::size_t, std::pmr::unordered_set<std::size_t>> direct_graph;
for (auto i = 0; i < m_Tasks.size(); ++i)
direct_graph[i] = {};

for (const auto& task : m_Tasks) {
tasks.emplace_back(taskflow.emplace([this, &executor, task]() {
m_TaskflowTasks.emplace_back(m_Taskflow.emplace([this, &executor, task]() {
thread_local bool tracy_thread_named = false;
if (!tracy_thread_named) {
if (const auto worker_id = executor.this_worker_id(); worker_id >= 0) {
Expand All @@ -62,27 +75,27 @@ void Schedule::Run(tf::Executor& executor) {
for (const auto& [component, task_indices] : m_ReadBeforeWriteSet) {
for (const auto task_index : task_indices) {
for (const auto write_task_index : m_WriteSet[component]) {
tasks[task_index].precede(tasks[write_task_index]);
m_TaskflowTasks[task_index].precede(m_TaskflowTasks[write_task_index]);
direct_graph[task_index].emplace(write_task_index);
}
}
for (const auto& task_index : task_indices) {
for (const auto read_after_write_task_index : m_ReadAfterWriteSet[component]) {
tasks[task_index].precede(tasks[read_after_write_task_index]);
m_TaskflowTasks[task_index].precede(m_TaskflowTasks[read_after_write_task_index]);
direct_graph[task_index].emplace(read_after_write_task_index);
}
}
}

for (const auto& [component, task_indices] : m_WriteSet) {
for (const auto [task_index, next_task_index] : std::ranges::views::zip(task_indices, task_indices | std::ranges::views::drop(1))) {
tasks[task_index].precede(tasks[next_task_index]);
m_TaskflowTasks[task_index].precede(m_TaskflowTasks[next_task_index]);
direct_graph[task_index].emplace(next_task_index);
}

for (const auto task_index : task_indices) {
for (const auto read_after_write_task_index : m_ReadAfterWriteSet[component]) {
tasks[task_index].precede(tasks[read_after_write_task_index]);
m_TaskflowTasks[task_index].precede(m_TaskflowTasks[read_after_write_task_index]);
direct_graph[task_index].emplace(read_after_write_task_index);
}
}
Expand All @@ -103,16 +116,15 @@ void Schedule::Run(tf::Executor& executor) {
}
const auto first_task_index = m_TaskNameToIndex[first_task_name];
const auto second_task_index = m_TaskNameToIndex[second_task_name];
tasks[first_task_index].precede(tasks[second_task_index]);
m_TaskflowTasks[first_task_index].precede(m_TaskflowTasks[second_task_index]);
direct_graph[first_task_index].emplace(second_task_index);
}

if (!CheckValid(direct_graph)) {
return;
}

ZoneScopedN("ECSFrame");
executor.run(taskflow).wait();
m_TaskflowDirty = false;
}

bool Schedule::CheckValid(const std::pmr::unordered_map<std::size_t, std::pmr::unordered_set<std::size_t>>& graph) {
Expand Down
5 changes: 5 additions & 0 deletions hitagi/ecs/schedule.cppm
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ private:
void Request(std::shared_ptr<TaskBase>&& task, const ParameterSets& parameter_sets);

void Run(tf::Executor& executor);
void BuildTaskflow(tf::Executor& executor);

bool CheckValid(const std::pmr::unordered_map<std::size_t, std::pmr::unordered_set<std::size_t>>& graph);

Expand All @@ -70,6 +71,10 @@ private:
std::pmr::unordered_map<utils::TypeID, std::pmr::vector<std::size_t>> m_ReadAfterWriteSet;

std::pmr::unordered_map<std::pmr::string, std::pmr::string> m_CustomOrder;

tf::Taskflow m_Taskflow;
std::pmr::vector<tf::Task> m_TaskflowTasks;
bool m_TaskflowDirty = true;
};

} // namespace hitagi::ecs
Expand Down
3 changes: 3 additions & 0 deletions hitagi/ecs/system_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ void SystemManager::EnableOne(utils::TypeID id) {

m_DisabledSystems.erase(id);
m_EnabledSystems.emplace(id);
m_World.InvalidateSchedule();

if (m_OnEnableFns.contains(id))
m_OnEnableFns.at(id)(m_World);
Expand All @@ -47,6 +48,7 @@ void SystemManager::DisableOne(utils::TypeID id) {

m_EnabledSystems.erase(id);
m_DisabledSystems.emplace(id);
m_World.InvalidateSchedule();

if (m_OnDisableFns.contains(id))
m_OnDisableFns.at(id)(m_World);
Expand All @@ -55,6 +57,7 @@ void SystemManager::DisableOne(utils::TypeID id) {
void SystemManager::UnRegisterOne(utils::TypeID id) {
DisableOne(id);
m_DisabledSystems.erase(id);
m_World.InvalidateSchedule();

if (m_OnDestroyFns.contains(id))
m_OnDestroyFns.at(id)(m_World);
Expand Down
17 changes: 14 additions & 3 deletions hitagi/ecs/world.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,22 @@ World::World(std::string_view name)
m_SystemManager(*this) {
}

World::~World() = default;

void World::Update() {
ZoneScopedN("ECS::World::Update");
Schedule schedule(*this);
m_SystemManager.Update(schedule);
schedule.Run(m_Executor);
if (m_ScheduleDirty || !m_Schedule) {
m_Schedule = std::make_unique<Schedule>(*this);
m_SystemManager.Update(*m_Schedule);
m_ScheduleDirty = false;
}

m_Schedule->Run(m_Executor);
}

void World::InvalidateSchedule() noexcept {
m_ScheduleDirty = true;
m_Schedule.reset();
}

} // namespace hitagi::ecs
9 changes: 9 additions & 0 deletions hitagi/ecs/world.cppm
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@ import :system_manager;

export namespace hitagi::ecs {

class Schedule;

class World {
public:
World(std::string_view name);
~World();

void Update();

Expand All @@ -24,12 +27,18 @@ public:
inline auto GetLogger() noexcept { return m_Logger; }

private:
friend SystemManager;

void InvalidateSchedule() noexcept;

std::pmr::string m_Name;
std::shared_ptr<spdlog::logger> m_Logger;

EntityManager m_EntityManager;
SystemManager m_SystemManager;
tf::Executor m_Executor;
std::unique_ptr<Schedule> m_Schedule;
bool m_ScheduleDirty = true;
};

} // namespace hitagi::ecs
5 changes: 2 additions & 3 deletions hitagi/gfx/render_graph/pass_builder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ PassBuilder::~PassBuilder() {
}

auto PassBuilder::Finish() -> std::size_t {
pass_base->m_Handle = m_RenderGraph.m_Nodes.size();
m_RenderGraph.m_Nodes.emplace_back(pass_base);
pass_base->m_Handle = m_RenderGraph.AllocateNode(pass_base);

// create edge for move resource:
// resource_1 -- move --> resource_2(*) resource_1 -- new_edge --> pass_2
Expand Down Expand Up @@ -789,4 +788,4 @@ void PresentPassBuilder::Finish() noexcept {
m_RenderGraph.m_PresentPassNode = pass;
}

} // namespace hitagi::rg
} // namespace hitagi::rg
Loading