]> git.seodisparate.com - EntityComponentMetaSystem/commitdiff
Add ptr versions of forMatchingSignature fns
authorStephen Seo <seo.disparate@gmail.com>
Tue, 14 Nov 2017 04:11:32 +0000 (13:11 +0900)
committerStephen Seo <seo.disparate@gmail.com>
Tue, 14 Nov 2017 04:11:32 +0000 (13:11 +0900)
src/EC/Manager.hpp
src/test/ECTest.cpp

index 15ff27c0b60187ca0e96af9e8edabfc6372062c9..5557c9734392bc2f688c18fac354b462d16cd5f3 100644 (file)
@@ -514,6 +514,18 @@ namespace EC
                 );
             }
 
+            template <typename CType, typename Function>
+            static void callPtr(
+                const std::size_t& entityID,
+                CType& ctype,
+                Function* function)
+            {
+                (*function)(
+                    entityID,
+                    ctype.template getEntityData<Types>(entityID)...
+                );
+            }
+
             template <typename CType, typename Function>
             void callInstance(
                 const std::size_t& entityID,
@@ -525,6 +537,18 @@ namespace EC
                     ctype,
                     std::forward<Function>(function));
             }
+
+            template <typename CType, typename Function>
+            void callInstancePtr(
+                const std::size_t& entityID,
+                CType& ctype,
+                Function* function) const
+            {
+                ForMatchingSignatureHelper<Types...>::callPtr(
+                    entityID,
+                    ctype,
+                    function);
+            }
         };
 
     public:
@@ -632,6 +656,112 @@ namespace EC
             }
         }
 
+        /*!
+            \brief Calls the given function on all Entities matching the given
+                Signature.
+
+            The function pointer given to this function must accept std::size_t
+            as its first parameter and Component references for the rest of the
+            parameters. Tags specified in the Signature are only used as
+            filters and will not be given as a parameter to the function.
+
+            The second parameter is default 1 (not multi-threaded). If the
+            second parameter threadCount is set to a value greater than 1, then
+            threadCount threads will be used.
+            Note that multi-threading is based on splitting the task of calling
+            the function across sections of entities. Thus if there are only
+            a small amount of entities in the manager, then using multiple
+            threads may not have as great of a speed-up.
+
+            Example:
+            \code{.cpp}
+                auto function = [] (std::size_t ID, C0& component0,
+                    C1& component1) {
+                    // Lambda function contents here
+                };
+                manager.forMatchingSignaturePtr<TypeList<C0, C1, T0>>(
+                    &function, // ptr
+                    4 // four threads
+                );
+            \endcode
+            Note, the ID given to the function is not permanent. An entity's ID
+            may change when cleanup() is called.
+        */
+        template <typename Signature, typename Function>
+        void forMatchingSignaturePtr(Function* function,
+            std::size_t threadCount = 1)
+        {
+            using SignatureComponents =
+                typename EC::Meta::Matching<Signature, ComponentsList>::type;
+            using Helper =
+                EC::Meta::Morph<
+                    SignatureComponents,
+                    ForMatchingSignatureHelper<> >;
+
+            BitsetType signatureBitset =
+                BitsetType::template generateBitset<Signature>();
+            if(threadCount <= 1)
+            {
+                for(std::size_t i = 0; i < currentSize; ++i)
+                {
+                    if(!std::get<bool>(entities[i]))
+                    {
+                        continue;
+                    }
+
+                    if((signatureBitset & std::get<BitsetType>(entities[i]))
+                        == signatureBitset)
+                    {
+                        Helper::callPtr(i, *this, function);
+                    }
+                }
+            }
+            else
+            {
+                std::vector<std::thread> threads(threadCount);
+                std::size_t s = currentSize / threadCount;
+                for(std::size_t i = 0; i < threadCount; ++i)
+                {
+                    std::size_t begin = s * i;
+                    std::size_t end;
+                    if(i == threadCount - 1)
+                    {
+                        end = currentSize;
+                    }
+                    else
+                    {
+                        end = s * (i + 1);
+                    }
+                    threads[i] = std::thread([this, &function, &signatureBitset]
+                            (std::size_t begin,
+                            std::size_t end) {
+                        for(std::size_t i = begin; i < end; ++i)
+                        {
+                            if(!std::get<bool>(this->entities[i]))
+                            {
+                                continue;
+                            }
+
+                            if((signatureBitset
+                                    & std::get<BitsetType>(entities[i]))
+                                == signatureBitset)
+                            {
+                                Helper::callPtr(i, *this, function);
+                            }
+                        }
+                    },
+                        begin,
+                        end);
+                }
+                for(std::size_t i = 0; i < threadCount; ++i)
+                {
+                    threads[i].join();
+                }
+            }
+        }
+
+
+
     private:
         std::unordered_map<std::size_t, std::function<void(std::size_t)> >
             forMatchingFunctions;
@@ -712,7 +842,7 @@ namespace EC
                             & std::get<BitsetType>(this->entities[i]))
                                 == signatureBitset)
                         {
-                            helper.callInstance(i, *this, function);
+                            helper.callInstancePtr(i, *this, &function);
                         }
                     }
                 }
