graph: sync point
This commit is contained in:
@@ -225,7 +225,19 @@ others tasks.
|
||||
|
||||
## Sync points
|
||||
|
||||
To be done. Coming soon.
|
||||
Sometimes it's useful to assign the role of _sync point_ to a node.<br/>
|
||||
Whether it accesses new resources or is simply a watershed, the procedure for
|
||||
assigning this role to a vertex is always the same: first it's tied to the flow
|
||||
builder, then the `sync` function is invoked:
|
||||
|
||||
```cpp
|
||||
builder.bind("sync_point").sync();
|
||||
```
|
||||
|
||||
The choice to assign an _identity_ to this type of nodes lies in the fact that,
|
||||
more often than not, they also perform operations on resources.<br/>
|
||||
If this isn't the case, it will still be possible to create no-op vertices to
|
||||
which empty tasks are assigned.
|
||||
|
||||
## Execution graph
|
||||
|
||||
|
||||
@@ -32,6 +32,16 @@ class basic_flow {
|
||||
using ro_rw_container_type = std::vector<std::pair<std::size_t, bool>, typename alloc_traits::template rebind_alloc<std::pair<std::size_t, bool>>>;
|
||||
using deps_container_type = dense_map<id_type, ro_rw_container_type, identity, std::equal_to<id_type>, typename alloc_traits::template rebind_alloc<std::pair<const id_type, ro_rw_container_type>>>;
|
||||
|
||||
void emplace(const id_type res, const bool is_rw) {
|
||||
ENTT_ASSERT(index.first() < vertices.size(), "Invalid node");
|
||||
|
||||
if(!deps.contains(res) && sync_on != vertices.size()) {
|
||||
deps[res].emplace_back(sync_on, true);
|
||||
}
|
||||
|
||||
deps[res].emplace_back(index.first(), is_rw);
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Allocator type. */
|
||||
using allocator_type = Allocator;
|
||||
@@ -51,7 +61,8 @@ public:
|
||||
explicit basic_flow(const allocator_type &allocator)
|
||||
: index{0u, allocator},
|
||||
vertices{},
|
||||
deps{} {}
|
||||
deps{},
|
||||
sync_on{} {}
|
||||
|
||||
/*! @brief Default copy constructor. */
|
||||
basic_flow(const basic_flow &) = default;
|
||||
@@ -64,7 +75,8 @@ public:
|
||||
basic_flow(const basic_flow &other, const allocator_type &allocator)
|
||||
: index{other.index.first(), allocator},
|
||||
vertices{other.vertices, allocator},
|
||||
deps{other.deps, allocator} {}
|
||||
deps{other.deps, allocator},
|
||||
sync_on{other.sync_on} {}
|
||||
|
||||
/*! @brief Default move constructor. */
|
||||
basic_flow(basic_flow &&) noexcept = default;
|
||||
@@ -77,7 +89,8 @@ public:
|
||||
basic_flow(basic_flow &&other, const allocator_type &allocator)
|
||||
: index{other.index.first(), allocator},
|
||||
vertices{std::move(other.vertices), allocator},
|
||||
deps{std::move(other.deps), allocator} {}
|
||||
deps{std::move(other.deps), allocator},
|
||||
sync_on{other.sync_on} {}
|
||||
|
||||
/**
|
||||
* @brief Default copy assignment operator.
|
||||
@@ -110,7 +123,7 @@ public:
|
||||
|
||||
/*! @brief Clears the flow builder. */
|
||||
void clear() noexcept {
|
||||
index.first() = 0u;
|
||||
index.first() = sync_on = {};
|
||||
vertices.clear();
|
||||
deps.clear();
|
||||
}
|
||||
@@ -124,6 +137,7 @@ public:
|
||||
std::swap(index, other.index);
|
||||
std::swap(vertices, other.vertices);
|
||||
std::swap(deps, other.deps);
|
||||
std::swap(sync_on, other.sync_on);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -140,19 +154,34 @@ public:
|
||||
* @return This flow builder.
|
||||
*/
|
||||
basic_flow &bind(const id_type value) {
|
||||
sync_on += (sync_on == vertices.size());
|
||||
const auto it = vertices.emplace(value).first;
|
||||
index.first() = size_type(it - vertices.begin());
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Turns the current task into a sync point.
|
||||
* @return This flow builder.
|
||||
*/
|
||||
basic_flow& sync() {
|
||||
ENTT_ASSERT(index.first() < vertices.size(), "Invalid node");
|
||||
sync_on = index.first();
|
||||
|
||||
for(const auto &elem: deps) {
|
||||
elem.second.emplace_back(sync_on, true);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns a read-only resource to the current task.
|
||||
* @param res Resource identifier.
|
||||
* @return This flow builder.
|
||||
*/
|
||||
basic_flow &ro(const id_type res) {
|
||||
ENTT_ASSERT(index.first() < vertices.size(), "Invalid task");
|
||||
deps[res].emplace_back(index.first(), false);
|
||||
emplace(res, false);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -167,7 +196,7 @@ public:
|
||||
std::enable_if_t<std::is_same_v<std::remove_const_t<typename std::iterator_traits<It>::value_type>, id_type>, basic_flow &>
|
||||
ro(It first, It last) {
|
||||
for(; first != last; ++first) {
|
||||
ro(*first);
|
||||
emplace(*first, false);
|
||||
}
|
||||
|
||||
return *this;
|
||||
@@ -179,8 +208,7 @@ public:
|
||||
* @return This flow builder.
|
||||
*/
|
||||
basic_flow &rw(const id_type res) {
|
||||
ENTT_ASSERT(index.first() < vertices.size(), "Invalid task");
|
||||
deps[res].emplace_back(index.first(), true);
|
||||
emplace(res, true);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -195,7 +223,7 @@ public:
|
||||
std::enable_if_t<std::is_same_v<std::remove_const_t<typename std::iterator_traits<It>::value_type>, id_type>, basic_flow &>
|
||||
rw(It first, It last) {
|
||||
for(; first != last; ++first) {
|
||||
rw(*first);
|
||||
emplace(*first, true);
|
||||
}
|
||||
|
||||
return *this;
|
||||
@@ -279,6 +307,7 @@ private:
|
||||
compressed_pair<size_type, allocator_type> index;
|
||||
task_container_type vertices;
|
||||
deps_container_type deps;
|
||||
size_type sync_on;
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
@@ -215,6 +215,49 @@ TEST(Flow, Graph) {
|
||||
ASSERT_EQ(it, last);
|
||||
}
|
||||
|
||||
TEST(Flow, Sync) {
|
||||
using namespace entt::literals;
|
||||
|
||||
entt::flow flow{};
|
||||
|
||||
flow.bind("task_0"_hs)
|
||||
.ro("resource_0"_hs);
|
||||
|
||||
flow.bind("task_1"_hs)
|
||||
.rw("resource_1"_hs);
|
||||
|
||||
flow.bind("task_2"_hs)
|
||||
.sync();
|
||||
|
||||
flow.bind("task_3"_hs)
|
||||
.ro("resource_0"_hs)
|
||||
.rw("resource_2"_hs);
|
||||
|
||||
flow.bind("task_4"_hs)
|
||||
.ro("resource_2"_hs);
|
||||
|
||||
auto graph = flow.graph();
|
||||
|
||||
ASSERT_EQ(flow.size(), 5u);
|
||||
ASSERT_EQ(flow.size(), graph.size());
|
||||
|
||||
ASSERT_EQ(flow[0u], "task_0"_hs);
|
||||
ASSERT_EQ(flow[1u], "task_1"_hs);
|
||||
ASSERT_EQ(flow[2u], "task_2"_hs);
|
||||
ASSERT_EQ(flow[3u], "task_3"_hs);
|
||||
ASSERT_EQ(flow[4u], "task_4"_hs);
|
||||
|
||||
auto it = graph.edges().cbegin();
|
||||
const auto last = graph.edges().cend();
|
||||
|
||||
ASSERT_NE(it, last);
|
||||
ASSERT_EQ(*it++, std::make_pair(std::size_t{0u}, std::size_t{2u}));
|
||||
ASSERT_EQ(*it++, std::make_pair(std::size_t{1u}, std::size_t{2u}));
|
||||
ASSERT_EQ(*it++, std::make_pair(std::size_t{2u}, std::size_t{3u}));
|
||||
ASSERT_EQ(*it++, std::make_pair(std::size_t{3u}, std::size_t{4u}));
|
||||
ASSERT_EQ(it, last);
|
||||
}
|
||||
|
||||
TEST(Flow, ThrowingAllocator) {
|
||||
using allocator = test::throwing_allocator<entt::id_type>;
|
||||
using task_allocator = test::throwing_allocator<std::pair<std::size_t, entt::id_type>>;
|
||||
|
||||
Reference in New Issue
Block a user