Helios Engine 0.1.0
A modular ECS based data-oriented C++23 game engine
 
Loading...
Searching...
No Matches
system_diagnostics.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <helios/core_pch.hpp>
4
8
9#include <cstddef>
10#include <format>
11#include <span>
12#include <string>
13#include <string_view>
14#include <vector>
15
16namespace helios::app::details {
17
18/**
19 * @brief Provides diagnostic information about system conflicts and validation errors.
20 * @details Helper class for generating detailed error messages about scheduling conflicts
21 * and access policy violations.
22 */
24public:
25 /**
26 * @brief Information about a component conflict between two systems.
27 */
30 std::string_view component_name;
31 bool read_write_conflict = false; ///< true if one reads and other writes, false if both write
32 std::string_view system_a_access; ///< "read" or "write" for system A
33 std::string_view system_b_access; ///< "read" or "write" for system B
34 };
35
36 /**
37 * @brief Information about a resource conflict between two systems.
38 */
41 std::string_view resource_name;
42 bool read_write_conflict = false; ///< true if one reads and other writes, false if both write
43 std::string_view system_a_access; ///< "read" or "write" for system A
44 std::string_view system_b_access; ///< "read" or "write" for system B
45 };
46
47 /**
48 * @brief Analyzes conflicts between two access policies.
49 * @param policy_a First access policy
50 * @param policy_b Second access policy
51 * @return Vector of component conflicts found
52 */
53 [[nodiscard]] static auto AnalyzeComponentConflicts(const AccessPolicy& policy_a, const AccessPolicy& policy_b)
54 -> std::vector<ComponentConflict>;
55
56 /**
57 * @brief Analyzes resource conflicts between two access policies.
58 * @param policy_a First access policy
59 * @param policy_b Second access policy
60 * @return Vector of resource conflicts found
61 */
62 [[nodiscard]] static auto AnalyzeResourceConflicts(const AccessPolicy& policy_a, const AccessPolicy& policy_b)
63 -> std::vector<ResourceConflict>;
64
65 /**
66 * @brief Formats component conflict information into human-readable string.
67 * @param system_a_name Name of first system
68 * @param system_b_name Name of second system
69 * @param conflicts List of component conflicts
70 * @return Formatted error message
71 */
72 [[nodiscard]] static std::string FormatComponentConflicts(std::string_view system_a_name,
73 std::string_view system_b_name,
74 std::span<const ComponentConflict> conflicts);
75
76 /**
77 * @brief Formats resource conflict information into human-readable string.
78 * @param system_a_name Name of first system
79 * @param system_b_name Name of second system
80 * @param conflicts List of resource conflicts
81 * @return Formatted error message
82 */
83 [[nodiscard]] static std::string FormatResourceConflicts(std::string_view system_a_name,
84 std::string_view system_b_name,
85 std::span<const ResourceConflict> conflicts);
86
87 /**
88 * @brief Generates a summary of access policy for debugging.
89 * @param policy Access policy to summarize
90 * @return Human-readable summary string
91 */
92 [[nodiscard]] static std::string SummarizeAccessPolicy(const AccessPolicy& policy);
93
94private:
95 /**
96 * @brief Helper to check if sorted ranges intersect and collect intersecting elements.
97 * @tparam T Type of elements (ComponentTypeInfo or ResourceTypeInfo)
98 * @param lhs First sorted range
99 * @param rhs Second sorted range
100 * @return Vector of intersecting elements
101 */
102 template <typename T>
103 [[nodiscard]] static auto FindIntersection(std::span<const T> lhs, std::span<const T> rhs) -> std::vector<T>;
104
105 template <typename T>
106 [[nodiscard]] static auto FindIntersection(const std::vector<T>& lhs, const std::vector<T>& rhs) -> std::vector<T> {
107 return FindIntersection(std::span<const T>(lhs), std::span<const T>(rhs));
108 }
109};
110
111template <typename T>
112inline auto SystemDiagnostics::FindIntersection(std::span<const T> lhs, std::span<const T> rhs) -> std::vector<T> {
113 std::vector<T> result;
114
115 auto it1 = lhs.begin();
116 auto it2 = rhs.begin();
117
118 while (it1 != lhs.end() && it2 != rhs.end()) {
119 if (it1->type_id < it2->type_id) {
120 ++it1;
121 } else if (it2->type_id < it1->type_id) {
122 ++it2;
123 } else {
124 result.push_back(*it1);
125 ++it1;
126 ++it2;
127 }
128 }
129
130 return result;
131}
132
133inline auto SystemDiagnostics::AnalyzeComponentConflicts(const AccessPolicy& policy_a, const AccessPolicy& policy_b)
134 -> std::vector<ComponentConflict> {
135 const auto queries_a = policy_a.GetQueries();
136 const auto queries_b = policy_b.GetQueries();
137
138 std::vector<ComponentConflict> conflicts;
139 conflicts.reserve(queries_a.size() * queries_b.size());
140
141 // Check all query pairs for conflicts
142 for (const auto& query_a : queries_a) {
143 for (const auto& query_b : queries_b) {
144 // Check write-write conflicts
145 const auto write_write = FindIntersection(query_a.write_components, query_b.write_components);
146 for (const auto& component : write_write) {
147 conflicts.emplace_back(component.type_id, component.name, false, "write", "write");
148 }
149
150 // Check write-read conflicts (a writes, b reads)
151 const auto write_read_a = FindIntersection(query_a.write_components, query_b.read_components);
152 for (const auto& component : write_read_a) {
153 conflicts.emplace_back(component.type_id, component.name, true, "write", "read");
154 }
155
156 // Check read-write conflicts (a reads, b writes)
157 const auto read_write_a = FindIntersection(query_a.read_components, query_b.write_components);
158 for (const auto& component : read_write_a) {
159 conflicts.emplace_back(component.type_id, component.name, true, "read", "write");
160 }
161 }
162 }
163
164 return conflicts;
165}
166
167inline std::vector<SystemDiagnostics::ResourceConflict> SystemDiagnostics::AnalyzeResourceConflicts(
168 const AccessPolicy& policy_a, const AccessPolicy& policy_b) {
169 const auto read_a = policy_a.GetReadResources();
170 const auto write_a = policy_a.GetWriteResources();
171 const auto read_b = policy_b.GetReadResources();
172 const auto write_b = policy_b.GetWriteResources();
173
174 // Write-write conflicts
175 const auto write_write = FindIntersection(write_a, write_b);
176
177 // Write-read conflicts (a writes, b reads)
178 const auto write_read = FindIntersection(write_a, read_b);
179
180 // Read-write conflicts (a reads, b writes)
181 const auto read_write = FindIntersection(read_a, write_b);
182
183 std::vector<ResourceConflict> conflicts;
184 conflicts.reserve(write_write.size() + write_read.size() + read_write.size());
185
186 for (const auto& resource : write_write) {
187 conflicts.emplace_back(resource.type_id, resource.name, false, "write", "write");
188 }
189
190 for (const auto& resource : write_read) {
191 conflicts.emplace_back(resource.type_id, resource.name, true, "write", "read");
192 }
193
194 for (const auto& resource : read_write) {
195 conflicts.emplace_back(resource.type_id, resource.name, true, "read", "write");
196 }
197
198 return conflicts;
199}
200
201inline std::string SystemDiagnostics::FormatComponentConflicts(std::string_view system_a_name,
202 std::string_view system_b_name,
203 std::span<const ComponentConflict> conflicts) {
204 if (conflicts.empty()) {
205 return "";
206 }
207
208 std::string result = std::format("Component conflicts between '{}' and '{}':\n", system_a_name, system_b_name);
209 for (const auto& conflict : conflicts) {
210 result += std::format(" - {} ({}: {}, {}: {})\n", conflict.component_name, system_a_name, conflict.system_a_access,
211 system_b_name, conflict.system_b_access);
212 }
213
214 return result;
215}
216
217inline std::string SystemDiagnostics::FormatResourceConflicts(std::string_view system_a_name,
218 std::string_view system_b_name,
219 std::span<const ResourceConflict> conflicts) {
220 if (conflicts.empty()) {
221 return {};
222 }
223
224 std::string result = std::format("Resource conflicts between '{}' and '{}':\n", system_a_name, system_b_name);
225
226 for (const auto& conflict : conflicts) {
227 result += std::format(" - {} ({}: {}, {}: {})\n", conflict.resource_name, system_a_name, conflict.system_a_access,
228 system_b_name, conflict.system_b_access);
229 }
230 return result;
231}
232
234 std::string result = "Access Policy Summary:\n";
235
236 // Summarize queries
237 const auto queries = policy.GetQueries();
238 if (!queries.empty()) {
239 result += std::format(" Queries ({}):\n", queries.size());
240 for (size_t i = 0; i < queries.size(); ++i) {
241 const auto& query = queries[i];
242 result += std::format(" Query {}:\n", i);
243
244 if (!query.read_components.empty()) {
245 result += " Read: ";
246 for (const auto& comp : query.read_components) {
247 result += std::format("{}, ", comp.name);
248 }
249 result += "\n";
250 }
251
252 if (!query.write_components.empty()) {
253 result += " Write: ";
254 for (const auto& comp : query.write_components) {
255 result += std::format("{}, ", comp.name);
256 }
257 result += "\n";
258 }
259 }
260 }
261
262 // Summarize resources
263 const auto read_resources = policy.GetReadResources();
264 const auto write_resources = policy.GetWriteResources();
265
266 if (!read_resources.empty()) {
267 result += " Read Resources: ";
268 for (const auto& res : read_resources) {
269 result += std::format("{}, ", res.name);
270 }
271 result += "\n";
272 }
273
274 if (!write_resources.empty()) {
275 result += " Write Resources: ";
276 for (const auto& res : write_resources) {
277 result += std::format("{}, ", res.name);
278 }
279 result += "\n";
280 }
281
282 if (queries.empty() && read_resources.empty() && write_resources.empty()) {
283 result += " (No data access declared)\n";
284 }
285
286 return result;
287}
288
289} // namespace helios::app::details
constexpr auto GetWriteResources() const noexcept -> std::span< const details::ResourceTypeInfo >
Gets all resource types declared for writing.
constexpr auto GetQueries() const noexcept -> std::span< const details::QueryDescriptor >
Gets all declared query descriptors.
constexpr auto GetReadResources() const noexcept -> std::span< const details::ResourceTypeInfo >
Gets all resource types declared for reading.
Provides diagnostic information about system conflicts and validation errors.
static auto AnalyzeResourceConflicts(const AccessPolicy &policy_a, const AccessPolicy &policy_b) -> std::vector< ResourceConflict >
Analyzes resource conflicts between two access policies.
static std::string FormatResourceConflicts(std::string_view system_a_name, std::string_view system_b_name, std::span< const ResourceConflict > conflicts)
Formats resource conflict information into human-readable string.
static auto AnalyzeComponentConflicts(const AccessPolicy &policy_a, const AccessPolicy &policy_b) -> std::vector< ComponentConflict >
Analyzes conflicts between two access policies.
static std::string SummarizeAccessPolicy(const AccessPolicy &policy)
Generates a summary of access policy for debugging.
static std::string FormatComponentConflicts(std::string_view system_a_name, std::string_view system_b_name, std::span< const ComponentConflict > conflicts)
Formats component conflict information into human-readable string.
Information about a component conflict between two systems.
bool read_write_conflict
true if one reads and other writes, false if both write
std::string_view system_b_access
"read" or "write" for system B
std::string_view system_a_access
"read" or "write" for system A
Information about a resource conflict between two systems.
std::string_view system_a_access
"read" or "write" for system A
std::string_view system_b_access
"read" or "write" for system B
bool read_write_conflict
true if one reads and other writes, false if both write