@@ -746,7 +876,7 @@ namespace EC
                                     & std::get<BitsetType>(this->entities[i]))
                                         == signatureBitset)
                                 {
-                                    helper.callInstance(i, *this, function);
+                                    helper.callInstancePtr(i, *this, &function);
                                 }
                             }
                         },
@@ -1151,6 +1281,190 @@ namespace EC
             });
         }
 
+        /*!
+            \brief Call multiple functions with mulitple signatures on all
+                living entities.
+
+            (Living entities as in entities that have not been marked for
+            deletion.)
+
+            Note that this function requires the tuple of functions to hold
+            pointers to functions, not just functions.
+
+            This function requires the first template parameter to be a
+            EC::Meta::TypeList of signatures. Note that a signature is a
+            EC::Meta::TypeList of components and tags, meaning that SigList
+            is a TypeList of TypeLists.
+
+            The second template parameter can be inferred from the function
+            parameter which should be a tuple of functions. The function
+            at any index in the tuple should match with a signature of the
+            same index in the SigList. Behavior is undefined if there are
+            less functions than signatures.
+
+            See the Unit Test of this function in src/test/ECTest.cpp for
+            usage examples.
+
+            This function was created for the use case where there are many
+            entities in the system which can cause multiple calls to
+            forMatchingSignature to be slow due to the overhead of iterating
+            through the entire list of entities on each invocation.
+            This function instead iterates through all entities once,
+            storing matching entities in a vector of vectors (for each
+            signature and function pair) and then calling functions with
+            the matching list of entities.
+
+            Note that multi-threaded or not, functions will be called in order
+            of signatures. The first function signature pair will be called
+            first, then the second, third, and so on.
+            If this function is called with more than 1 thread specified, then
+            the order of entities called is not guaranteed. Otherwise entities
+            will be called in consecutive order by their ID.
+        */
+        template <typename SigList, typename FuncTuple>
+        void forMatchingSignaturesPtr(FuncTuple funcTuple,
+            std::size_t threadCount = 1)
+        {
+            std::vector<std::vector<std::size_t> > multiMatchingEntities(
+                SigList::size);
+            BitsetType signatureBitsets[SigList::size];
+
+            // generate bitsets for each signature
+            EC::Meta::forEach<SigList>(
+            [this, &signatureBitsets] (auto signature) {
+                signatureBitsets[
+                        EC::Meta::IndexOf<decltype(signature), SigList>{} ] =
+                    BitsetType::template generateBitset<decltype(signature)>();
+            });
+
+            // find and store entities matching signatures
+            if(threadCount <= 1)
+            {
+                for(std::size_t eid = 0; eid < currentSize; ++eid)
+                {
+                    if(!isAlive(eid))
+                    {
+                        continue;
+                    }
+                    for(std::size_t i = 0; i < SigList::size; ++i)
+                    {
+                        if((signatureBitsets[i]
+                            & std::get<BitsetType>(entities[eid]))
+                                == signatureBitsets[i])
+                        {
+                            multiMatchingEntities[i].push_back(eid);
+                        }
+                    }
+                }
+            }
+            else
+            {
+                std::vector<std::thread> threads(threadCount);
+                std::size_t s = currentSize / threadCount;
+                std::mutex sigsMutexes[SigList::size];
+                for(std::size_t i = 0; i < threadCount; ++i)
+                {
+                    std::size_t begin = s * i;
+                    std::size_t end;
+                    if(i == threadCount - 1)
+                    {
+                        end = currentSize;
+                    }
+                    else
+                    {
+                        end = s * (i + 1);
+                    }
+                    threads[i] = std::thread(
+                    [this, &signatureBitsets, &multiMatchingEntities,
+                        &sigsMutexes]
+                    (std::size_t begin, std::size_t end) {
+                        for(std::size_t eid = begin; eid < end; ++eid)
+                        {
+                            if(!isAlive(eid))
+                            {
+                                continue;
+                            }
+                            for(std::size_t i = 0; i < SigList::size; ++i)
+                            {
+                                if((signatureBitsets[i]
+                                    & std::get<BitsetType>(entities[eid]))
+                                        == signatureBitsets[i])
+                                {
+                                    std::lock_guard<std::mutex> guard(
+                                        sigsMutexes[i]);
+                                    multiMatchingEntities[i].push_back(eid);
+
+                                }
+                            }
+                        }
+                    },
+                    begin, end);
+                }
+                for(std::size_t i = 0; i < threadCount; ++i)
+                {
+                    threads[i].join();
+                }
+            }
+
+            // call functions on matching entities
+            EC::Meta::forEach<SigList>(
+            [this, &multiMatchingEntities, &funcTuple, &threadCount]
+            (auto signature) {
+                using SignatureComponents =
+                    typename EC::Meta::Matching<
+                        decltype(signature), ComponentsList>::type;
+                using Helper =
+                    EC::Meta::Morph<
+                        SignatureComponents,
+                        ForMatchingSignatureHelper<> >;
+                using Index = EC::Meta::IndexOf<decltype(signature),
+                    SigList>;
+                constexpr std::size_t index = Index{};
+                if(threadCount <= 1)
+                {
+                    for(auto iter = multiMatchingEntities[index].begin();
+                        iter != multiMatchingEntities[index].end(); ++iter)
+                    {
+                        Helper::callPtr(*iter, *this,
+                            std::get<index>(funcTuple));
+                    }
+                }
+                else
+                {
+                    std::vector<std::thread> threads(threadCount);
+                    std::size_t s = multiMatchingEntities[index].size()
+                        / threadCount;
+                    for(std::size_t i = 0; i < threadCount; ++i)
+                    {
+                        std::size_t begin = s * i;
+                        std::size_t end;
+                        if(i == threadCount - 1)
+                        {
+                            end = multiMatchingEntities[index].size();
+                        }
+                        else
+                        {
+                            end = s * (i + 1);
+                        }
+                        threads[i] = std::thread(
+                        [this, &multiMatchingEntities, &funcTuple]
+                        (std::size_t begin, std::size_t end)
+                        {
+                            for(std::size_t j = begin; j < end; ++j)
+                            {
+                                Helper::callPtr(multiMatchingEntities[index][j],
+                                    *this, std::get<index>(funcTuple));
+                            }
+                        }, begin, end);
+                    }
+                    for(std::size_t i = 0; i < threadCount; ++i)
+                    {
+                        threads[i].join();
+                    }
+                }
+            });
+        }
+
         /*!
             \brief Resets the Manager, removing all entities.
 
index 2cf3b1106333deabf6015b58420a0719225ab080..ca084ffe853234ca067e5a59c19c8745c644ecf2 100644 (file)
@@ -791,3 +791,113 @@ TEST(EC, ForMatchingSignatures)
     }
 }
 
+TEST(EC, forMatchingPtrs)
+{
+    EC::Manager<ListComponentsAll, ListTagsAll> manager;
+
+    std::size_t e[] = {
+        manager.addEntity(),
+        manager.addEntity(),
+        manager.addEntity(),
+        manager.addEntity(),
+        manager.addEntity(),
+        manager.addEntity(),
+        manager.addEntity()
+    };
+
+    auto& first = e[0];
+    auto& last = e[6];
+
+    for(auto eid : e)
+    {
+        if(eid != first && eid != last)
+        {
+            manager.addComponent<C0>(eid);
+            manager.addComponent<C1>(eid);
+        }
+        else
+        {
+            manager.addComponent<C0>(eid);
+            manager.addTag<T0>(eid);
+        }
+    }
+
+    const auto func0 = [] (std::size_t eid, C0& c0, C1& c1)
+    {
+        c0.x = 1;
+        c0.y = 2;
+        c1.vx = 3;
+        c1.vy = 4;
+    };
+    const auto func1 = [] (std::size_t eid, C0& c0)
+    {
+        c0.x = 11;
+        c0.y = 12;
+    };
+
+    using namespace EC::Meta;
+
+    manager.forMatchingSignaturePtr<TypeList<C0, C1> >(
+        &func0
+    );
+    manager.forMatchingSignaturePtr<TypeList<C0, T0> >(
+        &func1
+    );
+
+    for(auto eid : e)
+    {
+        if(eid != first && eid != last)
+        {
+            C0& c0 = manager.getEntityData<C0>(eid);
+            EXPECT_EQ(1, c0.x);
+            EXPECT_EQ(2, c0.y);
+            c0.x = 0;
+            c0.y = 0;
+            C1& c1 = manager.getEntityData<C1>(eid);
+            EXPECT_EQ(3, c1.vx);
+            EXPECT_EQ(4, c1.vy);
+            c1.vx = 0;
+            c1.vy = 0;
+        }
+        else
+        {
+            C0& c = manager.getEntityData<C0>(eid);
+            EXPECT_EQ(11, c.x);
+            EXPECT_EQ(12, c.y);
+            c.x = 0;
+            c.y = 0;
+        }
+    }
+
+    manager.forMatchingSignaturesPtr<TypeList<
+        TypeList<C0, C1>,
+        TypeList<C0, T0> > >(
+        std::make_tuple(&func0, &func1)
+    );
+
+    for(auto eid : e)
+    {
+        if(eid != first && eid != last)
+        {
+            C0& c0 = manager.getEntityData<C0>(eid);
+            EXPECT_EQ(1, c0.x);
+            EXPECT_EQ(2, c0.y);
+            c0.x = 0;
+            c0.y = 0;
+            C1& c1 = manager.getEntityData<C1>(eid);
+            EXPECT_EQ(3, c1.vx);
+            EXPECT_EQ(4, c1.vy);
+            c1.vx = 0;
+            c1.vy = 0;
+        }
+        else
+        {
+            C0& c = manager.getEntityData<C0>(eid);
+            EXPECT_EQ(11, c.x);
+            EXPECT_EQ(12, c.y);
+            c.x = 0;
+            c.y = 0;
+        }
+    }
+}
+