Helios Engine
A modular ECS based data-oriented C++23 game engine framework
Loading...
Searching...
No Matches
multi_threaded.cpp
Go to the documentation of this file.
1#include <pch.hpp>
2
4
5#include <helios/assert.hpp>
6#include <helios/ecs/details/profile.hpp>
9
10#include <cstddef>
11#include <cstdint>
12
13namespace helios::ecs {
14
15void MultiThreadedExecutor::BuildGraph(Schedule& schedule, World& world) {
16 HELIOS_ECS_PROFILE_SCOPE_N("helios::ecs::MultiThreadedExecutor::Execute");
17
18 HELIOS_ASSERT(schedule.plan_.has_value(),
19 "MultiThreadedExecutor: schedule has no compiled plan!");
20
21 const auto& plan = *schedule.plan_;
22 const auto& execution_order = plan.execution_order;
23 if (execution_order.empty()) {
24 return;
25 }
26
27 const size_t size = execution_order.size();
28
29 task_graph_.Clear();
30 tasks_.clear();
31 tasks_.reserve(size);
32
33 for (size_t i = 0; i < size; ++i) {
34 tasks_.push_back(task_graph_.CreatePlaceholder());
35 }
36
37 for (size_t exec_idx = 0; exec_idx < size; ++exec_idx) {
38 const SystemId id = execution_order[exec_idx];
39
40 const auto it = plan.system_id_to_entry_idx.find(id.id);
41 if (it == plan.system_id_to_entry_idx.end()) {
42 tasks_[exec_idx].Work([]() {});
43 tasks_[exec_idx].Name("SyncPoint");
44 continue;
45 }
46
47 auto& entries = schedule.system_entries_;
48 const size_t entry_idx = it->second;
49 HELIOS_ASSERT(entry_idx < entries.size(),
50 "System index out of bounds in MultiThreadedExecutor!");
51
52 tasks_[exec_idx].Work([&world, &schedule, entry_idx, exec_idx]() {
53 auto& conditions = schedule.conditions_cache_;
54 if (exec_idx < conditions.size()) {
55 bool should_run = true;
56 for (RunConditionStorage* cond_storage : conditions[exec_idx]) {
57 HELIOS_ECS_PROFILE_SCOPE();
58 HELIOS_ECS_PROFILE_ZONE_NAME(cond_storage->name);
59 if (!cond_storage->run_condition(world, cond_storage->local_data)) {
60 should_run = false;
61 break;
62 }
63 }
64 if (!should_run) {
65 return;
66 }
67 }
68
69 auto& storage = schedule.system_entries_[entry_idx].storage;
70 HELIOS_ECS_PROFILE_SCOPE();
71 HELIOS_ECS_PROFILE_ZONE_NAME(storage.name);
72 storage.system(world, storage.local_data);
73 });
74
75 auto& storage = entries[entry_idx].storage;
76 tasks_[exec_idx].Name(storage.name);
77 }
78
79 if (size <= 64) {
80 for (size_t i = 0; i < size; ++i) {
81 uint64_t mask = plan.conflicts.bitmask[i];
82 while (mask != 0) {
83 const int j = std::countr_zero(mask);
84 mask &= mask - 1;
85 if (static_cast<size_t>(j) > i) {
86 tasks_[i].Precede(tasks_[static_cast<size_t>(j)]);
87 }
88 }
89 }
90 } else {
91 for (size_t i = 0; i < size; ++i) {
92 for (size_t j = i + 1; j < size; ++j) {
93 if (plan.conflicts.matrix[i][j]) {
94 tasks_[i].Precede(tasks_[j]);
95 }
96 }
97 }
98 }
99}
100
102 BuildGraph(schedule, world);
103 future_ = executor_.get().Run(task_graph_);
104}
105
107 BuildGraph(schedule, world);
108
109 if (executor_.get().IsWorkerThread()) {
110 executor_.get().CoRun(task_graph_);
111 return;
112 }
113
114 executor_.get().Run(task_graph_).Wait();
115}
116
117} // namespace helios::ecs
#define HELIOS_ASSERT(condition,...)
Assertion macro that aborts execution in debug builds.
Definition assert.hpp:259
void ExecuteAndWait(Schedule &schedule, World &world) override
Executes all systems in the schedule and blocks until completion.
void Execute(Schedule &schedule, World &world) override
Executes the schedule using the multi-threaded executor.
A collection of systems, sets, and run conditions with ordering constraints.
Definition schedule.hpp:182
The World class manages entities with their components and systems.
Definition world.hpp:39