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