OSSIA
Open Scenario System for Interactive Application
graph.hpp
1 #pragma once
2 #include <ossia/dataflow/graph/graph_interface.hpp>
3 #include <ossia/dataflow/graph/graph_utils.hpp>
4 
5 namespace ossia
6 {
7 
8 class OSSIA_EXPORT graph final
9  : public graph_util
10  , public graph_base
11 {
12 public:
13  template <typename Comp_T>
14  static void tick(
15  graph& g, execution_state& e, std::vector<graph_node*>& active_nodes,
16  Comp_T&& comp)
17  {
18  std::size_t executed = 0;
19  while(executed != active_nodes.size())
20  {
21  // Find all the nodes for which the inlets have executed
22  // (or without cables on the inlets)
23 
24  auto end = active_nodes.end();
25  auto cur_it = end;
26  for(auto it = active_nodes.begin(); it != end - executed; ++it)
27  {
28  auto node = *it;
29  if(cur_it != end)
30  {
31  if(!comp(*cur_it, node) && can_execute(*node, e))
32  cur_it = it;
33  }
34  else
35  {
36  if(can_execute(*node, e))
37  {
38  cur_it = it;
39  }
40  }
41  }
42 
43  if(cur_it != end)
44  {
45  g.exec_node(**cur_it, e);
46 
47  std::iter_swap(end - executed - 1, cur_it);
48  executed++;
49  }
50  else
51  {
52  break; // nothing more to execute
53  }
54  }
55  }
56 
57  template <typename Comp_T>
58  static void tick(
59  graph& g, execution_state& e, std::vector<graph_node*>& active_nodes,
60  Comp_T&& comp, ossia::logger_type& log)
61  {
62  std::size_t executed = 0;
63  while(executed != active_nodes.size())
64  {
65  // Find all the nodes for which the inlets have executed
66  // (or without cables on the inlets)
67 
68  auto end = active_nodes.end();
69  auto cur_it = end;
70  for(auto it = active_nodes.begin(); it != end - executed; ++it)
71  {
72  auto node = *it;
73  if(cur_it != end)
74  {
75  if(!comp(*cur_it, node) && can_execute(*node, e))
76  cur_it = it;
77  }
78  else
79  {
80  if(can_execute(*node, e))
81  {
82  cur_it = it;
83  }
84  }
85  }
86 
87  if(cur_it != end)
88  {
89  ossia::graph_node& node = **cur_it;
90  if(!node.logged())
91  g.exec_node(node, e);
92  else
93  g.exec_node(node, e, log);
94 
95  std::iter_swap(end - executed - 1, cur_it);
96  executed++;
97  }
98  else
99  {
100  break; // nothing more to execute
101  }
102  }
103  }
104  /*
105  template<typename Comp_T>
106  static void tick_ok(
107  graph_base& g,
108  execution_state& e,
109  std::vector<graph_node*>& active_nodes,
110  Comp_T&& comp)
111  {
112  while (!active_nodes.empty())
113  {
114  // Find all the nodes for which the inlets have executed
115  // (or without cables on the inlets)
116  const auto end = active_nodes.end();
117  auto cur_it = end;
118  for(auto it = active_nodes.begin(); it != end; ++it)
119  {
120  auto node = *it;
121  if(can_execute(*node, e))
122  {
123  if(cur_it != end)
124  {
125  if(!comp(*cur_it, node))
126  cur_it = it;
127  }
128  else
129  {
130  cur_it = it;
131  }
132  }
133  }
134 
135  if (cur_it != end)
136  {
137  g.exec_node(**cur_it, e);
138  active_nodes.erase(cur_it);
139  }
140  else
141  {
142  break; // nothing more to execute
143  }
144  }
145  }*/
146 
147  /*
148  template<typename Comp_T>
149  static void tick_slow(
150  graph_base& g,
151  execution_state& e,
152  std::vector<graph_node*>& active_nodes,
153  Comp_T&& comp)
154  {
155  auto it = active_nodes.begin();
156  auto end_it = active_nodes.end();
157 
158  while (it != end_it)
159  {
160  // Find all the nodes for which the inlets have executed
161  // (or without cables on the inlets)
162  auto cur_it = end_it;
163  for(auto sub_it = it; sub_it != end_it; ++sub_it)
164  {
165  ossia::graph_node* node = *sub_it;
166  if(can_execute(*node, e))
167  {
168  if(cur_it != end_it)
169  {
170  if(!comp(*cur_it, node))
171  cur_it = sub_it;
172  }
173  else
174  {
175  cur_it = sub_it;
176  }
177  }
178  }
179 
180  if (cur_it != end_it)
181  {
182  g.exec_node(**cur_it, e);
183  std::iter_swap(it, cur_it);
184  ++it;
185  }
186  else
187  {
188  break; // nothing more to execute
189  }
190  }
191  }
192  */
193 
194  /*
195  void get_sorted_nodes(const graph_t& gr)
196  {
197  // Get a total order on nodes
198  m_active_nodes.clear();
199  m_active_nodes.reserve(m_nodes.size());
200 
201  // TODO this should be doable with a single vector
202  if(m_topo_dirty)
203  {
204  m_topo_order_cache.clear();
205  m_topo_order_cache.reserve(m_nodes.size());
206  boost::topological_sort(gr, std::back_inserter(m_topo_order_cache));
207  m_topo_dirty = false;
208  }
209 
210  for(auto vtx : m_topo_order_cache)
211  {
212  auto node = gr[vtx].get();
213  if(node->enabled())
214  m_active_nodes.push_back(node);
215  }
216  }
217  */
218 
219  struct simple_topo_sort
220  {
221  simple_topo_sort(const graph_t& g)
222  : impl{g}
223  {
224  }
225  const graph_t& impl;
226  std::vector<graph_vertex_t> m_topo_order_cache;
227  std::vector<graph_node*> m_node_cache;
228  void operator()(const graph_t& gr, std::vector<graph_node*>& nodes)
229  {
230  const auto N = boost::num_vertices(impl);
231  m_topo_order_cache.clear();
232  m_topo_order_cache.reserve(N);
233  boost::topological_sort(gr, std::back_inserter(m_topo_order_cache));
234 
235  nodes.clear();
236  nodes.reserve(N);
237  for(auto vtx : m_topo_order_cache)
238  {
239  nodes.push_back(gr[vtx].get());
240  }
241  }
242  };
243 
244  void sort_nodes()
245  {
246  assert(sort_fun);
247  assert(m_nodes.size() == boost::num_vertices(m_graph));
248 
249  sort_fun(m_graph, m_node_static_sort);
250  }
251 
252  void get_enabled_nodes(const graph_t& gr)
253  {
254  m_active_nodes.clear();
255  m_active_nodes.reserve(m_nodes.size());
256 
257  assert(m_node_static_sort.size() == boost::num_vertices(gr));
258  for(auto node : m_node_static_sort)
259  {
260  if(node->enabled())
261  m_active_nodes.push_back(node);
262  }
263  }
264 
265 public:
266  ~graph() override;
267 
268  void state(execution_state& e) override
269  {
270  try
271  {
272  // TODO in the future, temporal_graph, space_graph that can be used as
273  // processes.
274  if(m_dirty)
275  {
276  sort_nodes();
277  m_dirty = false;
278  }
279 
280  // Filter disabled nodes (through strict relationships).
281  m_enabled_cache.clear();
282  m_enabled_cache.reserve(m_nodes.size());
283 
284  for(auto it = boost::vertices(m_graph).first;
285  it != boost::vertices(m_graph).second; ++it)
286  {
287  ossia::graph_node& ptr = *m_graph[*it];
288  if(ptr.enabled())
289  {
290  m_enabled_cache.insert(&ptr);
291  }
292  }
293 
294  disable_strict_nodes_rec(m_enabled_cache, m_disabled_cache);
295 
296  // Start executing the nodes
297  get_enabled_nodes(m_graph);
298  if(!logger)
299  tick(*this, e, m_active_nodes, node_sorter{m_node_static_sort, e});
300  else
301  tick(*this, e, m_active_nodes, node_sorter{m_node_static_sort, e}, *logger);
302 
303  finish_nodes(m_nodes);
304  }
305  catch(const boost::not_a_dag&)
306  {
307  ossia::logger().error("Execution graph is not a DAG.");
308  return;
309  }
310  }
311 
312  [[nodiscard]] const graph_t& impl() const { return m_graph; }
313  std::function<void(const graph_t& gr, std::vector<graph_node*>& nodes)> sort_fun{
314  simple_topo_sort{m_graph}};
315 
316  std::shared_ptr<ossia::logger_type> logger;
317 
318 private:
319  void print(std::ostream& stream) override { print_graph(m_graph, stream); }
320 
321  node_flat_set m_enabled_cache;
322  node_flat_set m_disabled_cache;
323  std::vector<graph_node*> m_active_nodes;
324  std::vector<graph_node*> m_node_static_sort;
325 
326  friend struct inlet;
327  friend struct outlet;
328  friend class ::DataflowTest;
329 };
330 }
Definition: git_info.h:7
spdlog::logger & logger() noexcept
Where the errors will be logged. Default is stderr.
Definition: context.cpp:104
std::ostream & print(std::ostream &out, const state_element &e)
print Print a state_element
Definition: state_element.cpp:23