• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ossia / score / 38475390298

13 Oct 2026 09:56PM UTC coverage: 48.898% (+0.8%) from 48.076%
38475390298

push

github

jcelerier
gfx: commit the initial batch before a frame can release what it names

RenderList::renderImpl carried m_initialBatch, holding every renderer's
init()-time uploads, into the frame's first pass. When an inlet spec changed
before that first frame, the surgical render-target update released and
re-initialised the renderer first, and releaseState() deletes its material
UBO outright, so the batch then named a freed QRhiBuffer. On OpenGL,
enqueueResourceUpdates copies a uniform-buffer update into the buffer's
CPU-side store: a write into freed memory, or, after destroy() cleared it,
into QByteArray's static empty storage. test_gfx_input_sampler_filter
(raw raster, OpenGL) crashed there, or read back black, depending on what
reused the allocation.

The batch is now committed to the command buffer as soon as the frame takes
it, as onEdgeRemoved() and Graph::relinkGraph() already flush it before
releasing renderer state.

The new case in test_gfx_outside_frame_release_buffer re-inits a probe
renderer on the first frame and checks, from its releaseState(), whether the
batch its upload went into still holds it: deterministic, on every backend.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JeL19ofVgPe9u9q7J1V9ex

2 of 2 new or added lines in 1 file covered. (100.0%)

6331 existing lines in 89 files now uncovered.

160459 of 328149 relevant lines covered (48.9%)

146726.48 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

62.71
/src/plugins/score-lib-process/Process/ExecutionSetup.cpp
1
#include <State/Address.hpp>
2

3
#include <Process/Dataflow/Cable.hpp>
4
#include <Process/Dataflow/Port.hpp>
5
#include <Process/ExecutionContext.hpp>
6
#include <Process/ExecutionFunctions.hpp>
7
#include <Process/ExecutionSetup.hpp>
8
#include <Process/ExecutionTransaction.hpp>
9
#include <Process/Process.hpp>
10

11
#include <LocalTree/ScriptableProcessComponent.hpp>
12

13
#include <QPointer>
14
#include <QTimer>
15

16
#include <ossia/dataflow/execution_state.hpp>
17
#include <ossia/dataflow/for_each_port.hpp>
18
#include <ossia/dataflow/graph/graph_interface.hpp>
19
#include <ossia/dataflow/graph_edge.hpp>
20
#include <ossia/dataflow/graph_node.hpp>
21
#include <ossia/dataflow/port.hpp>
22
#include <ossia/editor/scenario/time_process.hpp>
23
#include <ossia/network/common/destination_qualifiers.hpp>
24

