OSSIA
Open Scenario System for Interactive Application
tick_methods.hpp
1 #pragma once
2 #include <ossia/audio/audio_tick.hpp>
3 #include <ossia/dataflow/execution_state.hpp>
4 #include <ossia/dataflow/graph/graph_interface.hpp>
5 #include <ossia/dataflow/graph_node.hpp>
6 #include <ossia/detail/hash_map.hpp>
7 #include <ossia/detail/pod_vector.hpp>
8 #include <ossia/editor/scenario/execution_log.hpp>
9 #include <ossia/editor/scenario/scenario.hpp>
11 
12 #if defined(SCORE_BENCHMARK)
13 #if __has_include(<valgrind/callgrind.h>)
14 #include <QFile>
15 #include <QTextStream>
16 
17 #include <valgrind/callgrind.h>
18 namespace ossia
19 {
20 
21 struct cycle_count_bench
22 {
23  ossia::double_vector& m_tickDurations;
24  uint64_t rdtsc()
25  {
26  unsigned int lo = 0;
27  unsigned int hi = 0;
28  __asm__ __volatile__(
29  "lfence\n"
30  "rdtsc\n"
31  "lfence"
32  : "=a"(lo), "=d"(hi));
33  return ((uint64_t)hi << 32) | lo;
34  }
35 
36  uint64_t t0;
37 
38  cycle_count_bench(ossia::double_vector& v)
39  : m_tickDurations{v}
40  , t0{rdtsc()}
41  {
42  }
43 
44  ~cycle_count_bench()
45  {
46  auto t1 = rdtsc();
47  m_tickDurations.push_back(t1 - t0);
48  }
49 };
50 
51 struct clock_count_bench
52 {
53  ossia::double_vector& m_tickDurations;
54  std::chrono::time_point<std::chrono::steady_clock> t0;
55 
56  clock_count_bench(ossia::double_vector& v)
57  : m_tickDurations{v}
58  , t0{std::chrono::steady_clock::now()}
59  {
60  }
61 
62  ~clock_count_bench()
63  {
64  auto t1 = std::chrono::steady_clock::now();
65  m_tickDurations.push_back(
66  std::chrono::duration_cast<std::chrono::nanoseconds>(t1 - t0).count());
67  }
68 };
69 struct callgrind_bench
70 {
71  callgrind_bench() { CALLGRIND_START_INSTRUMENTATION; }
72  ~callgrind_bench() { CALLGRIND_STOP_INSTRUMENTATION; }
73 };
74 }
75 #endif
76 #endif
77 
78 namespace ossia
79 {
80 
81 struct tick_all_nodes
82 {
83  ossia::execution_state& e;
84  ossia::graph_interface& g;
85 
86  void operator()(const ossia::audio_tick_state& st) { (*this)(st.frames, st.seconds); }
87 
88  void operator()(unsigned long samples, double) const
89  {
90  std::atomic_thread_fence(std::memory_order_seq_cst);
91  e.begin_tick();
92  const time_value old_date{e.samples_since_start};
93  e.samples_since_start += samples;
94  const time_value new_date{e.samples_since_start};
95 
96  // TODO tempo / sig ?
97  for(auto& node : g.get_nodes())
98  node->request(
99  token_request{old_date, new_date, 0_tv, 0_tv, 1.0, {}, ossia::root_tempo});
100 
101  g.state(e);
102  std::atomic_thread_fence(std::memory_order_seq_cst);
103  e.commit();
104  }
105 };
106 
107 // 1 tick per buffer
108 struct buffer_tick
109 {
110  ossia::execution_state& st;
111  ossia::graph_interface& g;
112  ossia::scenario& scenar;
113  ossia::transport_info_fun transport;
114 
115  void operator()(const ossia::audio_tick_state& st) { (*this)(st.frames, st.seconds); }
116 
117  void operator()(unsigned long frameCount, double seconds)
118  {
119  auto& itv = **scenar.get_time_intervals().begin();
120 #if defined(OSSIA_EXECUTION_LOG)
121  auto log = g_exec_log.start_tick();
122 #endif
123 
124  std::atomic_thread_fence(std::memory_order_seq_cst);
125  st.begin_tick();
126  st.samples_since_start += frameCount;
127  st.bufferSize = (int)frameCount;
128  // we could run a syscall and call now() but that's a bit more costly.
129  st.cur_date = seconds * 1e9;
130 
131  const auto flicks = frameCount * st.samplesToModelRatio;
132 
133  ossia::token_request tok{};
134  tok.prev_date = scenar.last_date();
135 
136  // FIXME this is a bit ugly..
137  if(tok.prev_date == ossia::Infinite)
138  tok.prev_date = 0_tv;
139 
140  tok.date = tok.prev_date + flicks;
141 
142  // Notify the current transport state
143  if(transport.allocated())
144  {
145  transport(itv.current_transport_info());
146  }
147 
148  // Temporal tick
149  {
150 #if defined(OSSIA_EXECUTION_LOG)
151  auto log = g_exec_log.start_temporal();
152 #endif
153 
154  scenar.state_impl(tok);
155  }
156 
157  // Dataflow execution
158  {
159 #if defined(OSSIA_EXECUTION_LOG)
160  auto log = g_exec_log.start_dataflow();
161 #endif
162 
163  g.state(st);
164  }
165 
166  std::atomic_thread_fence(std::memory_order_seq_cst);
167 
168  // Apply messages
169  {
170 #if defined(OSSIA_EXECUTION_LOG)
171  auto log = g_exec_log.start_commit();
172 #endif
173 
174  st.commit();
175  }
176 
177 #if defined(OSSIA_SCENARIO_DATAFLOW)
178  // Clear the scenario token
179  {
180  scenar.node->requested_tokens.clear();
181  }
182 #endif
183  }
184 };
185 
186 // 1 tick per sample
187 struct precise_score_tick
188 {
189  ossia::execution_state& st;
190  ossia::graph_interface& g;
192  ossia::transport_info_fun transport;
193 
194  void operator()(const ossia::audio_tick_state& st) { (*this)(st.frames, st.seconds); }
195 
196  void operator()(unsigned long frameCount, double seconds)
197  {
198  std::atomic_thread_fence(std::memory_order_seq_cst);
199  st.bufferSize = 1;
200  st.cur_date = seconds * 1e9;
201  for(std::size_t i = 0; i < frameCount; i++)
202  {
203  st.begin_tick();
204  st.samples_since_start++;
205  const ossia::token_request tok{};
206  itv.tick_offset(ossia::time_value{1}, 0_tv, tok);
207  g.state(st);
208  std::atomic_thread_fence(std::memory_order_seq_cst);
209  st.commit();
210 
211  st.advance_tick(1);
212  std::atomic_thread_fence(std::memory_order_seq_cst);
213  }
214  }
215 };
216 
217 /*
218 struct split_score_tick
219 {
220 public:
221  split_score_tick(
222  ossia::execution_state& a, ossia::graph_interface& b,
223  ossia::time_interval& c)
224  : st{a}, g{b}, itv{c}
225  {
226  }
227 
228  ossia::execution_state& st;
229  ossia::graph_interface& g;
230  ossia::time_interval& itv;
231  ossia::transport_info_fun transport;
232 
233  static void do_cuts(
234  ossia::flat_set<int64_t>& cuts, token_request_vec& tokens,
235  time_value cur_date)
236  {
237  for (auto it = tokens.begin(); it != tokens.end(); ++it)
238  {
239  if (it->date > cur_date)
240  {
241  auto token_end_offset = it->offset + abs(it->date - cur_date);
242  auto start_it = cuts.upper_bound(it->offset);
243  while (start_it != cuts.end() && (*start_it) < token_end_offset)
244  {
245  auto cut = *start_it;
246  auto N = cut - it->offset;
247  auto inserted_token = *it;
248 
249  // make first token shorter
250  it->date = cur_date + N;
251 
252  // make next token
253  inserted_token.offset = cut;
254  it = tokens.insert(it, inserted_token);
255 
256  ++start_it;
257  }
258  }
259 
260  cur_date = it->date;
261  }
262  }
263 
264  void cut(ossia::graph_interface& g)
265  {
266  cuts.clear();
267  requests.clear();
268  for (const auto& node : g.get_nodes())
269  {
270  for (const auto& tk : node->requested_tokens)
271  {
272  cuts.insert(tk.offset.impl);
273  cuts.insert((tk.offset + abs(tk.date - tk.prev_date)).impl);
274  }
275  }
276 
277  for (auto& node : g.get_nodes())
278  {
279  if (!node->requested_tokens.empty())
280  {
281  do_cuts(
282  cuts, node->requested_tokens,
283  node->requested_tokens.front().prev_date);
284  auto it
285  = requests.insert({node, {std::move(node->requested_tokens), {}}});
286  it.first->second.second
287  = it.first->second.first
288  .begin(); // set iterator to begin() of token requests
289  node->requested_tokens.clear();
290  }
291  }
292  for (auto& cut : cuts)
293  {
294  st.begin_tick();
295 
296  for (auto& node : g.get_nodes())
297  {
298  auto& req = requests[node];
299  if (req.second != req.first.end() && req.second->offset == cut)
300  {
301  node->request(*req.second);
302  ++req.second;
303  }
304  }
305 
306  g.state(st);
307  (st.*Commit)();
308  }
309  }
310 
311  void operator()(const ossia::audio_tick_state& st)
312  {
313  (*this)(st.frames, st.seconds);
314  }
315 
316  void operator()(unsigned long frameCount, double seconds)
317  {
318  st.samples_since_start += frameCount;
319  st.bufferSize = (int)frameCount;
320  // we could run a syscall and call now() but that's a bit more costly.
321  st.cur_date = seconds * 1e9;
322  const ossia::token_request tok{};
323  itv.tick_offset(ossia::time_value{int64_t(frameCount)}, 0_tv, tok);
324 
325  cut(g);
326  }
327 
328 private:
329  ossia::flat_set<int64_t> cuts;
330  ossia::hash_map<
331  const ossia::graph_node*,
332  std::pair<ossia::token_request_vec, ossia::token_request_vec::iterator>>
333  requests;
334 };
335 */
336 #if defined(SCORE_BENCHMARK)
337 template <typename BaseTick>
338 struct benchmark_score_tick
339 {
340  BaseTick base;
341  ossia::double_vector m_tickDurations;
342 
343  void operator()(const ossia::audio_tick_state& st) { (*this)(st.frames, st.seconds); }
344 
345  void operator()(unsigned long frameCount, double seconds)
346  {
347  cycle_count_bench bench{m_tickDurations};
348  base(frameCount, seconds);
349  }
350  benchmark_score_tick() { m_tickDurations.reserve(100000); }
351  ~benchmark_score_tick()
352  {
353  QFile f("/tmp/out.data");
354  QTextStream s(&f);
355  f.open(QIODevice::WriteOnly);
356  for(auto t : m_tickDurations)
357  s << t << "\n";
358  }
359 };
360 #endif
361 }
The time_interval class.
Definition: time_interval.hpp:49
Definition: git_info.h:7
The time_value class.
Definition: ossia/editor/scenario/time_value.hpp:28