From 170b9150381a13ccee28517aff19c61db8a8386a Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Tue, 30 Apr 2024 00:01:32 +0200 Subject: [PATCH] Prepare for parallel Disk I/O --- libs/ardour/ardour/io_tasklist.h | 47 ++++++++++++++++++++++++++++ libs/ardour/ardour/session.h | 3 ++ libs/ardour/butler.cc | 36 +++++++++++++--------- libs/ardour/io_tasklist.cc | 53 ++++++++++++++++++++++++++++++++ libs/ardour/session.cc | 4 +++ libs/ardour/session_transport.cc | 38 +++++++++++++++-------- libs/ardour/wscript | 1 + 7 files changed, 155 insertions(+), 27 deletions(-) create mode 100644 libs/ardour/ardour/io_tasklist.h create mode 100644 libs/ardour/io_tasklist.cc diff --git a/libs/ardour/ardour/io_tasklist.h b/libs/ardour/ardour/io_tasklist.h new file mode 100644 index 0000000000..daac29fdb0 --- /dev/null +++ b/libs/ardour/ardour/io_tasklist.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2024 Robin Gareus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _ardour_io_tasklist_h_ +#define _ardour_io_tasklist_h_ + +#include +#include + +#include "ardour/libardour_visibility.h" + +namespace ARDOUR +{ + +class LIBARDOUR_API IOTaskList +{ +public: + IOTaskList (); + ~IOTaskList (); + + /** process tasks in list in parallel, wait for them to complete */ + void process (); + void push_back (boost::function fn); + +private: + std::vector> _tasks; + + size_t _n_threads; +}; + +} // namespace ARDOUR +#endif diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index 09ef6f25d9..bfddd4c6a6 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -149,6 +149,7 @@ struct GraphChain; class IO; class IOPlug; class IOProcessor; +class IOTaskList; class ImportStatus; class MidiClockTicker; class MidiControlUI; @@ -334,6 +335,7 @@ public: } std::shared_ptr rt_tasklist () { return _rt_tasklist; } + std::shared_ptr io_tasklist () { return _io_tasklist; } RouteList get_routelist (bool mixer_order = false, PresentationInfo::Flag fl = PresentationInfo::MixerRoutes) const; @@ -2344,6 +2346,7 @@ private: std::shared_ptr _ltc_output_port; std::shared_ptr _rt_tasklist; + std::shared_ptr _io_tasklist; /* Scene Changing */ SceneChanger* _scene_changer; diff --git a/libs/ardour/butler.cc b/libs/ardour/butler.cc index 0c16425022..8c76c9b084 100644 --- a/libs/ardour/butler.cc +++ b/libs/ardour/butler.cc @@ -44,6 +44,7 @@ #include "ardour/disk_io.h" #include "ardour/disk_reader.h" #include "ardour/io.h" +#include "ardour/io_tasklist.h" #include "ardour/session.h" #include "ardour/track.h" @@ -264,6 +265,8 @@ Butler::thread_work () DEBUG_TRACE (DEBUG::Butler, string_compose ("butler starts refill loop, twr = %1\n", transport_work_requested ())); + std::shared_ptr tl = _session.io_tasklist (); + for (i = rl_with_auditioner.begin (); !transport_work_requested () && should_run && i != rl_with_auditioner.end (); ++i) { std::shared_ptr tr = std::dynamic_pointer_cast (*i); @@ -278,24 +281,27 @@ Butler::thread_work () // DEBUG_TRACE (DEBUG::Butler, string_compose ("butler skips inactive track %1\n", tr->name())); continue; } - // DEBUG_TRACE (DEBUG::Butler, string_compose ("butler refills %1, playback load = %2\n", tr->name(), tr->playback_buffer_load())); - switch (tr->do_refill ()) { - case 0: - //DEBUG_TRACE (DEBUG::Butler, string_compose ("\ttrack refill done %1\n", tr->name())); - break; - case 1: - DEBUG_TRACE (DEBUG::Butler, string_compose ("\ttrack refill unfinished %1\n", tr->name ())); - disk_work_outstanding = true; - break; - - default: - error << string_compose (_("Butler read ahead failure on dstream %1"), (*i)->name ()) << endmsg; - std::cerr << string_compose (_("Butler read ahead failure on dstream %1"), (*i)->name ()) << std::endl; - break; - } + tl->push_back ([tr, &disk_work_outstanding]() { + switch (tr->do_refill ()) { + case 0: + //DEBUG_TRACE (DEBUG::Butler, string_compose ("\ttrack refill done %1\n", tr->name())); + break; + case 1: + DEBUG_TRACE (DEBUG::Butler, string_compose ("\ttrack refill unfinished %1\n", tr->name ())); + disk_work_outstanding = true; + break; + default: + error << string_compose (_("Butler read ahead failure on dstream %1"), tr->name ()) << endmsg; + std::cerr << string_compose (_("Butler read ahead failure on dstream %1"), tr->name ()) << std::endl; + break; + } + }); } + tl->process (); + tl.reset (); + if (i != rl_with_auditioner.begin () && i != rl_with_auditioner.end ()) { /* we didn't get to all the streams */ disk_work_outstanding = true; diff --git a/libs/ardour/io_tasklist.cc b/libs/ardour/io_tasklist.cc new file mode 100644 index 0000000000..8af4a7f4e2 --- /dev/null +++ b/libs/ardour/io_tasklist.cc @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2017-2024 Robin Gareus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "pbd/pthread_utils.h" + +#include "ardour/io_tasklist.h" + +using namespace ARDOUR; + +IOTaskList::IOTaskList () + : _n_threads (0) +{ +} + +IOTaskList::~IOTaskList () +{ +} + +void +IOTaskList::push_back (boost::function fn) +{ + _tasks.push_back (fn); +} + +void +IOTaskList::process () +{ + assert (strcmp (pthread_name (), "butler") == 0); + //std::cout << "IOTaskList::process " << pthread_name () << " " << _tasks.size () << "\n"; + if (_n_threads > 1 && _tasks.size () > 2) { + // TODO + } else { + for (auto const& fn : _tasks) { + fn (); + } + } + _tasks.clear (); +} diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index fa071ba7f3..af1438cbcf 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -85,6 +85,7 @@ #include "ardour/gain_control.h" #include "ardour/graph.h" #include "ardour/io_plug.h" +#include "ardour/io_tasklist.h" #include "ardour/luabindings.h" #include "ardour/lv2_plugin.h" #include "ardour/midiport_manager.h" @@ -589,6 +590,7 @@ Session::immediately_post_engine () _process_graph.reset (new Graph (*this)); _rt_tasklist.reset (new RTTaskList (_process_graph)); + _io_tasklist.reset (new IOTaskList ()); /* every time we reconnect, recompute worst case output latencies */ @@ -720,6 +722,8 @@ Session::destroy () _io_graph_chain[0].reset (); _io_graph_chain[1].reset (); + _io_tasklist.reset (); + _butler->drop_references (); delete _butler; _butler = 0; diff --git a/libs/ardour/session_transport.cc b/libs/ardour/session_transport.cc index aeece88079..09e957983e 100644 --- a/libs/ardour/session_transport.cc +++ b/libs/ardour/session_transport.cc @@ -54,6 +54,7 @@ #include "ardour/click.h" #include "ardour/debug.h" #include "ardour/disk_reader.h" +#include "ardour/io_tasklist.h" #include "ardour/location.h" #include "ardour/playlist.h" #include "ardour/profile.h" @@ -1283,12 +1284,14 @@ Session::non_realtime_locate () tf = _transport_sample; start = get_microseconds (); + std::shared_ptr tl = io_tasklist (); for (auto const& i : *rl) { ++nt; - i->non_realtime_locate (tf); - if (sc != _seek_counter.load ()) { - goto restart; - } + tl->push_back ([this, i, tf, sc]() { if (sc == _seek_counter.load ()) { i->non_realtime_locate (tf); }}); + } + tl->process (); + if (sc != _seek_counter.load ()) { + goto restart; } microseconds_t end = get_microseconds (); @@ -1528,15 +1531,26 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished, bool will_ DEBUG_TRACE (DEBUG::Transport, X_("Butler PTW: locate\n")); - for (auto const& i : *r) { - DEBUG_TRACE (DEBUG::Transport, string_compose ("Butler PTW: locate on %1\n", i->name())); - i->non_realtime_locate (_transport_sample); + std::shared_ptr tl = io_tasklist (); - if (on_entry != _butler->should_do_transport_work.load()) { - finished = false; - /* we will be back */ - return; - } + std::atomic fini (finished); + for (auto const& i : *r) { + tl->push_back ([this, i, on_entry, &fini]() { + if (!fini.load ()) { + return; + } + DEBUG_TRACE (DEBUG::Transport, string_compose ("Butler PTW: locate on %1\n", i->name())); + i->non_realtime_locate (_transport_sample); + if (on_entry != _butler->should_do_transport_work.load()) { + fini = false; + } + }); + } + tl->process (); + if (!fini.load ()) { + finished = false; + /* we will be back */ + return; } VCAList v = _vca_manager->vcas (); diff --git a/libs/ardour/wscript b/libs/ardour/wscript index aa75d4a256..777a0325f1 100644 --- a/libs/ardour/wscript +++ b/libs/ardour/wscript @@ -110,6 +110,7 @@ libardour_sources = [ 'io.cc', 'io_plug.cc', 'io_processor.cc', + 'io_tasklist.cc', 'kmeterdsp.cc', 'ladspa_plugin.cc', 'latent.cc',