From 99e2937a5017f17b1e6b1df7f5d99e8922a28ae4 Mon Sep 17 00:00:00 2001 From: Ubpa <641614112@qq.com> Date: Sat, 6 Jun 2020 11:35:03 +0800 Subject: [PATCH] generate frame graph in Graphviz --- CMakeLists.txt | 3 +- README.md | 1 + doc/todo.md | 8 +- include/UECS/RTDCmptTraits.h | 45 ++++-- include/UECS/Schedule.h | 15 +- include/UECS/World.h | 6 + include/UECS/detail/RTDCmptTraits.inl | 62 +++++++- include/UECS/detail/Schedule.inl | 16 +- src/core/Schedule.cpp | 212 ++++++++++++-------------- src/core/World.cpp | 119 +++++++++++++++ src/test/12_framegraph/CMakeLists.txt | 6 + src/test/12_framegraph/main.cpp | 54 +++++++ 12 files changed, 398 insertions(+), 149 deletions(-) create mode 100644 src/test/12_framegraph/CMakeLists.txt create mode 100644 src/test/12_framegraph/main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 912b136..2295b1b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.14 FATAL_ERROR) -project(UECS VERSION 0.9.4) +project(UECS VERSION 0.9.5) message(STATUS "[Project] ${PROJECT_NAME}") include(FetchContent) @@ -24,6 +24,7 @@ Ubpa_InitProject() Ubpa_AddDep(UContainer 0.0.4) Ubpa_AddDep(UTemplate 0.4.4) +Ubpa_AddDep(UGraphviz 0.1.0) Ubpa_AddSubDirsRec(include) Ubpa_AddSubDirsRec(src) diff --git a/README.md b/README.md index 193d39a..160ba25 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,7 @@ int main() { - [parrallel with `None` filter](src/test/06_none_parallel/main.cpp) - [system **overload**](src/test/07_overload/main.cpp) - [runtime dynamic component and system](src/test/11_runtime_cmpt/main.cpp) +- [generate **frame graph** in **Graphviz**](src/test/12_framegraph/main.cpp) ## Licensing diff --git a/doc/todo.md b/doc/todo.md index daa94f3..7df97bd 100644 --- a/doc/todo.md +++ b/doc/todo.md @@ -40,21 +40,19 @@ ### not sure -- [ ] `Any` influence `EntityLocator` - [ ] ChunkJob +- [ ] serialize ## tool -- [ ] SysFuncGraph dump -- [ ] serialize -- [ ] doc +- [x] SysFuncGraph dump +- [x] doc ## detial - [x] remove `EntityMngr::ai2ei` - [x] Archetype share `Pool` - [x] simplify `Schedule` -- [ ] parallel `Schedule` - [x] `constexpr SystemFunc::HashCode()` - [x] cache `CmptTypeSet`'s hashcode - [x] store `EntityMngr` and `SystemMngr` instead `World` in `Schedule` diff --git a/include/UECS/RTDCmptTraits.h b/include/UECS/RTDCmptTraits.h index 4fef57b..d925d3f 100644 --- a/include/UECS/RTDCmptTraits.h +++ b/include/UECS/RTDCmptTraits.h @@ -14,39 +14,44 @@ namespace Ubpa { // - copy constructor: memcpy as default // - move constructor: memcpy as default // - destructor: do nothing as default + // - name class RTDCmptTraits { public: static constexpr size_t default_alignment = alignof(std::max_align_t); - inline static RTDCmptTraits& Instance() noexcept; + static RTDCmptTraits& Instance() noexcept; // neccessary - inline RTDCmptTraits& RegisterSize(CmptType type, size_t size); + RTDCmptTraits& RegisterSize(CmptType type, size_t size); // optional - inline RTDCmptTraits& RegisterAlignment(CmptType type, size_t alignment); + RTDCmptTraits& RegisterAlignment(CmptType type, size_t alignment); // optional - inline RTDCmptTraits& RegisterDefaultConstructor(CmptType type, std::function f); + RTDCmptTraits& RegisterDefaultConstructor(CmptType type, std::function f); // optional - inline RTDCmptTraits& RegisterCopyConstructor(CmptType type, std::function f); + RTDCmptTraits& RegisterCopyConstructor(CmptType type, std::function f); // optional - inline RTDCmptTraits& RegisterMoveConstructor(CmptType type, std::function f); + RTDCmptTraits& RegisterMoveConstructor(CmptType type, std::function f); // optional - inline RTDCmptTraits& RegisterDestructor(CmptType type, std::function f); + RTDCmptTraits& RegisterDestructor(CmptType type, std::function f); - inline RTDCmptTraits& Deregister(CmptType type) noexcept; + // optional + RTDCmptTraits& RegisterName(CmptType type, std::string name); - // register all for Cmpt - // static_assert - // - is_default_constructible_v - // - is_copy_constructible_v - // - is_move_constructible_v - // - is_destructible_v - template + size_t Sizeof(CmptType type) const; + size_t Alignof(CmptType type) const; + void CopyConstruct(CmptType type, void* dst, void* src) const; + void MoveConstruct(CmptType type, void* dst, void* src) const; + void Destruct(CmptType type, void* cmpt) const; + std::string_view Nameof(CmptType type) const; + + RTDCmptTraits& Deregister(CmptType type) noexcept; + + template void Register(); template @@ -59,12 +64,22 @@ namespace Ubpa { RTDCmptTraits() = default; + // register all for Cmpt + // static_assert + // - is_default_constructible_v + // - is_copy_constructible_v + // - is_move_constructible_v + // - is_destructible_v + template + void RegisterOne(); + std::unordered_map sizeofs; std::unordered_map alignments; std::unordered_map> default_constructors; // dst <- src std::unordered_map> copy_constructors; // dst <- src std::unordered_map> move_constructors; // dst <- src std::unordered_map> destructors; + std::unordered_map names; }; } diff --git a/include/UECS/Schedule.h b/include/UECS/Schedule.h index 13d96e1..342cbaa 100644 --- a/include/UECS/Schedule.h +++ b/include/UECS/Schedule.h @@ -8,6 +8,10 @@ #include +namespace Ubpa::detail::Schedule_ { + struct Compiler; +} + namespace Ubpa { class EntityMngr; class SystemMngr; @@ -54,10 +58,19 @@ namespace Ubpa { private: template - Schedule& Request(Args&&... args); + void Request(Args&&... args); Schedule(EntityMngr* entityMngr, SystemMngr* systemMngr); void Clear(); + + struct CmptSysFuncs { + std::vector lastFrameSysFuncs; + std::vector writeSysFuncs; + std::vector latestSysFuncs; + }; + friend struct detail::Schedule_::Compiler; + std::unordered_map cmptSysFuncsMap; + SysFuncGraph GenSysFuncGraph() const; // SystemFunc's hashcode to pointer of SystemFunc diff --git a/include/UECS/World.h b/include/UECS/World.h index 1b087f8..5e29e80 100644 --- a/include/UECS/World.h +++ b/include/UECS/World.h @@ -4,6 +4,8 @@ #include "SystemMngr.h" #include "EntityMngr.h" +#include + namespace Ubpa { // SystemMngr + EntityMngr class World { @@ -23,6 +25,10 @@ namespace Ubpa { // you can use graphviz to vistualize the graph std::string DumpUpdateJobGraph() const; + // after running Update + // use RTDCmptTraits' registered component name + Graphviz::Graph GenUpdateFrameGraph() const; + private: mutable JobExecutor executor; Schedule schedule; diff --git a/include/UECS/detail/RTDCmptTraits.inl b/include/UECS/detail/RTDCmptTraits.inl index 103e71a..e8b5825 100644 --- a/include/UECS/detail/RTDCmptTraits.inl +++ b/include/UECS/detail/RTDCmptTraits.inl @@ -8,44 +8,90 @@ namespace Ubpa { return instance; } - // neccessary inline RTDCmptTraits& RTDCmptTraits::RegisterSize(CmptType type, size_t size) { sizeofs[type] = size; return *this; } - // optional inline RTDCmptTraits& RTDCmptTraits::RegisterAlignment(CmptType type, size_t alignment) { alignments[type] = alignment; return *this; } - // optional inline RTDCmptTraits& RTDCmptTraits::RegisterDefaultConstructor(CmptType type, std::function f) { default_constructors[type] = std::move(f); return *this; } - // optional inline RTDCmptTraits& RTDCmptTraits::RegisterCopyConstructor(CmptType type, std::function f) { copy_constructors[type] = std::move(f); return *this; } - // optional inline RTDCmptTraits& RTDCmptTraits::RegisterMoveConstructor(CmptType type, std::function f) { move_constructors[type] = std::move(f); return *this; } - // optional inline RTDCmptTraits& RTDCmptTraits::RegisterDestructor(CmptType type, std::function f) { destructors[type] = std::move(f); return *this; } - template + inline RTDCmptTraits& RTDCmptTraits::RegisterName(CmptType type, std::string name) { + names[type] = std::move(name); + return *this; + } + + inline size_t RTDCmptTraits::Sizeof(CmptType type) const { + assert(sizeofs.find(type) != sizeofs.end()); + return sizeofs.find(type)->second; + } + + inline size_t RTDCmptTraits::Alignof(CmptType type) const { + assert(alignments.find(type) != alignments.end()); + return alignments.find(type)->second; + } + + inline void RTDCmptTraits::CopyConstruct(CmptType type, void* dst, void* src) const { + auto target = copy_constructors.find(type); + + if (target != copy_constructors.end()) + target->second(dst, src); + else + memcpy(dst, src, Sizeof(type)); + } + + inline void RTDCmptTraits::MoveConstruct(CmptType type, void* dst, void* src) const { + auto target = move_constructors.find(type); + + if (target != move_constructors.end()) + target->second(dst, src); + else + memcpy(dst, src, Sizeof(type)); + } + + inline void RTDCmptTraits::Destruct(CmptType type, void* cmpt) const { + auto target = destructors.find(type); + if (target != destructors.end()) + target->second(cmpt); + } + + inline std::string_view RTDCmptTraits::Nameof(CmptType type) const { + auto target = names.find(type); + if (target != names.end()) + return target->second; + else + return {}; + } + + template void RTDCmptTraits::Register() { + (RegisterOne(), ...); + } + + template + void RTDCmptTraits::RegisterOne() { static_assert(std::is_default_constructible_v, " must be default-constructible"); static_assert(std::is_copy_constructible_v, " must be copy-constructible"); static_assert(std::is_move_constructible_v, " must be move-constructible"); @@ -55,6 +101,7 @@ namespace Ubpa { sizeofs[type] = sizeof(Cmpt); alignments[type] = alignof(Cmpt); + names[type] = std::string{ nameof::nameof_type() }; if constexpr (!std::is_trivially_default_constructible_v) { default_constructors[type] = [](void* cmpt) { @@ -84,6 +131,7 @@ namespace Ubpa { sizeofs.erase(type); alignments.erase(type); + names.erase(type); if constexpr (!std::is_trivially_constructible_v) default_constructors.erase(type); diff --git a/include/UECS/detail/Schedule.inl b/include/UECS/detail/Schedule.inl index d0a5f6c..43ed7b2 100644 --- a/include/UECS/detail/Schedule.inl +++ b/include/UECS/detail/Schedule.inl @@ -3,19 +3,27 @@ namespace Ubpa { template Schedule& Schedule::Register(Func&& func, std::string name, EntityFilter filter) { - return Request(std::forward(func), std::move(name), std::move(filter)); + Request(std::forward(func), std::move(name), std::move(filter)); + return *this; } template Schedule& Schedule::Register(Func&& func, std::string name, EntityLocator locator, EntityFilter filter) { - return Request(std::forward(func), std::move(name), std::move(locator), std::move(filter)); + Request(std::forward(func), std::move(name), std::move(locator), std::move(filter)); + return *this; } template - Schedule& Schedule::Request(Args&&... args) { + void Schedule::Request(Args&&... args) { SystemFunc* sysFunc = sysFuncPool.Request(std::forward(args)...); sysFuncs.emplace(sysFunc->HashCode(), sysFunc); - return *this; + const auto& locator = sysFunc->query.locator; + for (const auto& type : locator.LastFrameCmptTypes()) + cmptSysFuncsMap[type].lastFrameSysFuncs.push_back(sysFunc); + for (const auto& type : locator.WriteCmptTypes()) + cmptSysFuncsMap[type].writeSysFuncs.push_back(sysFunc); + for (const auto& type : locator.LatestCmptTypes()) + cmptSysFuncsMap[type].latestSysFuncs.push_back(sysFunc); } inline Schedule::Schedule(EntityMngr* entityMngr, SystemMngr* systemMngr) diff --git a/src/core/Schedule.cpp b/src/core/Schedule.cpp index 6e7eda2..c2183b6 100644 --- a/src/core/Schedule.cpp +++ b/src/core/Schedule.cpp @@ -8,12 +8,6 @@ using namespace Ubpa; using namespace std; namespace Ubpa::detail::Schedule_ { - struct CmptSysFuncs { - vector lastFrameSysFuncs; - vector writeSysFuncs; - vector latestSysFuncs; - }; - struct NoneGroup { NoneGroup() = default; NoneGroup(SystemFunc* func) @@ -41,115 +35,117 @@ namespace Ubpa::detail::Schedule_ { set sysFuncs; }; - void SetPrePostEdge(SysFuncGraph& graph, - const CmptSysFuncs& cmptFuncs, - const unordered_map& gMap) - { - const auto& preReaders = cmptFuncs.lastFrameSysFuncs; - const auto& writers = cmptFuncs.writeSysFuncs; - const auto& postReaders = cmptFuncs.latestSysFuncs; - - if (!preReaders.empty()) { - NoneGroup preGroup{ gMap.find(preReaders.front())->second }; - for (size_t i = 1; i < preReaders.size(); i++) - preGroup += gMap.find(preReaders[i])->second; - - for (const auto& w : writers) { - if (NoneGroup::Parallelable(preGroup, gMap.find(w)->second)) - continue; - for (auto preReader : preReaders) - graph.AddEdge(preReader, w); + struct Compiler { + static void SetPrePostEdge(SysFuncGraph& graph, + const Schedule::CmptSysFuncs& cmptFuncs, + const unordered_map& gMap) + { + const auto& preReaders = cmptFuncs.lastFrameSysFuncs; + const auto& writers = cmptFuncs.writeSysFuncs; + const auto& postReaders = cmptFuncs.latestSysFuncs; + + if (!preReaders.empty()) { + NoneGroup preGroup{ gMap.find(preReaders.front())->second }; + for (size_t i = 1; i < preReaders.size(); i++) + preGroup += gMap.find(preReaders[i])->second; + + for (const auto& w : writers) { + if (NoneGroup::Parallelable(preGroup, gMap.find(w)->second)) + continue; + for (auto preReader : preReaders) + graph.AddEdge(preReader, w); + } } - } - if (!postReaders.empty()) { - NoneGroup postGroup{ gMap.find(postReaders.front())->second }; - for (size_t i = 1; i < postReaders.size(); i++) - postGroup += gMap.find(postReaders[i])->second; + if (!postReaders.empty()) { + NoneGroup postGroup{ gMap.find(postReaders.front())->second }; + for (size_t i = 1; i < postReaders.size(); i++) + postGroup += gMap.find(postReaders[i])->second; - for (const auto& w : writers) { - if (NoneGroup::Parallelable(postGroup, gMap.find(w)->second)) - continue; - for (auto postReader : postReaders) - graph.AddEdge(w, postReader); + for (const auto& w : writers) { + if (NoneGroup::Parallelable(postGroup, gMap.find(w)->second)) + continue; + for (auto postReader : postReaders) + graph.AddEdge(w, postReader); + } } } - } - vector GenSortNoneGroup(SysFuncGraph graph, - const CmptSysFuncs& cmptFuncs, - const unordered_map& gMap) - { - const auto& preReaders = cmptFuncs.lastFrameSysFuncs; - const auto& writers = cmptFuncs.writeSysFuncs; - const auto& postReaders = cmptFuncs.latestSysFuncs; + static vector GenSortNoneGroup(SysFuncGraph graph, + const Schedule::CmptSysFuncs& cmptFuncs, + const unordered_map& gMap) + { + const auto& preReaders = cmptFuncs.lastFrameSysFuncs; + const auto& writers = cmptFuncs.writeSysFuncs; + const auto& postReaders = cmptFuncs.latestSysFuncs; - NoneGroup preGroup, postGroup; + NoneGroup preGroup, postGroup; - if (!preReaders.empty()) { - preGroup = gMap.find(preReaders.front())->second; - for (size_t i = 1; i < preReaders.size(); i++) - preGroup += gMap.find(preReaders[i])->second; - } + if (!preReaders.empty()) { + preGroup = gMap.find(preReaders.front())->second; + for (size_t i = 1; i < preReaders.size(); i++) + preGroup += gMap.find(preReaders[i])->second; + } - if (!postReaders.empty()) { - postGroup = gMap.find(postReaders.front())->second; - for (size_t i = 1; i < postReaders.size(); i++) - postGroup += gMap.find(postReaders[i])->second; - } + if (!postReaders.empty()) { + postGroup = gMap.find(postReaders.front())->second; + for (size_t i = 1; i < postReaders.size(); i++) + postGroup += gMap.find(postReaders[i])->second; + } - vector funcs; - if (!preGroup.sysFuncs.empty()) - funcs.push_back(*preGroup.sysFuncs.begin()); - if (!postGroup.sysFuncs.empty()) - funcs.push_back(*postGroup.sysFuncs.begin()); - for (auto w : writers) - funcs.push_back(w); - - auto subgraph = graph.SubGraph(funcs); - auto [success, sortedFuncs] = subgraph.Toposort(); - assert(success); - - vector rst; - - for (auto func : sortedFuncs) { - if (!preGroup.sysFuncs.empty() && func == *preGroup.sysFuncs.begin()) - rst.push_back(move(preGroup)); - else if (!postGroup.sysFuncs.empty() && func == *postGroup.sysFuncs.begin()) - rst.push_back(move(postGroup)); - else // writer - rst.push_back(gMap.find(func)->second); - } + vector funcs; + if (!preGroup.sysFuncs.empty()) + funcs.push_back(*preGroup.sysFuncs.begin()); + if (!postGroup.sysFuncs.empty()) + funcs.push_back(*postGroup.sysFuncs.begin()); + for (auto w : writers) + funcs.push_back(w); + + auto subgraph = graph.SubGraph(funcs); + auto [success, sortedFuncs] = subgraph.Toposort(); + assert(success); + + vector rst; + + for (auto func : sortedFuncs) { + if (!preGroup.sysFuncs.empty() && func == *preGroup.sysFuncs.begin()) + rst.push_back(move(preGroup)); + else if (!postGroup.sysFuncs.empty() && func == *postGroup.sysFuncs.begin()) + rst.push_back(move(postGroup)); + else // writer + rst.push_back(gMap.find(func)->second); + } - for (size_t i = 0; i < rst.size(); i++) { - auto& gi = rst[i]; - for (size_t j = i + 1; j < rst.size(); j++) { - const auto& gj = rst[j]; - if (!NoneGroup::Parallelable(gi, gj)) - continue; - - bool haveOrder = false; - for (auto ifunc : gi.sysFuncs) { - for (auto jfunc : gj.sysFuncs) { - if (subgraph.HavePath(ifunc, jfunc) - || subgraph.HavePath(jfunc, ifunc)) { - haveOrder = true; - break; + for (size_t i = 0; i < rst.size(); i++) { + auto& gi = rst[i]; + for (size_t j = i + 1; j < rst.size(); j++) { + const auto& gj = rst[j]; + if (!NoneGroup::Parallelable(gi, gj)) + continue; + + bool haveOrder = false; + for (auto ifunc : gi.sysFuncs) { + for (auto jfunc : gj.sysFuncs) { + if (subgraph.HavePath(ifunc, jfunc) + || subgraph.HavePath(jfunc, ifunc)) { + haveOrder = true; + break; + } } + if (haveOrder) break; } - if (haveOrder) break; - } - if (haveOrder) continue; + if (haveOrder) continue; - gi += gj; + gi += gj; - rst.erase(rst.begin() + j); - j--; + rst.erase(rst.begin() + j); + j--; + } } - } - return rst; - } + return rst; + } + }; } Schedule& Schedule::Order(string_view x, string_view y) { @@ -227,8 +223,6 @@ void Schedule::Clear() { } SysFuncGraph Schedule::GenSysFuncGraph() const { - // TODO : parallel - // [change func Filter] for (const auto& [hashcode, change] : sysFilterChange) { if (sysLockFilter.find(hashcode) != sysLockFilter.end()) @@ -248,20 +242,6 @@ SysFuncGraph Schedule::GenSysFuncGraph() const { func->query.filter.EraseNone(change.eraseNones); } - // [gen cmptSysFuncsMap] - - unordered_map cmptSysFuncsMap; - - for (const auto& [hashcode, sysFunc] : sysFuncs) { - const auto& locator = sysFunc->query.locator; - for (const auto& type : locator.LastFrameCmptTypes()) - cmptSysFuncsMap[type].lastFrameSysFuncs.push_back(sysFunc); - for (const auto& type : locator.WriteCmptTypes()) - cmptSysFuncsMap[type].writeSysFuncs.push_back(sysFunc); - for (const auto& type : locator.LatestCmptTypes()) - cmptSysFuncsMap[type].latestSysFuncs.push_back(sysFunc); - } - // [gen groupMap] unordered_map groupMap; for (const auto& [hashcode, sysFunc] : sysFuncs) @@ -290,14 +270,14 @@ SysFuncGraph Schedule::GenSysFuncGraph() const { // [gen graph] - edge - time point for (const auto& [type, cmptSysFuncs] : cmptSysFuncsMap) - detail::Schedule_::SetPrePostEdge(graph, cmptSysFuncs, groupMap); + detail::Schedule_::Compiler::SetPrePostEdge(graph, cmptSysFuncs, groupMap); // [gen graph] - edge - none group for (const auto& [type, cmptSysFuncs] : cmptSysFuncsMap) { if (cmptSysFuncs.writeSysFuncs.empty()) continue; - auto sortedGroup = detail::Schedule_::GenSortNoneGroup(graph, cmptSysFuncs, groupMap); + auto sortedGroup = detail::Schedule_::Compiler::GenSortNoneGroup(graph, cmptSysFuncs, groupMap); for (size_t i = 0; i < sortedGroup.size() - 1; i++) { const auto& gx = sortedGroup[i]; diff --git a/src/core/World.cpp b/src/core/World.cpp index 6a955bb..ee002ce 100644 --- a/src/core/World.cpp +++ b/src/core/World.cpp @@ -40,3 +40,122 @@ void World::Update() { string World::DumpUpdateJobGraph() const { return jobGraph.dump(); } + +// after running Update +Graphviz::Graph World::GenUpdateFrameGraph() const { + Graphviz::Graph graph("Update Frame Graph", true); + + graph + .RegisterGraphNodeAttr("style", "filled") + .RegisterGraphNodeAttr("fontcolor", "white") + .RegisterGraphNodeAttr("fontname", "consolas"); + + auto& registrar = graph.GetRegistrar(); + + auto& subgraph_cmpt = graph.GenSubgraph("Component Nodes"); + auto& subgraph_sys = graph.GenSubgraph("System Function Nodes"); + + auto& subgraph_lastframe = graph.GenSubgraph("LastFrame Edges"); + auto& subgraph_write = graph.GenSubgraph("Write Edges"); + auto& subgraph_latest = graph.GenSubgraph("Latest Edges"); + + auto& subgraph_all = graph.GenSubgraph("All Edges"); + auto& subgraph_any = graph.GenSubgraph("Any Edges"); + auto& subgraph_none = graph.GenSubgraph("None Edges"); + + subgraph_cmpt + .RegisterGraphNodeAttr("shape", "ellipse") + .RegisterGraphNodeAttr("color", "#6597AD"); + + subgraph_sys + .RegisterGraphNodeAttr("shape", "box") + .RegisterGraphNodeAttr("color", "#F79646"); + + subgraph_lastframe.RegisterGraphEdgeAttr("color", "#60C5F1"); + subgraph_write.RegisterGraphEdgeAttr("color", "#F47378"); + subgraph_latest.RegisterGraphEdgeAttr("color", "#6BD089"); + + subgraph_all + .RegisterGraphEdgeAttr("style", "dashed") + .RegisterGraphEdgeAttr("color", "#C785C8") + .RegisterGraphEdgeAttr("arrowhead", "crow"); + + subgraph_any + .RegisterGraphEdgeAttr("style", "dashed") + .RegisterGraphEdgeAttr("color", "#C785C8") + .RegisterGraphEdgeAttr("arrowhead", "diamond"); + + subgraph_none + .RegisterGraphEdgeAttr("style", "dashed") + .RegisterGraphEdgeAttr("color", "#C785C8") + .RegisterGraphEdgeAttr("arrowhead", "odot"); + + unordered_set cmptTypes; + unordered_map cmptType2idx; + + auto queryCmptName = [](CmptType type) -> string { + auto cmptName = RTDCmptTraits::Instance().Nameof(type); + return cmptName.empty() ? std::to_string(type.HashCode()) : string{ cmptName }; + }; + + for (const auto& [hash, sysFunc] : schedule.sysFuncs) { + for (auto cmptType : sysFunc->query.locator.CmptTypes()) + cmptTypes.insert(cmptType); + for (auto cmptType : sysFunc->query.filter.AllCmptTypes()) + cmptTypes.insert(cmptType); + for (auto cmptType : sysFunc->query.filter.AnyCmptTypes()) + cmptTypes.insert(cmptType); + for (auto cmptType : sysFunc->query.filter.NoneCmptTypes()) + cmptTypes.insert(cmptType); + } + + for (auto cmptType : cmptTypes) { + auto cmptIdx = registrar.RegisterNode(queryCmptName(cmptType)); + cmptType2idx[cmptType] = cmptIdx; + subgraph_cmpt.AddNode(cmptIdx); + } + + for (const auto& [hash, sysFuncs] : schedule.sysFuncs) { + auto sysIdx = registrar.RegisterNode(sysFuncs->Name()); + subgraph_sys.AddNode(sysIdx); + + const auto& locator = sysFuncs->query.locator; + for (const auto& cmptType : locator.LastFrameCmptTypes()) { + auto edgeIdx = registrar.RegisterEdge(cmptType2idx[cmptType], sysIdx); + subgraph_lastframe.AddEdge(edgeIdx); + } + for (const auto& cmptType : locator.WriteCmptTypes()) { + auto edgeIdx = registrar.RegisterEdge(sysIdx, cmptType2idx[cmptType]); + subgraph_write.AddEdge(edgeIdx); + } + for (const auto& cmptType : locator.LatestCmptTypes()) { + auto edgeIdx = registrar.RegisterEdge(cmptType2idx[cmptType], sysIdx); + subgraph_latest.AddEdge(edgeIdx); + } + + const auto& filter = sysFuncs->query.filter; + for (const auto& cmptType : filter.AllCmptTypes()) { + auto cmptIdx = cmptType2idx[cmptType]; + if (registrar.IsRegisteredEdge(sysIdx, cmptIdx)) + continue; + auto edgeIdx = registrar.RegisterEdge(sysIdx, cmptType2idx[cmptType]); + subgraph_all.AddEdge(edgeIdx); + } + for (const auto& cmptType : filter.AnyCmptTypes()) { + auto cmptIdx = cmptType2idx[cmptType]; + if (registrar.IsRegisteredEdge(sysIdx, cmptIdx)) + continue; + auto edgeIdx = registrar.RegisterEdge(sysIdx, cmptType2idx[cmptType]); + subgraph_any.AddEdge(edgeIdx); + } + for (const auto& cmptType : filter.NoneCmptTypes()) { + auto cmptIdx = cmptType2idx[cmptType]; + if (registrar.IsRegisteredEdge(sysIdx, cmptIdx)) + continue; + auto edgeIdx = registrar.RegisterEdge(sysIdx, cmptType2idx[cmptType]); + subgraph_none.AddEdge(edgeIdx); + } + } + + return graph; +} diff --git a/src/test/12_framegraph/CMakeLists.txt b/src/test/12_framegraph/CMakeLists.txt new file mode 100644 index 0000000..ec4a795 --- /dev/null +++ b/src/test/12_framegraph/CMakeLists.txt @@ -0,0 +1,6 @@ +Ubpa_GetTargetName(core "${PROJECT_SOURCE_DIR}/src/core") +Ubpa_AddTarget( + TEST + MODE EXE + LIB ${core} +) diff --git a/src/test/12_framegraph/main.cpp b/src/test/12_framegraph/main.cpp new file mode 100644 index 0000000..46b180a --- /dev/null +++ b/src/test/12_framegraph/main.cpp @@ -0,0 +1,54 @@ +#include + +#include + +using namespace Ubpa; +using namespace std; + +struct A {}; +struct B {}; +struct C {}; +struct D {}; +struct E {}; +struct F {}; +struct G {}; + +struct MySystem { + static void OnUpdate(Schedule& schedule) { + EntityFilter filter( + TypeList{}, // all + TypeList{}, // any + TypeList{} // none + ); + schedule + .Register( + [](CmptTag::LastFrame a, CmptTag::Write b, CmptTag::Latest c) {}, + "System Func", + filter + ); + } +}; + +int main() { + World w; + w.systemMngr.Register(); + + RTDCmptTraits::Instance() + .RegisterName(CmptType::Of, "A") + .RegisterName(CmptType::Of, "B") + .RegisterName(CmptType::Of, "C") + .RegisterName(CmptType::Of, "D") + .RegisterName(CmptType::Of, "E") + .RegisterName(CmptType::Of, "F") + .RegisterName(CmptType::Of, "G") + ; + + w.entityMngr.Create(); + + w.Update(); + + cout << w.DumpUpdateJobGraph() << endl; + cout << w.GenUpdateFrameGraph().Dump() << endl; + + return 0; +}