25
namespace Execution
26
{
27

28
namespace
29
{
30
struct ContextEnqueuer
31
{
32
  SetupContext& self;
33
  template <typename F>
34
  void operator()(F&& f)
3,505 ✔
35
  {
36
    static_assert(std::is_nothrow_move_constructible_v<F>);
37
    OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
3,505 ✔
38
    self.context.executionQueue.enqueue(std::move(f));
3,505 ✔
39
  }
3,505 ✔
40
};
41
struct VectorEnqueuer
42
{
43
  Transaction& vec;
44
  template <typename F>
45
  void operator()(F&& f)
1,824 ✔
46
  {
47
    static_assert(std::is_nothrow_move_constructible_v<F>);
48
    OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
1,824 ✔
49
    vec.push_back(std::move(f));
1,824 ✔
50
  }
1,824 ✔
51
};
52

53
// Changed from lambda to explicit struct to improve debuggability
54
static auto enqueue_in_context(SetupContext& self) noexcept
1,097 ✔
55
{
56
  return ContextEnqueuer{self};
1,097 ✔
57
}
58

59
static auto enqueue_in_vector(Transaction& vec) noexcept
1,050 ✔
60
{
61
  return VectorEnqueuer{vec};
1,050 ✔
62
}
63
}
64
ossia::net::node_base*
65
findNode(const ossia::execution_state& st, const State::Address& addr)
3,578 ✔
66
{
67
  auto& devs = st.edit_devices();
3,578 ✔
68
  auto dev_p = ossia::find_if(
3,578 ✔
69
      devs, [d = addr.device.toStdString()](auto& dev) { return dev->get_name() == d; });
7,662 ✔
70
  if(dev_p == devs.end())
3,578 ✔
71
    return nullptr;
17 ✔
72
  return ossia::net::find_node(
3,561 ✔
73
      (*dev_p)->get_root_node(), addr.path.join("/").toStdString());
3,561 ✔
74
}
3,578 ✔
75

76
std::optional<ossia::destination> makeDestination(
25 ✔
77
    const ossia::execution_state& devices, const State::AddressAccessor& addr)
78
{
79
  auto n = findNode(devices, addr.address);
25 ✔
80
  if(!n)
25 ✔
81
    return {};
13 ✔
82

83
  auto p = n->get_parameter();
12 ✔
84
  if(!p)
12 ✔
85
    return {};
×
86

87
  auto& qual = addr.qualifiers.get();
12 ✔
88
  return ossia::destination{*p, qual.accessors, qual.unit};
12 ✔
89
}
25 ✔
90

91
template <typename Impl>
92
void SetupContext::disconnect_cable_impl(const Process::Cable& c, Impl&& impl)
103 ✔
93
{
94
  if(!context.created) {
103 ✔
95
    return;
82 ✔
96
  }
97
  auto it = m_cables.find(c.id());
21 ✔
98
  if(it != m_cables.end())
21 ✔
99
  {
100
    impl([cable = it->second, graph = context.execGraph] {
41 ✔
101
      OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
20 ✔
102
      graph->disconnect(cable);
20 ✔
103
    });
20 ✔
104
    // The edge is gone from the graph: a later connectCable() for the same
105
    // id makes a new one, and whoever asks whether the cable is wired up
106
    // (a node rebuilding its ports) must not find this one.
107
    m_cables.erase(it);
21 ✔
108
  }
21 ✔
109
}
103 ✔
110

111
template <typename Impl>
112
void SetupContext::connect_cable_impl(Process::Cable& cable, Impl&& impl)
118 ✔
113
{
114
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
118 ✔
115
  if(!context.created) {
118 ✔
116
    return;
76 ✔
117
  }
118
  ossia::node_ptr source_node{}, sink_node{};
42 ✔
119
  ossia::outlet_ptr source_port{};
42 ✔
120
  ossia::inlet_ptr sink_port{};
42 ✔
121
  if(auto port_src = cable.source().try_find(context.doc))
42 ✔
122
  {
123
    auto it = outlets.find(port_src);
42 ✔
124
    if(it != outlets.end())
42 ✔
125
    {
126
      source_node = it->second.first;
42 ✔
127
      source_port = it->second.second;
42 ✔
128
    }
42 ✔
129
  }
42 ✔
130
  if(auto port_snk = cable.sink().try_find(context.doc))
42 ✔
131
  {
132
    auto it = inlets.find(port_snk);
42 ✔
133
    if(it != inlets.end())
42 ✔
134
    {
135
      sink_node = it->second.first;
42 ✔
136
      sink_port = it->second.second;
42 ✔
137
    }
42 ✔
138
  }
42 ✔
139

140
  if(source_node && sink_node && source_port && sink_port)
42 ✔
141
  {
142
    ossia::edge_ptr edge;
42 ✔
143
    switch(cable.type())
42 ✔
144
    {
145
      case Process::CableType::ImmediateStrict: {
146
        edge = context.execGraph->allocate_edge(
×
147
            ossia::immediate_strict_connection{}, std::move(source_port),
×
148
            std::move(sink_port), std::move(source_node), std::move(sink_node));
149
        break;
150
      }
151
      case Process::CableType::ImmediateGlutton: {
152
        edge = context.execGraph->allocate_edge(
84 ✔
153
            ossia::immediate_glutton_connection{}, std::move(source_port),
42 ✔
154
            std::move(sink_port), std::move(source_node), std::move(sink_node));
42 ✔
155
        break;
42 ✔
156
      }
157
      case Process::CableType::DelayedStrict: {
158
        edge = context.execGraph->allocate_edge(
×
159
            ossia::delayed_strict_connection{}, std::move(source_port),
×
160
            std::move(sink_port), std::move(source_node), std::move(sink_node));
161
        break;
162
      }
163
      case Process::CableType::DelayedGlutton: {
164
        edge = context.execGraph->allocate_edge(
×
165
            ossia::delayed_glutton_connection{}, std::move(source_port),
×
166
            std::move(sink_port), std::move(source_node), std::move(sink_node));
167
        break;
168
      }
169
    }
170

171
    m_cables[cable.id()] = edge;
42 ✔
172
    impl([edge, graph = context.execGraph]() mutable {
84 ✔
173
      OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
42 ✔
174
      graph->connect(std::move(edge));
42 ✔
175
    });
42 ✔
176
  }
42 ✔
177
}
118 ✔
178

179
// Pushed once, when the node is registered; the address machinery never touches
180
// `type` afterwards.
181
template <typename T, typename Impl>
182
static void set_declared_unit_impl(
4,142 ✔
183
    const Process::Port& proc_port, const T& port, Impl&& append)
184
{
185
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
4,142 ✔
186
  const ossia::unit_t& u = proc_port.unit().get();
4,142 ✔
187
  if(!u)
4,142 ✔
188
    return;
4,125 ✔
189

190
  append([port, u] {
34 ✔
191
    OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
17 ✔
192
    if(ossia::value_port* dat = port->template target<ossia::value_port>())
17 ✔
193
    {
194
      // A unit the node declared is more precise than the widget's and wins; a
195
      // plain value type does not.
196
      if(!dat->type.target<ossia::unit_t>())
17 ✔
197
        dat->type = u;
×
198
    }
17 ✔
199
  });
17 ✔
200
}
4,142 ✔
201

202
// Without an explicit address, a scriptable port uses its published parameter
203
static State::AddressAccessor effectiveAddress(const Process::Port& port)
4,214 ✔
204
{
205
  if(port.address().isSet())
4,214 ✔
206
    return port.address();
192 ✔
207
  return State::AddressAccessor{LocalTree::scriptableAddress(port)};
4,022 ✔
208
}
4,214 ✔
209

210
template <typename Port_T, typename OssiaPort_T, typename Impl>
211
void SetupContext::bind_address(
4,142 ✔
212
    Port_T& proc_port, const OssiaPort_T& ossia_port,
213
    RegisteredPorts::connections& cons, Impl&& impl)
214
{
215
  for(auto& con : cons)
4,142 ✔
216
    QObject::disconnect(con);
×
217
  cons.clear();
4,142 ✔
218

219
  // The ossia port is looked up when this runs: a queued call can arrive after
220
  // a program change has replaced the one the port was bound with.
221
  auto rebind = [this, port = QPointer<Process::Port>{&proc_port}] {
4,170 ✔
222
    OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
28 ✔
223
    if(port)
28 ✔
224
      this->rebind(port.data());
27 ✔
225
  };
28 ✔
226
  cons.push_back(connect(&proc_port, &Process::Port::addressChanged, this, rebind));
4,142 ✔
227
  // Queued: the namespace updates the published address on these signals too
228
  cons.push_back(connect(
8,284 ✔
229
      &proc_port, &Process::Port::scriptableChanged, this, rebind, Qt::QueuedConnection));
4,142 ✔
230
  cons.push_back(connect(
8,284 ✔
231
      &proc_port, &Process::Port::exposedChanged, this, rebind, Qt::QueuedConnection));
4,142 ✔
232
  if(auto proc = Process::parentProcess(&proc_port))
4,142 ✔
233
    cons.push_back(connect(
7,092 ✔
234
        proc, &Process::ProcessModel::scriptableChanged, this, rebind,
3,546 ✔
235
        Qt::QueuedConnection));
236
  follow_published();
4,142 ✔
237
  set_declared_unit_impl(proc_port, ossia_port, impl);
4,142 ✔
238

239
  // Also registers the port with the execution state once the address resolves.
240
  set_destination_impl(context, effectiveAddress(proc_port), ossia_port, impl);
4,142 ✔
241
}
4,142 ✔
242

243
template <typename Impl>
244
void SetupContext::register_inlet_impl(
3,285 ✔
245
    Process::Inlet& proc_port, const ossia::inlet_ptr& ossia_port,
246
    const std::shared_ptr<ossia::graph_node>& node, Impl&& impl)
247
{
248
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
3,285 ✔
249
  SCORE_ASSERT(node);
3,285 ✔
250
  SCORE_ASSERT(ossia_port);
3,285 ✔
251

252
  bind_address(proc_port, ossia_port, runtime_connections[node].inlets[proc_port.id()], impl);
3,285 ✔
253

254
  inlets.insert({&proc_port, std::make_pair(node, ossia_port)});
3,285 ✔
255
}
3,285 ✔
256

257
template <typename Impl>
258
void SetupContext::register_node_impl(
1,036 ✔
259
    const Process::Inlets& proc_inlets, const Process::Outlets& proc_outlets,
260
    const std::shared_ptr<ossia::graph_node>& node, Impl&& exec)
261
{
262
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
1,036 ✔
263
  if(node)
1,036 ✔
264
  {
265
    std::weak_ptr<ossia::graph_interface> wg = context.execGraph;
1,036 ✔
266
    exec([wg, node = node]() mutable {
2,072 ✔
267
      OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
1,036 ✔
268
      if(auto g = wg.lock())
2,072 ✔
269
        g->add_node(std::move(node));
1,036 ✔
270
    });
1,036 ✔
271

272
    const std::size_t proc_n_inlets = proc_inlets.size();
1,036 ✔
273
    const std::size_t proc_n_outlets = proc_outlets.size();
1,036 ✔
274

275
    const std::size_t ossia_n_inlets = node->root_inputs().size();
1,036 ✔
276
    const std::size_t ossia_n_outlets = node->root_outputs().size();
1,036 ✔
277

278
    std::size_t n_inlets = std::min(proc_n_inlets, ossia_n_inlets);
1,036 ✔
279
    std::size_t n_outlets = std::min(proc_n_outlets, ossia_n_outlets);
1,036 ✔
280

281
    for(std::size_t i = 0; i < n_inlets; i++)
2,886 ✔
282
    {
283
      register_inlet_impl(*proc_inlets[i], node->root_inputs()[i], node, exec);
1,850 ✔
284
    }
1,850 ✔
285

286
    for(std::size_t i = 0; i < n_outlets; i++)
1,776 ✔
287
    {
288
      register_outlet_impl(*proc_outlets[i], node->root_outputs()[i], node, exec);
740 ✔
289
    }
740 ✔
290
  }
1,036 ✔
291
}
1,036 ✔
292

293
void SetupContext::register_node(
404 ✔
294
    const Process::ProcessModel& proc, const std::shared_ptr<ossia::graph_node>& node)
295
{
296
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
404 ✔
297
  register_node(proc.inlets(), proc.outlets(), node);
404 ✔
298
  proc_map[node.get()] = &proc;
404 ✔
299
}
404 ✔
300

301
void SetupContext::unregister_node(
406 ✔
302
    const Process::ProcessModel& proc, const std::shared_ptr<ossia::graph_node>& node)
303
{
304
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
406 ✔
305
  unregister_node(proc.inlets(), proc.outlets(), node);
406 ✔
306
  proc_map.erase(node.get());
406 ✔
307
}
406 ✔
308

309
void SetupContext::register_node(
×
310
    const Process::ProcessModel& proc, const std::shared_ptr<ossia::graph_node>& node,
311
    Transaction& vec)
312
{
313
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
×
314
  register_node(proc.inlets(), proc.outlets(), node, vec);
×
315
  proc_map[node.get()] = &proc;
×
316
}
×
317

318
void SetupContext::unregister_node(
×
319
    const Process::ProcessModel& proc, const std::shared_ptr<ossia::graph_node>& node,
320
    Transaction& vec)
321
{
322
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
×
323
  unregister_node(proc.inlets(), proc.outlets(), node, vec);
×
324
  proc_map.erase(node.get());
×
325
}
×
326

327
template <typename T, typename Impl>
328
void set_destination_impl(
4,214 ✔
329
    const Context& plug, const State::AddressAccessor& address, const T& port,
330
    Impl&& append)
331
{
332
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
4,214 ✔
333
  if(plug.retired)
4,214 ✔
UNCOV
334
    return;
×
335
  auto& s = plug.execState;
4,214 ✔
336
  auto& g = plug.execGraph;
4,214 ✔
337
  if(!g)
4,214 ✔
UNCOV
338
    return;
×
339

340
  if(address.address.device.isEmpty())
4,214 ✔
341
  {
342
    append([ws = std::weak_ptr{s}, port, wg = std::weak_ptr{g}] {
7,574 ✔
343
      OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
3,785 ✔
344
      auto s = ws.lock();
3,785 ✔
345
      if(!s)
3,785 ✔
UNCOV
346
        return;
×
347
      auto g = wg.lock();
3,785 ✔
348
      if(!g)
3,785 ✔
UNCOV
349
        return;
×
350
      if(port->address)
3,785 ✔
351
      {
352
        s->unregister_port(*port);
7 ✔
353
        port->address = {};
7 ✔
354
        if(ossia::value_port* dat = port->template target<ossia::value_port>())
7 ✔
355
        {
356
          dat->address_unit = {};
7 ✔
357
          dat->index = {};
7 ✔
358
        }
7 ✔
359
        g->mark_dirty();
7 ✔
360
      }
7 ✔
361
    });
3,785 ✔
362
    return;
3,789 ✔
363
  }
364

365
  auto& qual = address.qualifiers.get();
425 ✔
366
  if(auto n = findNode(*plug.execState, address.address))
425 ✔
367
  {
368
    auto p = n->get_parameter();
424 ✔
369
    if(p)
424 ✔
370
    {
371
      append([ws = std::weak_ptr{s}, port, p, qual = qual, wg = std::weak_ptr{g}] {
838 ✔
372
        OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
414 ✔
373
        auto s = ws.lock();
414 ✔
374
        if(!s)
414 ✔
UNCOV
375
          return;
×
376
        auto g = wg.lock();
414 ✔
377
        if(!g)
414 ✔
UNCOV
378
          return;
×
379
        s->unregister_port(*port);
414 ✔
380
        port->address = p;
414 ✔
381
        if(ossia::value_port* dat = port->template target<ossia::value_port>())
414 ✔
382
        {
383
          // Unconditionally, like the index: dropping the qualifier has to give
384
          // the process's declaration back.
385
          dat->address_unit = qual.unit;
231 ✔
386
          dat->index = qual.accessors;
231 ✔
387
        }
231 ✔
388
        s->register_port(*port);
414 ✔
389
        g->mark_dirty();
414 ✔
390
      });
414 ✔
391
    }
424 ✔
392
    else
393
    {
394
      append([ws = std::weak_ptr{s}, n, port, wg = std::weak_ptr{g}] {
×
395
        auto s = ws.lock();
×
396
        if(!s)
×
397
          return;
×
398
        auto g = wg.lock();
×
399
        if(!g)
×
400
          return;
×
401
        OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
×
402
        s->unregister_port(*port);
×
403
        port->address = n;
×
404
        s->register_port(*port);
×
UNCOV
405
        g->mark_dirty();
×
UNCOV
406
      });
×
407
    }
408
  }
424 ✔
409
  else if(auto ad = address.address.toString_unsafe().toStdString();
1 ✔
410
          ossia::traversal::is_pattern(ad))
2 ✔
411
  {
412
    // OPTIMIZEME
UNCOV
413
    auto path = ossia::traversal::make_path(ad);
×
414
    if(path)
×
415
    {
416
      append([ws = std::weak_ptr{s}, p = *path, port, wg = std::weak_ptr{g}]() mutable {
×
417
        OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
×
418
        auto s = ws.lock();
×
419
        if(!s)
×
420
          return;
×
421
        auto g = wg.lock();
×
422
        if(!g)
×
423
          return;
×
424
        s->unregister_port(*port);
×
UNCOV
425
        port->address = std::move(p);
×
426
        if(ossia::value_port* dat = port->template target<ossia::value_port>())
×
427
        {
428
          dat->address_unit = {};
×
429
          dat->index.clear();
×
430
        }
×
431
        s->register_port(*port);
×
432
        g->mark_dirty();
×
UNCOV
433
      });
×
UNCOV
434
    }
×
435
    else
436
    {
437
      append([ws = std::weak_ptr{s}, n, port, wg = std::weak_ptr{g}] {
×
438
        OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
×
439
        auto s = ws.lock();
×
440
        if(!s)
×
441
          return;
×
442
        auto g = wg.lock();
×
443
        if(!g)
×
444
          return;
×
445
        s->unregister_port(*port);
×
UNCOV
446
        port->address = {};
×
447
        if(ossia::value_port* dat = port->template target<ossia::value_port>())
×
448
        {
449
          dat->address_unit = {};
×
450
          dat->index.clear();
×
451
        }
×
452
        s->register_port(*port);
×
UNCOV
453
        g->mark_dirty();
×
454
      });
×
455
    }
UNCOV
456
  }
×
457
}
4,214 ✔
458

459
void SetupContext::set_destination(
69 ✔
460
    const State::AddressAccessor& address, const ossia::inlet_ptr& port)
461
{
462
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
69 ✔
463
  set_destination_impl(context, address, port, enqueue_in_context(*this));
69 ✔
464
}
69 ✔
465

466
void SetupContext::set_destination(
3 ✔
467
    const State::AddressAccessor& address, const ossia::outlet_ptr& port)
468
{
469
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
3 ✔
470
  set_destination_impl(context, address, port, enqueue_in_context(*this));
3 ✔
471
}
3 ✔
472

UNCOV
473
void SetupContext::register_inlet(
×
474
    Process::Inlet& inlet, const ossia::inlet_ptr& exec,
475
    const std::shared_ptr<ossia::graph_node>& node)
476
{
477
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
×
478
  register_inlet_impl(inlet, exec, node, enqueue_in_context(*this));
×
UNCOV
479
}
×
UNCOV
480
void SetupContext::register_outlet(
×
481
    Process::Outlet& outlet, const ossia::outlet_ptr& exec,
482
    const std::shared_ptr<ossia::graph_node>& node)
483
{
484
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
×
UNCOV
485
  register_outlet_impl(outlet, exec, node, enqueue_in_context(*this));
×
UNCOV
486
}
×
487

488
void SetupContext::register_node(
856 ✔
489
    const Process::Inlets& proc_inlets, const Process::Outlets& proc_outlets,
490
    const std::shared_ptr<ossia::graph_node>& node)
491
{
492
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
856 ✔
493
  register_node_impl(proc_inlets, proc_outlets, node, enqueue_in_context(*this));
856 ✔
494
}
856 ✔
495

496
void SetupContext::register_node(
180 ✔
497
    const Process::Inlets& proc_inlets, const Process::Outlets& proc_outlets,
498
    const std::shared_ptr<ossia::graph_node>& node, Transaction& vec)
499
{
500
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
180 ✔
501
  register_node_impl(proc_inlets, proc_outlets, node, enqueue_in_vector(vec));
180 ✔
502
}
180 ✔
503

504
void SetupContext::register_inlet(
701 ✔
505
    Process::Inlet& inlet, const ossia::inlet_ptr& exec,
506
    const std::shared_ptr<ossia::graph_node>& node, Transaction& vec)
507
{
508
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
701 ✔
509
  register_inlet_impl(inlet, exec, node, enqueue_in_vector(vec));
701 ✔
510
}
701 ✔
511
void SetupContext::register_outlet(
117 ✔
512
    Process::Outlet& outlet, const ossia::outlet_ptr& exec,
513
    const std::shared_ptr<ossia::graph_node>& node, Transaction& vec)
514
{
515
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
117 ✔
516
  register_outlet_impl(outlet, exec, node, enqueue_in_vector(vec));
117 ✔
517
}
117 ✔
518

519
void SetupContext::on_cableCreated(Process::Cable& c)
82 ✔
520
{
521
  if(context.retired)
82 ✔
UNCOV
522
    return;
×
523
  connectCable(c);
82 ✔
524
}
82 ✔
525

526
void SetupContext::on_cableRemoved(const Process::Cable& c)
87 ✔
527
{
528
  if(context.retired)
87 ✔
UNCOV
529
    return;
×
530
  removeCable(c);
87 ✔
531
}
87 ✔
532

533
void SetupContext::connectCable(Process::Cable& c)
82 ✔
534
{
535
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
82 ✔
536
  connect_cable_impl(c, enqueue_in_context(*this));
82 ✔
537
}
82 ✔
538

539
void SetupContext::connectCable(Process::Cable& c, Transaction& vec)
36 ✔
540
{
541
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
36 ✔
542
  connect_cable_impl(c, enqueue_in_vector(vec));
36 ✔
543
}
36 ✔
544

545
void SetupContext::removeCable(const Process::Cable& c)
87 ✔
546
{
547
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
87 ✔
548
  disconnect_cable_impl(c, enqueue_in_context(*this));
87 ✔
549
}
87 ✔
550

551
void SetupContext::removeCable(const Process::Cable& c, Transaction& vec)
16 ✔
552
{
553
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
16 ✔
554
  disconnect_cable_impl(c, enqueue_in_vector(vec));
16 ✔
555
}
16 ✔
556

557
template <typename Impl>
558
void SetupContext::register_outlet_impl(
857 ✔
559
    Process::Outlet& proc_port, const ossia::outlet_ptr& ossia_port,
560
    const std::shared_ptr<ossia::graph_node>& node, Impl&& impl)
561
{
562
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
857 ✔
563
  SCORE_ASSERT(node);
857 ✔
564
  SCORE_ASSERT(ossia_port);
857 ✔
565
  bind_address(
857 ✔
566
      proc_port, ossia_port, runtime_connections[node].outlets[proc_port.id()], impl);
857 ✔
567

568
  outlets.insert({&proc_port, std::make_pair(node, ossia_port)});
857 ✔
569

570
  proc_port.mapExecution(
1,714 ✔
571
      *ossia_port, [&](Process::Inlet& model_inl, ossia::inlet& ossia_inl) {
1,591 ✔
572
        register_inlet_impl(model_inl, &ossia_inl, node, impl);
734 ✔
573
      });
734 ✔
574

575
  // Unneeded : the execution_state only needs inlets to be registered,
576
  // in order to set up data value queues from the network thread
577

578
  // std::weak_ptr<ossia::execution_state> ws = context.execState;
579
  // impl([ws, ossia_port] {
580
  //   if (auto state = ws.lock())
581
  //     state->register_outlet(*ossia_port);
582
  // });
583
}
857 ✔
584

585
void SetupContext::unregister_inlet(
×
586
    const Process::Inlet& proc_port, const std::shared_ptr<ossia::graph_node>& node)
587
{
UNCOV
588
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
×
589
  if(node)
×
590
  {
591
    auto& runtime_connection = runtime_connections[node].inlets;
×
592
    auto it = runtime_connection.find(proc_port.id());
×
UNCOV
593
    if(it != runtime_connection.end())
×
594
    {
595
      for(auto& con : it->second)
×
UNCOV
596
        QObject::disconnect(con);
×
597
      runtime_connection.erase(it);
×
598
    }
×
599

600
    auto ossia_port_it = inlets.find(const_cast<Process::Inlet*>(&proc_port));
×
601
    if(ossia_port_it != inlets.end())
×
602
    {
UNCOV
603
      std::weak_ptr<ossia::execution_state> ws = context.execState;
×
604
      context.executionQueue.enqueue([ws, ossia_port = ossia_port_it->second.second] {
×
605
        OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
×
606
        if(auto state = ws.lock())
×
UNCOV
607
          state->unregister_port(*ossia_port);
×
UNCOV
608
      });
×
609

UNCOV
610
      inlets.erase(ossia_port_it);
×
611
    }
×
UNCOV
612
  }
×
613
  else
614
  {
UNCOV
615
    inlets.erase(const_cast<Process::Inlet*>(&proc_port));
×
616
  }
617
}
×
618

619
void SetupContext::unregister_outlet(
×
620
    const Process::Outlet& proc_port, const std::shared_ptr<ossia::graph_node>& node)
621
{
UNCOV
622
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
×
623
  if(node)
×
624
  {
625
    auto& runtime_connection = runtime_connections[node].outlets;
×
626
    auto it = runtime_connection.find(proc_port.id());
×
UNCOV
627
    if(it != runtime_connection.end())
×
628
    {
629
      for(auto& con : it->second)
×
630
        QObject::disconnect(con);
×
631
      runtime_connection.erase(it);
×
632
    }
×
633

634
    proc_port.forChildInlets([&](Process::Inlet& model_inl) {
×
635
      OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
×
UNCOV
636
      unregister_inlet(model_inl, node);
×
637
    });
×
UNCOV
638
  }
×
639

UNCOV
640
  outlets.erase(const_cast<Process::Outlet*>(&proc_port));
×
641
}
×
642

UNCOV
643
void SetupContext::unregister_inlet(
×
644
    const Process::Inlet& proc_port, const std::shared_ptr<ossia::graph_node>& node,
645
    Transaction& commands)
646
{
UNCOV
647
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
×
648
  if(node)
×
649
  {
650
    auto& runtime_connection = runtime_connections[node].inlets;
×
651
    auto it = runtime_connection.find(proc_port.id());
×
UNCOV
652
    if(it != runtime_connection.end())
×
653
    {
654
      for(auto& con : it->second)
×
UNCOV
655
        QObject::disconnect(con);
×
656
      runtime_connection.erase(it);
×
657
    }
×
658

659
    auto ossia_port_it = inlets.find(const_cast<Process::Inlet*>(&proc_port));
×
660
    if(ossia_port_it != inlets.end())
×
661
    {
UNCOV
662
      std::weak_ptr<ossia::execution_state> ws = context.execState;
×
663
      commands.push_back([ws, ossia_port = ossia_port_it->second.second] {
×
664
        OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
×
665
        if(auto state = ws.lock())
×
UNCOV
666
          state->unregister_port(*ossia_port);
×
UNCOV
667
      });
×
668

UNCOV
669
      inlets.erase(ossia_port_it);
×
670
    }
×
UNCOV
671
  }
×
672
  else
673
  {
UNCOV
674
    inlets.erase(const_cast<Process::Inlet*>(&proc_port));
×
675
  }
676
}
×
677

UNCOV
678
void SetupContext::unregister_outlet(
×
679
    const Process::Outlet& proc_port, const std::shared_ptr<ossia::graph_node>& node,
680
    Transaction& commands)
681
{
UNCOV
682
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
×
683
  if(node)
×
684
  {
685
    auto& runtime_connection = runtime_connections[node].outlets;
×
686
    auto it = runtime_connection.find(proc_port.id());
×
UNCOV
687
    if(it != runtime_connection.end())
×
688
    {
689
      for(auto& con : it->second)
×
690
        QObject::disconnect(con);
×
691
      runtime_connection.erase(it);
×
692
    }
×
693

694
    proc_port.forChildInlets([&](Process::Inlet& model_inl) {
×
695
      OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
×
UNCOV
696
      unregister_inlet(model_inl, node);
×
697
    });
×
UNCOV
698
  }
×
699

UNCOV
700
  outlets.erase(const_cast<Process::Outlet*>(&proc_port));
×
701
}
×
702

703
void SetupContext::replace_node(
×
704
    const std::shared_ptr<ossia::time_process>& process,
705
    const std::shared_ptr<ossia::graph_node>& node, Transaction& commands)
706
{
707
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
×
UNCOV
708
  commands.push_back([p = process, n = node]() mutable {
×
UNCOV
709
    OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
×
710
    using namespace std;
UNCOV
711
    swap(p->node, n);
×
UNCOV
712
  });
×
UNCOV
713
}
×
714

715
void SetupContext::unregister_node(
743 ✔
716
    const Process::Inlets& proc_inlets, const Process::Outlets& proc_outlets,
717
    const std::shared_ptr<ossia::graph_node>& node)
718
{
719
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
743 ✔
720
  if(node)
743 ✔
721
  {
722
    std::weak_ptr<ossia::graph_interface> wg = context.execGraph;
743 ✔
723
    std::weak_ptr<ossia::execution_state> ws = context.execState;
743 ✔
724
    context.executionQueue.enqueue([wg, ws, node = node] mutable {
1,186 ✔
725
      OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
443 ✔
726
      if(auto s = ws.lock())
886 ✔
727
      {
728
        ossia::for_each_inlet(*node, [&](auto& p) { s->unregister_port(p); });
1,647 ✔
729
        ossia::for_each_outlet(*node, [&](auto& p) { s->unregister_port(p); });
852 ✔
730
      }
443 ✔
731

732
      if(auto g = wg.lock())
886 ✔
733
        g->remove_node(node);
443 ✔
734

735
      node->clear();
443 ✔
736
    });
443 ✔
737

738
    runtime_connections[node].clear();
743 ✔
739
    runtime_connections.erase(node);
743 ✔
740

741
    proc_map.erase(node.get());
743 ✔
742
  }
743 ✔
743

744
  for(auto ptr : proc_inlets)
2,518 ✔
745
    inlets.erase(ptr);
1,775 ✔
746
  for(auto ptr : proc_outlets)
1,394 ✔
747
    outlets.erase(ptr);
651 ✔
748
}
743 ✔
749

UNCOV
750
void SetupContext::unregister_node(
×
751
    const Process::Inlets& proc_inlets, const Process::Outlets& proc_outlets,
752
    const std::shared_ptr<ossia::graph_node>& node, Transaction& vec)
753
{
754
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
×
755
  if(node)
×
756
  {
757
    std::weak_ptr<ossia::graph_interface> wg = context.execGraph;
×
758
    std::weak_ptr<ossia::execution_state> ws = context.execState;
×
759
    vec.push_back([wg, ws, node] {
×
UNCOV
760
      OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
×
761
      if(auto s = ws.lock())
×
762
      {
763
        ossia::for_each_inlet(*node, [&](auto& p) { s->unregister_port(p); });
×
764
        ossia::for_each_outlet(*node, [&](auto& p) { s->unregister_port(p); });
×
UNCOV
765
      }
×
766

767
      if(auto g = wg.lock())
×
UNCOV
768
        g->remove_node(node);
×
769
      node->clear();
×
770
    });
×
771

772
    runtime_connections[node].clear();
×
773
    runtime_connections.erase(node);
×
774

775
    proc_map.erase(node.get());
×
776
  }
×
777

UNCOV
778
  for(auto ptr : proc_inlets)
×
UNCOV
779
    inlets.erase(ptr);
×
UNCOV
780
  for(auto ptr : proc_outlets)
×
UNCOV
781
    outlets.erase(ptr);
×
UNCOV
782
}
×
783

784
void SetupContext::unregister_node_soft(
78 ✔
785
    const Process::Inlets& proc_inlets, const Process::Outlets& proc_outlets,
786
    const std::shared_ptr<ossia::graph_node>& node, Transaction& vec)
787
{
788
  OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Ui);
78 ✔
789
  if(node)
78 ✔
790
  {
791
    std::weak_ptr<ossia::execution_state> ws = context.execState;
78 ✔
792
    vec.push_back([ws, node] {
155 ✔
793
      OSSIA_ENSURE_CURRENT_THREAD_KIND(ossia::thread_type::Audio);
77 ✔
794
      if(auto s = ws.lock())
154 ✔
795
      {
796
        ossia::for_each_inlet(*node, [&](auto& p) { s->unregister_port(p); });
790 ✔
797
        ossia::for_each_outlet(*node, [&](auto& p) { s->unregister_port(p); });
184 ✔
798
      }
77 ✔
799
    });
77 ✔
800
    runtime_connections[node].clear();
78 ✔
801
    runtime_connections.erase(node);
78 ✔
802

803
    proc_map.erase(node.get());
78 ✔
804
  }
78 ✔
805

806
  for(auto ptr : proc_inlets)
758 ✔
807
    inlets.erase(ptr);
680 ✔
808
  for(auto ptr : proc_outlets)
189 ✔
809
    outlets.erase(ptr);
111 ✔
810
}
78 ✔
811

