Prepare for parallel Disk I/O
This commit is contained in:
parent
2af2df3516
commit
170b915038
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright (C) 2024 Robin Gareus <robin@gareus.org>
|
||||
*
|
||||
* 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 <boost/function.hpp>
|
||||
#include <vector>
|
||||
|
||||
#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<void ()> fn);
|
||||
|
||||
private:
|
||||
std::vector<boost::function<void ()>> _tasks;
|
||||
|
||||
size_t _n_threads;
|
||||
};
|
||||
|
||||
} // namespace ARDOUR
|
||||
#endif
|
|
@ -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<RTTaskList> rt_tasklist () { return _rt_tasklist; }
|
||||
std::shared_ptr<IOTaskList> 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<Port> _ltc_output_port;
|
||||
|
||||
std::shared_ptr<RTTaskList> _rt_tasklist;
|
||||
std::shared_ptr<IOTaskList> _io_tasklist;
|
||||
|
||||
/* Scene Changing */
|
||||
SceneChanger* _scene_changer;
|
||||
|
|
|
@ -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<IOTaskList> tl = _session.io_tasklist ();
|
||||
|
||||
for (i = rl_with_auditioner.begin (); !transport_work_requested () && should_run && i != rl_with_auditioner.end (); ++i) {
|
||||
std::shared_ptr<Track> tr = std::dynamic_pointer_cast<Track> (*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;
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright (C) 2017-2024 Robin Gareus <robin@gareus.org>
|
||||
*
|
||||
* 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<void ()> 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 ();
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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<IOTaskList> 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<IOTaskList> tl = io_tasklist ();
|
||||
|
||||
if (on_entry != _butler->should_do_transport_work.load()) {
|
||||
finished = false;
|
||||
/* we will be back */
|
||||
return;
|
||||
}
|
||||
std::atomic<bool> 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 ();
|
||||
|
|
|
@ -110,6 +110,7 @@ libardour_sources = [
|
|||
'io.cc',
|
||||
'io_plug.cc',
|
||||
'io_processor.cc',
|
||||
'io_tasklist.cc',
|
||||
'kmeterdsp.cc',
|
||||
'ladspa_plugin.cc',
|
||||
'latent.cc',
|
||||
|
|
Loading…
Reference in New Issue