OSSIA
Open Scenario System for Interactive Application
Loading...
Searching...
No Matches
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.bufferSize = (int)samples;
94 e.begin_tick();
95 const time_value old_date{e.samples_since_start};
96 e.samples_since_start += samples;
97 const time_value new_date{e.samples_since_start};
98
99 // TODO tempo / sig ?
100 token_request tok{old_date, new_date, 0_tv, 0_tv, 1.0, {}, ossia::root_tempo};
101 tok.start_sample = 0;
102 tok.length_sample = int32_t(samples);
103 for(auto& node : g.get_nodes())
104 node->request(tok);
105
106 g.state(e);
107 std::atomic_thread_fence(std::memory_order_seq_cst);
108 e.commit();
109 }
110};
111
112// 1 tick per buffer
113struct buffer_tick
114{
115 ossia::execution_state& st;
116 ossia::graph_interface& g;
117 ossia::scenario& scenar;
118 ossia::transport_info_fun transport;
119
120 void operator()(const ossia::audio_tick_state& st) { (*this)(st.frames, st.seconds); }
121
122 void operator()(unsigned long frameCount, double seconds)
123 {
124 auto& itv = **scenar.get_time_intervals().begin();
125#if defined(OSSIA_EXECUTION_LOG)
126 auto log = g_exec_log.start_tick();
127#endif
128
129 ossia::disable_fpe();
130 std::atomic_thread_fence(std::memory_order_seq_cst);
131 st.bufferSize = (int)frameCount;
132 st.begin_tick();
133 st.samples_since_start += frameCount;
134 // we could run a syscall and call now() but that's a bit more costly.
135 st.cur_date = seconds * 1e9;
136
137 const auto flicks = frameCount * st.samplesToModelRatio;
138
139 ossia::token_request tok{};
140 tok.prev_date = scenar.last_date();
141
142 // FIXME this is a bit ugly..
143 if(tok.prev_date == ossia::Infinite)
144 tok.prev_date = 0_tv;
145
146 tok.date = tok.prev_date + flicks;
147
148 // This is the one thing the audio callback knows for certain: the buffer
149 // is frameCount samples. Everything downstream carries cuts of this span
150 // rather than reconstructing them from the flick-quantised model dates.
151 tok.start_sample = 0;
152 tok.length_sample = int32_t(frameCount);
153
154 // Notify the current transport state
155 if(transport.allocated())
156 {
157 transport(itv.current_transport_info());
158 }
159
160 // Temporal tick
161 {
162#if defined(OSSIA_EXECUTION_LOG)
163 auto log = g_exec_log.start_temporal();
164#endif
165
166 scenar.state_impl(tok);
167 }
168
169 // Dataflow execution
170 {
171#if defined(OSSIA_EXECUTION_LOG)
172 auto log = g_exec_log.start_dataflow();
173#endif
174
175 g.state(st);
176 }
177
178 std::atomic_thread_fence(std::memory_order_seq_cst);
179
180 // Apply messages
181 {
182#if defined(OSSIA_EXECUTION_LOG)
183 auto log = g_exec_log.start_commit();
184#endif
185
186 st.commit();
187 }
188
189#if defined(OSSIA_SCENARIO_DATAFLOW)
190 // Clear the scenario token
191 {
192 scenar.node->requested_tokens.clear();
193 }
194#endif
195 }
196};
197
198// 1 tick per sample
199struct precise_score_tick
200{
201 ossia::execution_state& st;
202 ossia::graph_interface& g;
204 ossia::transport_info_fun transport;
205
206 void operator()(const ossia::audio_tick_state& st) { (*this)(st.frames, st.seconds); }
207
208 void operator()(unsigned long frameCount, double seconds)
209 {
210 ossia::disable_fpe();
211 std::atomic_thread_fence(std::memory_order_seq_cst);
212 st.bufferSize = 1;
213 st.cur_date = seconds * 1e9;
214 for(std::size_t i = 0; i < frameCount; i++)
215 {
216 st.begin_tick();
217 st.samples_since_start++;
218 const ossia::token_request tok{};
219 itv.tick_offset(ossia::time_value{1}, 0_tv, tok, 0, 1);
220 g.state(st);
221 std::atomic_thread_fence(std::memory_order_seq_cst);
222 st.commit();
223
224 st.advance_tick(1);
225 std::atomic_thread_fence(std::memory_order_seq_cst);
226 }
227 }
228};
229
230/*
231struct split_score_tick
232{
233public:
234 split_score_tick(
235 ossia::execution_state& a, ossia::graph_interface& b,
236 ossia::time_interval& c)
237 : st{a}, g{b}, itv{c}
238 {
239 }
240
241 ossia::execution_state& st;
242 ossia::graph_interface& g;
243 ossia::time_interval& itv;
244 ossia::transport_info_fun transport;
245
246 static void do_cuts(
247 ossia::flat_set<int64_t>& cuts, token_request_vec& tokens,
248 time_value cur_date)
249 {
250 for (auto it = tokens.begin(); it != tokens.end(); ++it)
251 {
252 if (it->date > cur_date)
253 {
254 auto token_end_offset = it->offset + abs(it->date - cur_date);
255 auto start_it = cuts.upper_bound(it->offset);
256 while (start_it != cuts.end() && (*start_it) < token_end_offset)
257 {
258 auto cut = *start_it;
259 auto N = cut - it->offset;
260 auto inserted_token = *it;
261
262 // make first token shorter
263 it->date = cur_date + N;
264
265 // make next token
266 inserted_token.offset = cut;
267 it = tokens.insert(it, inserted_token);
268
269 ++start_it;
270 }
271 }
272
273 cur_date = it->date;
274 }
275 }
276
277 void cut(ossia::graph_interface& g)
278 {
279 cuts.clear();
280 requests.clear();
281 for (const auto& node : g.get_nodes())
282 {
283 for (const auto& tk : node->requested_tokens)
284 {
285 cuts.insert(tk.offset.impl);
286 cuts.insert((tk.offset + abs(tk.date - tk.prev_date)).impl);
287 }
288 }
289
290 for (auto& node : g.get_nodes())
291 {
292 if (!node->requested_tokens.empty())
293 {
294 do_cuts(
295 cuts, node->requested_tokens,
296 node->requested_tokens.front().prev_date);
297 auto it
298 = requests.insert({node, {std::move(node->requested_tokens), {}}});
299 it.first->second.second
300 = it.first->second.first
301 .begin(); // set iterator to begin() of token requests
302 node->requested_tokens.clear();
303 }
304 }
305 for (auto& cut : cuts)
306 {
307 st.begin_tick();
308
309 for (auto& node : g.get_nodes())
310 {
311 auto& req = requests[node];
312 if (req.second != req.first.end() && req.second->offset == cut)
313 {
314 node->request(*req.second);
315 ++req.second;
316 }
317 }
318
319 g.state(st);
320 (st.*Commit)();
321 }
322 }
323
324 void operator()(const ossia::audio_tick_state& st)
325 {
326 (*this)(st.frames, st.seconds);
327 }
328
329 void operator()(unsigned long frameCount, double seconds)
330 {
331 ossia::disable_fpe();
332 st.samples_since_start += frameCount;
333 st.bufferSize = (int)frameCount;
334 // we could run a syscall and call now() but that's a bit more costly.
335 st.cur_date = seconds * 1e9;
336 const ossia::token_request tok{};
337 itv.tick_offset(ossia::time_value{int64_t(frameCount)}, 0_tv, tok);
338
339 cut(g);
340 }
341
342private:
343 ossia::flat_set<int64_t> cuts;
344 ossia::hash_map<
345 const ossia::graph_node*,
346 std::pair<ossia::token_request_vec, ossia::token_request_vec::iterator>>
347 requests;
348};
349*/
350#if defined(SCORE_BENCHMARK)
351template <typename BaseTick>
352struct benchmark_score_tick
353{
354 BaseTick base;
355 ossia::double_vector m_tickDurations;
356
357 void operator()(const ossia::audio_tick_state& st) { (*this)(st.frames, st.seconds); }
358
359 void operator()(unsigned long frameCount, double seconds)
360 {
361 cycle_count_bench bench{m_tickDurations};
362 base(frameCount, seconds);
363 }
364 benchmark_score_tick() { m_tickDurations.reserve(100000); }
365 ~benchmark_score_tick()
366 {
367 QFile f("/tmp/out.data");
368 QTextStream s(&f);
369 f.open(QIODevice::WriteOnly);
370 for(auto t : m_tickDurations)
371 s << t << "\n";
372 }
373};
374#endif
375}
The time_interval class.
Definition time_interval.hpp:49
void tick_offset(ossia::time_value, ossia::time_value offset, const ossia::token_request &parent_request, int32_t start_sample=-1, int32_t length_sample=-1)
Definition time_interval.cpp:272
Definition git_info.h:7
The time_value class.
Definition ossia/editor/scenario/time_value.hpp:30