812
SetupContext::SetupContext(Context& other) noexcept
4,916 ✔
813
    : context{other}
4,916 ✔
814
{
9,832 ✔
815
}
816

817
void SetupContext::follow_published()
4,142 ✔
818
{
819
  if(m_followsPublished)
4,142 ✔
820
    return;
3,991 ✔
821
  m_followsPublished = true;
151 ✔
822
  // Rebinds ports published after setup, e.g. restored by undo during playback
823
  if(auto tree = context.doc.findPlugin<LocalTree::ScriptableTreeBase>())
151 ✔
824
    connect(tree, &LocalTree::ScriptableTreeBase::published, this, [this](QObject* object) {
212 ✔
825
      QTimer::singleShot(0, this, [this, object = QPointer<QObject>{object}] {
122 ✔
826
        if(object)
61 ✔
827
          rebind(object.data());
61 ✔
828
      });
61 ✔
829
    });
61 ✔
830
}
4,142 ✔
831

832
void SetupContext::rebind(QObject* port)
88 ✔
833
{
834
  if(context.retired)
88 ✔
UNCOV
835
    return;
×
836
  if(auto in = qobject_cast<Process::Inlet*>(port))
88 ✔
837
  {
838
    if(auto it = inlets.find(in); it != inlets.end())
69 ✔
839
      set_destination(effectiveAddress(*in), it->second.second);
69 ✔
840
  }
69 ✔
841
  else if(auto out = qobject_cast<Process::Outlet*>(port))
19 ✔
842
  {
843
    if(auto it = outlets.find(out); it != outlets.end())
3 ✔
844
      set_destination(effectiveAddress(*out), it->second.second);
3 ✔
845
  }
3 ✔
846
}
88 ✔
847

848
SetupContext::~SetupContext() { }
4,916 ✔
849

850
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc