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