From 016f15f76d051fe005ec54b33df09c6231663125 Mon Sep 17 00:00:00 2001 From: ubpa Date: Mon, 22 Mar 2021 14:21:45 +0800 Subject: [PATCH] CommandBuffer --- include/UECS/Chunk.hpp | 9 ++++- include/UECS/CommandBuffer.hpp | 42 ++++++++++++++++++++++ include/UECS/Schedule.hpp | 7 ---- include/UECS/SystemFunc.hpp | 9 +++-- include/UECS/World.hpp | 4 ++- include/UECS/details/CmptTag.inl | 5 --- include/UECS/details/SystemFunc.inl | 11 +++--- src/core/CommandBuffer.cpp | 0 src/core/EntityMngr.cpp | 37 ++++++++++++------- src/core/SystemFunc.cpp | 11 +++--- src/core/World.cpp | 22 +++++------- src/test/23_command_buffer/CMakeLists.txt | 8 +++++ src/test/23_command_buffer/main.cpp | 44 +++++++++++++++++++++++ 13 files changed, 159 insertions(+), 50 deletions(-) create mode 100644 include/UECS/CommandBuffer.hpp create mode 100644 src/core/CommandBuffer.cpp create mode 100644 src/test/23_command_buffer/CMakeLists.txt create mode 100644 src/test/23_command_buffer/main.cpp diff --git a/include/UECS/Chunk.hpp b/include/UECS/Chunk.hpp index e095dfa..651636b 100644 --- a/include/UECS/Chunk.hpp +++ b/include/UECS/Chunk.hpp @@ -73,5 +73,12 @@ namespace Ubpa::UECS { std::uint8_t data[ChunkSize]; }; - using ChunkView = const Chunk*; + class ChunkView { + public: + ChunkView(const Chunk* c = nullptr) : chunk { c }{} + const Chunk* GetChunk() const noexcept { return chunk; } + const Chunk* operator->() const noexcept { return chunk; } + private: + const Chunk* chunk; + }; } diff --git a/include/UECS/CommandBuffer.hpp b/include/UECS/CommandBuffer.hpp new file mode 100644 index 0000000..1b2b1aa --- /dev/null +++ b/include/UECS/CommandBuffer.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include +#include +#include + +namespace Ubpa::UECS { + class CommandBuffer { + public: + void AddCommand(std::function command, int layer) { lcommands[layer].push_back(std::move(command)); } + void AddCommandBuffer(CommandBuffer cb) { + for (auto& [layer, cmds] : cb.lcommands) { + auto& dst = lcommands[layer]; + dst.reserve(dst.size() + cmds.size()); + for (auto& cmd : cmds) + dst.push_back(std::move(cmd)); + } + } + bool Empty() const noexcept { + for (const auto& [layer, cmds] : lcommands) { + if (!cmds.empty()) + return false; + } + return true; + } + void Clear() { lcommands.clear(); } + + auto& GetCommands() noexcept { return lcommands; } + const auto& GetCommands() const noexcept { return lcommands; } + private: + std::map>> lcommands; + }; + + class CommandBufferView { + public: + CommandBufferView(CommandBuffer* cb = nullptr) : commandBuffer{ cb } {} + CommandBuffer* GetCommandBuffer() const noexcept { return commandBuffer; } + CommandBuffer* operator->()const noexcept { return commandBuffer; } + private: + CommandBuffer* commandBuffer; + }; +} diff --git a/include/UECS/Schedule.hpp b/include/UECS/Schedule.hpp index dbe9764..afc768d 100644 --- a/include/UECS/Schedule.hpp +++ b/include/UECS/Schedule.hpp @@ -106,11 +106,6 @@ namespace Ubpa::UECS { ChunkJobConfig config ); - Schedule& RegisterCommand(std::function command, int layer = 0) { - commandBuffer[layer].push_back(std::move(command)); - return *this; - } - Schedule& Order(std::string_view x, std::string_view y); Schedule& AddNone(std::string_view sys, TypeID); @@ -159,8 +154,6 @@ namespace Ubpa::UECS { std::unordered_map> sysNones; - std::map>> commandBuffer; - mutable std::pmr::monotonic_buffer_resource frame_rsrc; // release in every frame std::string_view RegisterFrameString(std::string_view str); diff --git a/include/UECS/SystemFunc.hpp b/include/UECS/SystemFunc.hpp index 7bffc3b..cc0f20f 100644 --- a/include/UECS/SystemFunc.hpp +++ b/include/UECS/SystemFunc.hpp @@ -8,6 +8,7 @@ #include "CmptsView.hpp" #include "SingletonsView.hpp" #include "Chunk.hpp" +#include "CommandBuffer.hpp" #include @@ -24,9 +25,11 @@ namespace Ubpa::UECS { // * std::size_t indexInQuery // * : {LastFrame|Write|Latest}... // * CmptsView + // * CommandBufferView // 2. Mode::Chunk // * std::size_t entityBeginIndexInQuery // * ChunkView (necessary) + // * CommandBufferView // 3. Mode::Job // * Write> (only job can write singletons) class SystemFunc { @@ -60,8 +63,8 @@ namespace Ubpa::UECS { std::size_t GetValue() const noexcept { return hashCode; } - void operator()(World*, SingletonsView, Entity, std::size_t entityIndexInQuery, CmptsView) const; - void operator()(World*, SingletonsView, std::size_t entityBeginIndexInQuery, ChunkView) const; + void operator()(World*, SingletonsView, Entity, std::size_t entityIndexInQuery, CmptsView, CommandBufferView) const; + void operator()(World*, SingletonsView, std::size_t entityBeginIndexInQuery, ChunkView, CommandBufferView) const; void operator()(World*, SingletonsView) const; Mode GetMode() const noexcept { return mode; } @@ -73,7 +76,7 @@ namespace Ubpa::UECS { std::string_view name; std::size_t hashCode; // after name bool isParallel; - std::function func; + std::function func; }; } diff --git a/include/UECS/World.hpp b/include/UECS/World.hpp index c7721e1..2e26dbd 100644 --- a/include/UECS/World.hpp +++ b/include/UECS/World.hpp @@ -1,6 +1,7 @@ #pragma once #include "Entity.hpp" +#include "CommandBuffer.hpp" #include "SystemMngr.hpp" #include "EntityMngr.hpp" #include "Schedule.hpp" @@ -33,6 +34,7 @@ namespace Ubpa::UECS { void Update(); void AddCommand(std::function command, int layer = 0); + void AddCommandBuffer(CommandBuffer cb); // after running Update() // you can use graphviz to vistualize the graph @@ -130,7 +132,7 @@ namespace Ubpa::UECS { std::unique_ptr jobRsrc; // command - std::map>> commandBuffer; + CommandBuffer commandBuffer; std::mutex commandBufferMutex; void RunCommands(); diff --git a/include/UECS/details/CmptTag.inl b/include/UECS/details/CmptTag.inl index 3ddaecd..a5229f4 100644 --- a/include/UECS/details/CmptTag.inl +++ b/include/UECS/details/CmptTag.inl @@ -6,7 +6,6 @@ namespace Ubpa::UECS { struct Entity; class CmptLocator; class World; - class Chunk; } namespace Ubpa::UECS { @@ -172,8 +171,6 @@ namespace Ubpa::UECS { struct DecayArg : DecayTag {}; template<> struct DecayArg : std::type_identity {}; - template<> - struct DecayArg : std::type_identity {}; // ==== @@ -187,14 +184,12 @@ namespace Ubpa::UECS { template struct IsWrite : std::true_type {}; template struct IsWrite : std::false_type {}; template<> struct IsWrite : std::false_type {}; - template<> struct IsWrite : std::false_type { static_assert("you should use const Chunk*"); }; template struct IsLatest : std::false_type {}; template struct IsLatest> : std::true_type {}; template struct IsLatest>> : std::false_type {}; template struct IsLatest : std::true_type {}; template<> struct IsLatest : std::false_type {}; - template<> struct IsLatest : std::false_type {}; template struct IsLastFrameSingleton : std::false_type {}; template struct IsLastFrameSingleton>> : std::true_type {}; diff --git a/include/UECS/details/SystemFunc.inl b/include/UECS/details/SystemFunc.inl index e11ec97..3d3df76 100644 --- a/include/UECS/details/SystemFunc.inl +++ b/include/UECS/details/SystemFunc.inl @@ -103,9 +103,10 @@ namespace Ubpa::UECS { !Contain_v && !Contain_v && !Contain_v - && !Contain_v, - "(Mode::Job) SystemFunc's argument list cann't have Entity, indexInQuery CmptsView or ChunkView" - ); + && !Contain_v + && !Contain_v, + "(Mode::Job) SystemFunc's argument list cann't have Entity, indexInQuery CmptsView, ChunkView or CommandBufferView" + ); } } @@ -125,7 +126,8 @@ namespace Ubpa::UECS::details { Entity e, std::size_t entityIndexInQuery, CmptsView cmpts, - ChunkView chunkView) + ChunkView chunkView, + CommandBufferView cbv) { auto args = std::tuple{ w, @@ -136,6 +138,7 @@ namespace Ubpa::UECS::details { cmpts, reinterpret_cast(cmpts.Components()[Find_v].Ptr())..., chunkView, + cbv }; func(std::get(args)...); }; diff --git a/src/core/CommandBuffer.cpp b/src/core/CommandBuffer.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/core/EntityMngr.cpp b/src/core/EntityMngr.cpp index 919b348..cc3fcee 100644 --- a/src/core/EntityMngr.cpp +++ b/src/core/EntityMngr.cpp @@ -4,6 +4,7 @@ #include #include +#include using namespace Ubpa::UECS; using namespace std; @@ -176,7 +177,7 @@ void EntityMngr::Attach(Entity e, std::span types) { auto target = dstArchetype->GetCmptTraits().GetTypes().find(type); assert(target != dstArchetype->GetCmptTraits().GetTypes().end()); auto idx = static_cast(std::distance(dstArchetype->GetCmptTraits().GetTypes().begin(), target)); - srcArchetype->GetCmptTraits().GetTraits()[idx].DefaultConstruct(info.archetype->WriteAt(type, info.idxInArchetype).Ptr()); + dstArchetype->GetCmptTraits().GetTraits()[idx].DefaultConstruct(info.archetype->WriteAt(type, info.idxInArchetype).Ptr()); } } @@ -321,12 +322,13 @@ bool EntityMngr::GenEntityJob(World* w, Job* job, SystemFunc* sys) const { auto singletons = LocateSingletons(sys->singletonLocator); if (!sys->singletonLocator.SingletonTypes().empty() && singletons.empty()) return false; - + + const auto& archetypes = QueryArchetypes(sys->entityQuery); + if (archetypes.empty()) + return false; + if (sys->IsParallel()) { assert(job); - const auto& archetypes = QueryArchetypes(sys->entityQuery); - if (archetypes.empty()) - return false; std::size_t indexOffsetInQuery = 0; for (Archetype* archetype : archetypes) { auto [chunkEntity, chunkCmpts, sizes] = archetype->Locate(sys->entityQuery.locator.AccessTypeIDs()); @@ -344,7 +346,7 @@ bool EntityMngr::GenEntityJob(World* w, Job* job, SystemFunc* sys) const { std::size_t indexOffsetInQueryChunk = indexOffsetInQuery + idxOffsetInChunk; CmptsView chunkView{ std::span{cmpts.data(), cmpts.size()} }; SingletonsView singletonsView{ std::span{singletons.data(), singletons.size()} }; - + CommandBuffer cb; std::size_t J = min(chunkCapacity, num - idxOffsetInChunk); for (std::size_t j = 0; j < J; j++) { (*sys)( @@ -352,11 +354,13 @@ bool EntityMngr::GenEntityJob(World* w, Job* job, SystemFunc* sys) const { singletonsView, entities[j], indexOffsetInQueryChunk + j, - chunkView + chunkView, + &cb ); for (std::size_t k = 0; k < cmpts.size(); k++) reinterpret_cast(cmpts[k].p) += sizes[k]; } + w->AddCommandBuffer(std::move(cb)); }); } @@ -364,9 +368,10 @@ bool EntityMngr::GenEntityJob(World* w, Job* job, SystemFunc* sys) const { } } else { - auto work = [this, singletons = std::move(singletons), sys, w]() { + auto work = [this, singletons = std::move(singletons), sys, w, archetypes]() { + CommandBuffer cb; std::size_t indexOffsetInQuery = 0; - for (Archetype* archetype : QueryArchetypes(sys->entityQuery)) { + for (Archetype* archetype : archetypes) { auto [chunkEntity, chunkCmpts, sizes] = archetype->Locate(sys->entityQuery.locator.AccessTypeIDs()); std::size_t num = archetype->EntityNum(); @@ -388,7 +393,8 @@ bool EntityMngr::GenEntityJob(World* w, Job* job, SystemFunc* sys) const { singletonsView, chunkEntity[i][j], indexOffsetInQueryChunk + j, - chunkView + chunkView, + &cb ); for (std::size_t k = 0; k < chunkCmpts[i].size(); k++) reinterpret_cast(chunkCmpts[i][k].p) += sizes[k]; @@ -397,6 +403,7 @@ bool EntityMngr::GenEntityJob(World* w, Job* job, SystemFunc* sys) const { indexOffsetInQuery += num; } + w->AddCommandBuffer(std::move(cb)); }; if (job) @@ -433,12 +440,15 @@ bool EntityMngr::GenChunkJob(World* w, Job* job, SystemFunc* sys) const { std::size_t idxOffsetInChunk = i * chunkCapacity; std::size_t indexOffsetInQueryChunk = indexOffsetInQuery + idxOffsetInChunk; job->emplace([=, singletons = singletons]() { + CommandBuffer cb; (*sys)( w, SingletonsView{ std::span{singletons.data(), singletons.size()} }, indexOffsetInQueryChunk, - archetype->chunks[i] + archetype->chunks[i], + &cb ); + w->AddCommandBuffer(std::move(cb)); }); } @@ -449,6 +459,7 @@ bool EntityMngr::GenChunkJob(World* w, Job* job, SystemFunc* sys) const { auto work = [this, w, sys, singletons = std::move(singletons)]() { SingletonsView singletonsView{ std::span{singletons.data(), singletons.size()} }; + CommandBuffer cb; std::size_t indexOffsetInQuery = 0; for (Archetype* archetype : QueryArchetypes(sys->entityQuery)) { std::size_t num = archetype->EntityNum(); @@ -465,10 +476,12 @@ bool EntityMngr::GenChunkJob(World* w, Job* job, SystemFunc* sys) const { w, singletonsView, indexOffsetInQueryChunk, - archetype->chunks[i] + archetype->chunks[i], + &cb ); } } + w->AddCommandBuffer(std::move(cb)); }; if (job) diff --git a/src/core/SystemFunc.cpp b/src/core/SystemFunc.cpp index a471c5c..b05b320 100644 --- a/src/core/SystemFunc.cpp +++ b/src/core/SystemFunc.cpp @@ -2,7 +2,7 @@ using namespace Ubpa::UECS; -void SystemFunc::operator()(World* w, SingletonsView singletonsView, Entity e, std::size_t entityIndexInQuery, CmptsView cmptsView) const { +void SystemFunc::operator()(World* w, SingletonsView singletonsView, Entity e, std::size_t entityIndexInQuery, CmptsView cmptsView, CommandBufferView cbv) const { assert(mode == Mode::Entity); return func( w, @@ -10,11 +10,12 @@ void SystemFunc::operator()(World* w, SingletonsView singletonsView, Entity e, s e, entityIndexInQuery, cmptsView, - {} + {}, + cbv ); } -void SystemFunc::operator()(World* w, SingletonsView singletonsView, std::size_t entityBeginIndexInQuery, ChunkView chunkView) const { +void SystemFunc::operator()(World* w, SingletonsView singletonsView, std::size_t entityBeginIndexInQuery, ChunkView chunkView, CommandBufferView cbv) const { assert(mode == Mode::Chunk); return func( w, @@ -22,7 +23,8 @@ void SystemFunc::operator()(World* w, SingletonsView singletonsView, std::size_t Entity::Invalid(), entityBeginIndexInQuery, {}, - chunkView + chunkView, + cbv ); } @@ -34,6 +36,7 @@ void SystemFunc::operator()(World* w, SingletonsView singletonsView) const { Entity::Invalid(), static_cast(-1), {}, + {}, {} ); } diff --git a/src/core/World.cpp b/src/core/World.cpp index 8c0e317..72ed8c4 100644 --- a/src/core/World.cpp +++ b/src/core/World.cpp @@ -44,16 +44,6 @@ void World::Update() { for (const auto& ID : systemMngr.GetActiveSystemIDs()) systemMngr.Update(ID, schedule); - for (auto& [layer, scheduleCommands] : schedule.commandBuffer) { - auto& worldCommands = commandBuffer[layer]; - for (auto& command : scheduleCommands) { - worldCommands.emplace_back([this, command = std::move(command)](){ - command(this); - }); - } - } - schedule.commandBuffer.clear(); - // 3. generate job graph auto* table = schedule.CreateFrameObject> @@ -344,13 +334,19 @@ void World::Accept(IListener* listener) const { void World::AddCommand(std::function command, int layer) { assert(inRunningJobGraph); std::lock_guard guard(commandBufferMutex); - commandBuffer[layer].push_back(std::move(command)); + commandBuffer.AddCommand(command, layer); +} + +void World::AddCommandBuffer(CommandBuffer cb) { + assert(inRunningJobGraph); + std::lock_guard guard(commandBufferMutex); + commandBuffer.AddCommandBuffer(std::move(cb)); } void World::RunCommands() { - for (const auto& [layer, commands] : commandBuffer) { + for (const auto& [layer, commands] : commandBuffer.GetCommands()) { for (const auto& command : commands) command(); } - commandBuffer.clear(); + commandBuffer.Clear(); } diff --git a/src/test/23_command_buffer/CMakeLists.txt b/src/test/23_command_buffer/CMakeLists.txt new file mode 100644 index 0000000..9c1502e --- /dev/null +++ b/src/test/23_command_buffer/CMakeLists.txt @@ -0,0 +1,8 @@ +Ubpa_AddTarget( + TEST + RET_TARGET_NAME tname + MODE EXE + LIB + Ubpa::UECS_core +) +target_precompile_headers(${tname} REUSE_FROM UECS_core) diff --git a/src/test/23_command_buffer/main.cpp b/src/test/23_command_buffer/main.cpp new file mode 100644 index 0000000..c0dce82 --- /dev/null +++ b/src/test/23_command_buffer/main.cpp @@ -0,0 +1,44 @@ +#include + +using namespace Ubpa; +using namespace Ubpa::UECS; + +struct Position { float val; }; +struct Velocity { float val; }; + +struct MoverSystem { + static void OnUpdate(Schedule& schedule) { + schedule.RegisterEntityJob( + [](const Velocity* v, Position* p) { + std::cout << "Mover" << std::endl; + p->val += v->val; + }, + "Mover" + ); + schedule.RegisterEntityJob( + [](World* w, Entity e, CommandBufferView cbv) { + std::cout << "Attach Velocity" << std::endl; + cbv->AddCommand([w, e]() { + w->entityMngr.Attach(e, TypeIDs_of); + w->entityMngr.WriteComponent(e)->val = 1.f; + }, 0); + }, + "Attach Velocity", + Schedule::EntityJobConfig { + .archetypeFilter = { + .all = { AccessTypeID_of> }, + .none = { TypeID_of }, + } + } + ); + } +}; + +int main() { + World w; + w.entityMngr.cmptTraits.Register(); + w.systemMngr.RegisterAndActivate(); + w.entityMngr.Create(TypeIDs_of); + w.Update(); + w.Update(); +}