From 5465c4af1c7b18cba31fa3d15ab1741f2613e9d4 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Sat, 9 May 2009 23:59:01 +0000 Subject: Dramatically reduce resize overhead everywhere. Make alsa driver create individual ports and do minimal work vs naive full refresh when anything changes. Fixes ticket #355. git-svn-id: http://svn.drobilla.net/lad/trunk/patchage@1967 a436a847-0d15-0410-975c-d299462d15a1 --- src/AlsaDriver.cpp | 310 +++++++++++++++++++++++-------------------------- src/AlsaDriver.hpp | 13 +++ src/JackDbusDriver.cpp | 6 +- src/JackDriver.cpp | 26 +++-- src/Patchage.cpp | 26 ++++- src/Patchage.hpp | 21 ++-- src/PatchageEvent.cpp | 9 +- src/PatchageModule.hpp | 1 - 8 files changed, 218 insertions(+), 194 deletions(-) diff --git a/src/AlsaDriver.cpp b/src/AlsaDriver.cpp index 25a9105..32c5f24 100644 --- a/src/AlsaDriver.cpp +++ b/src/AlsaDriver.cpp @@ -101,12 +101,130 @@ AlsaDriver::refresh() refresh_connections(); } - + boost::shared_ptr AlsaDriver::create_port_view(Patchage* patchage, const PortID& id) { - return boost::shared_ptr(); + boost::shared_ptr parent; + boost::shared_ptr port; + create_port_view_internal(patchage, id.id.alsa_addr, parent, port); + return port; +} + + +boost::shared_ptr +AlsaDriver::find_or_create_module( + Patchage* patchage, + const std::string& client_name, + ModuleType type) +{ + boost::shared_ptr m = _app->canvas()->find_module(client_name, type); + if (!m) { + m = boost::shared_ptr(new PatchageModule(patchage, client_name, type)); + m->load_location(); + _app->canvas()->add_item(m); + _app->enqueue_resize(m); + } + return m; +} + + +void +AlsaDriver::create_port_view_internal( + Patchage* patchage, + snd_seq_addr_t addr, + boost::shared_ptr& m, + boost::shared_ptr& port) +{ + snd_seq_client_info_t* cinfo; + snd_seq_client_info_alloca(&cinfo); + snd_seq_client_info_set_client(cinfo, addr.client); + snd_seq_get_any_client_info(_seq, addr.client, cinfo); + + snd_seq_port_info_t* pinfo; + snd_seq_port_info_alloca(&pinfo); + snd_seq_port_info_set_client(pinfo, addr.client); + snd_seq_port_info_set_port(pinfo, addr.port); + snd_seq_get_any_port_info(_seq, addr.client, addr.port, pinfo); + + const string client_name = snd_seq_client_info_get_name(cinfo); + const string port_name = snd_seq_port_info_get_name(pinfo); + bool is_input = false; + bool is_duplex = false; + bool is_application = true; + bool need_refresh = false; + + int caps = snd_seq_port_info_get_capability(pinfo); + int type = snd_seq_port_info_get_type(pinfo); + + // Skip ports we shouldn't show + if (caps & SND_SEQ_PORT_CAP_NO_EXPORT) + return; + else if ( !( (caps & SND_SEQ_PORT_CAP_READ) + || (caps & SND_SEQ_PORT_CAP_WRITE) + || (caps & SND_SEQ_PORT_CAP_DUPLEX))) + return; + else if ((snd_seq_client_info_get_type(cinfo) != SND_SEQ_USER_CLIENT) + && ((type == SND_SEQ_PORT_SYSTEM_TIMER + || type == SND_SEQ_PORT_SYSTEM_ANNOUNCE))) + return; + + // Figure out direction + if ((caps & SND_SEQ_PORT_CAP_READ) && (caps & SND_SEQ_PORT_CAP_WRITE)) + is_duplex = true; + else if (caps & SND_SEQ_PORT_CAP_READ) + is_input = false; + else if (caps & SND_SEQ_PORT_CAP_WRITE) + is_input = true; + + is_application = (type & SND_SEQ_PORT_TYPE_APPLICATION); + + // Because there would be name conflicts, we must force a split if (stupid) + // alsa duplex ports are present on the client + bool split = false; + if (is_duplex) { + split = true; + if (!_app->state_manager()->get_module_split(client_name, !is_application)) { + need_refresh = true; + _app->state_manager()->set_module_split(client_name, true); + } + } else { + split = _app->state_manager()->get_module_split(client_name, !is_application); + } + + /*cout << "ALSA PORT: " << client_name << " : " << port_name + << " is_application = " << is_application + << " is_duplex = " << is_duplex + << " split = " << split << endl;*/ + + if (!split) { + m = find_or_create_module(_app, client_name, InputOutput); + if (!m->get_port(port_name)) { + port = create_port(m, port_name, is_input, addr); + port->show(); + m->add_port(port); + } + + } else { // split + ModuleType type = ((is_input) ? Input : Output); + m = find_or_create_module(_app, client_name, type); + if (!m->get_port(port_name)) { + port = create_port(m, port_name, is_input, addr); + port->show(); + m->add_port(port); + } + + if (is_duplex) { + type = ((!is_input) ? Input : Output); + m = find_or_create_module(_app, client_name, type); + if (!m->get_port(port_name)) { + port = create_port(m, port_name, !is_input, addr); + port->show(); + m->add_port(port); + } + } + } } @@ -129,7 +247,7 @@ AlsaDriver::refresh_ports() { assert(is_attached()); assert(_seq); - + snd_seq_client_info_t* cinfo; snd_seq_client_info_alloca(&cinfo); snd_seq_client_info_set_client(cinfo, -1); @@ -137,161 +255,18 @@ AlsaDriver::refresh_ports() snd_seq_port_info_t* pinfo; snd_seq_port_info_alloca(&pinfo); - string client_name; - string port_name; - bool is_input = false; - bool is_duplex = false; - bool is_application = true; - bool need_refresh = false; + boost::shared_ptr parent; + boost::shared_ptr port; set< boost::shared_ptr > to_resize; while (snd_seq_query_next_client (_seq, cinfo) >= 0) { snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo)); snd_seq_port_info_set_port(pinfo, -1); - - client_name = snd_seq_client_info_get_name(cinfo); - while (snd_seq_query_next_port(_seq, pinfo) >= 0) { - int caps = snd_seq_port_info_get_capability(pinfo); - int type = snd_seq_port_info_get_type(pinfo); - - // Skip ports we shouldn't show - if (caps & SND_SEQ_PORT_CAP_NO_EXPORT) - continue; - else if ( !( (caps & SND_SEQ_PORT_CAP_READ) - || (caps & SND_SEQ_PORT_CAP_WRITE) - || (caps & SND_SEQ_PORT_CAP_DUPLEX))) - continue; - else if ((snd_seq_client_info_get_type(cinfo) != SND_SEQ_USER_CLIENT) - && ((type == SND_SEQ_PORT_SYSTEM_TIMER - || type == SND_SEQ_PORT_SYSTEM_ANNOUNCE))) - continue; - - const snd_seq_addr_t addr = *snd_seq_port_info_get_addr(pinfo); - - is_duplex = false; - - // FIXME: Should be CAP_SUBS_READ etc? - if ((caps & SND_SEQ_PORT_CAP_READ) && (caps & SND_SEQ_PORT_CAP_WRITE)) - is_duplex = true; - else if (caps & SND_SEQ_PORT_CAP_READ) - is_input = false; - else if (caps & SND_SEQ_PORT_CAP_WRITE) - is_input = true; - - is_application = (type & SND_SEQ_PORT_TYPE_APPLICATION); - port_name = snd_seq_port_info_get_name(pinfo); - boost::shared_ptr m; - - bool split = false; - - // Because there would be name conflicts, we must force a split if (stupid) - // alsa duplex ports are present on the client - if (is_duplex) { - split = true; - if (!_app->state_manager()->get_module_split(client_name, !is_application)) { - need_refresh = true; - _app->state_manager()->set_module_split(client_name, true); - } - } else { - split = _app->state_manager()->get_module_split(client_name, !is_application); - } - - /*cout << "SHOW: " << client_name << " : " << port_name - << " is_application = " << is_application - << " is_duplex = " << is_duplex - << ", split = " << split << endl;*/ - - // Application input/output ports go on the same module - if (!split) { - m = _app->canvas()->find_module(client_name, InputOutput); - if (!m) { - m = boost::shared_ptr( - new PatchageModule(_app, client_name, InputOutput)); - m->load_location(); - _app->canvas()->add_item(m); - } - - if (!m->get_port(port_name)) { - if (!is_duplex) { - m->add_port(create_port(m, port_name, is_input, addr)); - } else { - m->add_port(create_port(m, port_name, true, addr)); - m->add_port(create_port(m, port_name, false, addr)); - } - to_resize.insert(m); - } - - } else { // non-application input/output ports (hw interface, etc) go on separate modules - ModuleType type = InputOutput; - - // The 'application' hint isn't always set by clients, so this bit - // is pretty nasty... - - if (!is_duplex) { // just one port to add - - type = ((is_input) ? Input : Output); - - m = _app->canvas()->find_module(client_name, type); - - if (!m) { - m = boost::shared_ptr( - new PatchageModule(_app, client_name, type)); - m->load_location(); - _app->canvas()->add_item(m); - } - - if (!m->get_port(port_name)) { - m->add_port(create_port(m, port_name, is_input, addr)); - to_resize.insert(m); - } - - } else { // two ports to add - type = Input; - - m = _app->canvas()->find_module(client_name, type); - - if (!m) { - m = boost::shared_ptr( - new PatchageModule(_app, client_name, type)); - m->load_location(); - _app->canvas()->add_item(m); - } - - assert(m); - - if (!m->get_port(port_name)) { - m->add_port(create_port(m, port_name, true, addr)); - to_resize.insert(m); - } - - type = Output; - - m = _app->canvas()->find_module(client_name, type); - - if (!m) { - m = boost::shared_ptr( - new PatchageModule(_app, client_name, type)); - m->load_location(); - _app->canvas()->add_item(m); - } - - if (!m->get_port(port_name)) { - m->add_port(create_port(m, port_name, false, addr)); - to_resize.insert(m); - } - } - } - } - } - - if (need_refresh) { - _app->refresh(); - } else { - for (set< boost::shared_ptr >::iterator i = to_resize.begin(); - i != to_resize.end(); ++i) { - (*i)->resize(); + create_port_view_internal(_app, *snd_seq_port_info_get_addr(pinfo), parent, port); + if (parent) + _app->enqueue_resize(parent); } } } @@ -496,15 +471,12 @@ AlsaDriver::refresh_main(void* me) void AlsaDriver::_refresh_main() { - // "Heavily influenced" from alsa-patch-bay - // (C) 2002 Robert Ham, released under GPL - if (!create_refresh_port()) { cerr << "Could not create Alsa listen port. Auto refreshing will not work." << endl; return; } - int ret; + int ret = 0; int nfds = snd_seq_poll_descriptors_count(_seq, POLLIN); struct pollfd* pfds = new struct pollfd[nfds]; unsigned short* revents = new unsigned short[nfds]; @@ -528,12 +500,15 @@ AlsaDriver::_refresh_main() continue; } + snd_seq_port_info_t* pinfo; + snd_seq_port_info_alloca(&pinfo); + int caps = 0; + for (int i = 0; i < nfds; ++i) { if (revents[i] > 0) { snd_seq_event_t* ev; snd_seq_event_input(_seq, &ev); - - if (ev == NULL) + if (!ev) continue; switch (ev->type) { @@ -546,15 +521,26 @@ AlsaDriver::_refresh_main() ev->data.connect.sender, ev->data.connect.dest)); break; case SND_SEQ_EVENT_PORT_START: + snd_seq_port_info_set_client(pinfo, ev->data.addr.client); + caps = snd_seq_port_info_get_capability(pinfo); + _events.push(PatchageEvent(PatchageEvent::PORT_CREATION, + PortID(ev->data.addr, (caps & SND_SEQ_PORT_CAP_READ)))); + break; case SND_SEQ_EVENT_PORT_EXIT: + snd_seq_port_info_set_client(pinfo, ev->data.addr.client); + caps = snd_seq_port_info_get_capability(pinfo); + _events.push(PatchageEvent(PatchageEvent::PORT_DESTRUCTION, + PortID(ev->data.addr, (caps & SND_SEQ_PORT_CAP_READ)))); + break; + // TODO: What should happen for these? case SND_SEQ_EVENT_PORT_CHANGE: case SND_SEQ_EVENT_CLIENT_START: case SND_SEQ_EVENT_CLIENT_EXIT: case SND_SEQ_EVENT_CLIENT_CHANGE: case SND_SEQ_EVENT_RESET: default: - // FIXME: Ultra slow kludge, use proper find-grained events - _events.push(PatchageEvent(PatchageEvent::REFRESH)); + //_events.push(PatchageEvent(PatchageEvent::REFRESH)); + break; } } } @@ -564,9 +550,3 @@ AlsaDriver::_refresh_main() delete[] revents; } - -void -AlsaDriver::print_addr(snd_seq_addr_t addr) -{ - cout << (int)addr.client << ":" << (int)addr.port << endl; -} diff --git a/src/AlsaDriver.hpp b/src/AlsaDriver.hpp index c39f8c4..51e2b57 100644 --- a/src/AlsaDriver.hpp +++ b/src/AlsaDriver.hpp @@ -63,6 +63,19 @@ private: bool create_refresh_port(); static void* refresh_main(void* me); void _refresh_main(); + + boost::shared_ptr + find_or_create_module( + Patchage* patchage, + const std::string& client_name, + ModuleType type); + + void + create_port_view_internal( + Patchage* patchage, + snd_seq_addr_t addr, + boost::shared_ptr& parent, + boost::shared_ptr& port); boost::shared_ptr create_port( boost::shared_ptr parent, diff --git a/src/JackDbusDriver.cpp b/src/JackDbusDriver.cpp index 0d0cf11..feb1923 100644 --- a/src/JackDbusDriver.cpp +++ b/src/JackDbusDriver.cpp @@ -111,7 +111,7 @@ JackDriver::destroy_all_ports() if (module->ports().empty()) _app->canvas()->remove_item(module); else - module->resize(); + _app->enqueue_resize(); } } @@ -555,7 +555,7 @@ JackDriver::add_port( is_input, _app->state_manager()->get_port_color(type)))); - module->resize(); + _app->enqueue_resize(module); } @@ -620,7 +620,7 @@ JackDriver::remove_port( _app->canvas()->remove_item(module); module.reset(); } else { - module->resize(); + _app->enqueue_resize(module); } if (_app->canvas()->items().empty()) { diff --git a/src/JackDriver.cpp b/src/JackDriver.cpp index a0f1616..b98005c 100644 --- a/src/JackDriver.cpp +++ b/src/JackDriver.cpp @@ -135,7 +135,7 @@ JackDriver::destroy_all_ports() if (module->ports().empty()) _app->canvas()->remove_item(module); else - module->resize(); + _app->enqueue_resize(module); } } @@ -170,26 +170,31 @@ JackDriver::create_port_view(Patchage* patchage, boost::shared_ptr parent = _app->canvas()->find_module(module_name, type); + bool resize = false; + if (!parent) { parent = boost::shared_ptr( new PatchageModule(patchage, module_name, type)); parent->load_location(); patchage->canvas()->add_item(parent); parent->show(); + resize = true; } boost::shared_ptr port = boost::dynamic_pointer_cast(parent->get_port(port_name)); - if (port) { - return port; - } else { + if (!port) { port = create_port(parent, jack_port); port->show(); parent->add_port(port); - parent->resize(); - return port; + resize = true; } + + if (resize) + _app->enqueue_resize(parent); + + return port; } @@ -300,9 +305,10 @@ JackDriver::refresh() continue; } - if (!m->get_port(jack_port_short_name(port))) { + if (!m->get_port(jack_port_short_name(port))) m->add_port(create_port(m, port)); - } + + _app->enqueue_resize(m); } // Add all connections @@ -436,7 +442,6 @@ JackDriver::jack_client_registration_cb(const char* name, int registered, void* jack_reset_max_delayed_usecs(me->_client); - // FIXME: This sucks, not realtime :( if (registered) { me->_events.push(PatchageEvent(PatchageEvent::CLIENT_CREATION, name)); } else { @@ -452,9 +457,6 @@ JackDriver::jack_port_registration_cb(jack_port_id_t port_id, int registered, vo JackDriver* me = reinterpret_cast(jack_driver); assert(me->_client); - //jack_port_t* const port = jack_port_by_id(me->_client, port_id); - //const string full_name = jack_port_name(port); - jack_reset_max_delayed_usecs(me->_client); if (registered) { diff --git a/src/Patchage.cpp b/src/Patchage.cpp index 0a6d5e0..b00013b 100644 --- a/src/Patchage.cpp +++ b/src/Patchage.cpp @@ -23,6 +23,7 @@ #include #include #include "raul/SharedPtr.hpp" +#include "flowcanvas/Module.hpp" #include "patchage-config.h" #include "GladeFile.hpp" @@ -342,6 +343,7 @@ Patchage::idle_callback() _refresh = false; } + flush_resize(); update_load(); return true; @@ -414,9 +416,7 @@ Patchage::refresh() _alsa_driver->refresh(); #endif - for (ItemList::iterator i = _canvas->items().begin(); i != _canvas->items().end(); ++i) { - (*i)->resize(); - } + flush_resize(); } } @@ -692,3 +692,23 @@ Patchage::buffer_size_changed() #endif } + +void +Patchage::enqueue_resize(boost::shared_ptr module) +{ + if (module) + _pending_resize.insert(module); +} + + +void +Patchage::flush_resize() +{ + for (set< boost::shared_ptr >::iterator i = _pending_resize.begin(); + i != _pending_resize.end(); ++i) { + (*i)->resize(); + } + + _pending_resize.clear(); +} + diff --git a/src/Patchage.hpp b/src/Patchage.hpp index 9aeedd0..a7b970f 100644 --- a/src/Patchage.hpp +++ b/src/Patchage.hpp @@ -19,21 +19,24 @@ #define PATCHAGE_PATCHAGE_HPP #include +#include #include #include #include #include "patchage-config.h" #include "Widget.hpp" -class PatchageCanvas; -class JackDriver; class AlsaDriver; -class LashProxy; -class StateManager; -class JackSettingsDialog; -class Session; class DBus; +class JackDriver; +class JackSettingsDialog; +class LashProxy; +class PatchageCanvas; class ProjectList; +class Session; +class StateManager; + +namespace FlowCanvas { class Module; } class Patchage { public: @@ -73,7 +76,9 @@ public: void status_msg(const std::string& msg); void update_state(); void store_window_location(); - + + void enqueue_resize(boost::shared_ptr module); + void flush_resize(); protected: void connect_widgets(); @@ -118,6 +123,8 @@ protected: boost::shared_ptr _canvas; + std::set< boost::shared_ptr > _pending_resize; + JackDriver* _jack_driver; StateManager* _state_manager; diff --git a/src/PatchageEvent.cpp b/src/PatchageEvent.cpp index 63fa029..4fbefdf 100644 --- a/src/PatchageEvent.cpp +++ b/src/PatchageEvent.cpp @@ -68,9 +68,11 @@ PatchageEvent::execute(Patchage* patchage) #endif } - if (driver) { - if ( ! driver->create_port_view(patchage, _port_1)) + SharedPtr port = driver->create_port_view(patchage, _port_1); + if (port) + patchage->enqueue_resize(port->module().lock()); + else cerr << "Unable to create port view" << endl; } else { cerr << "ERROR: Create port with unknown port type" << endl; @@ -85,6 +87,7 @@ PatchageEvent::execute(Patchage* patchage) assert(module); module->remove_port(port); + patchage->enqueue_resize(module); port.reset(); // No empty modules (for now) @@ -92,7 +95,7 @@ PatchageEvent::execute(Patchage* patchage) patchage->canvas()->remove_item(module); module.reset(); } else { - module->resize(); + patchage->enqueue_resize(module); } } else { diff --git a/src/PatchageModule.hpp b/src/PatchageModule.hpp index e21f67d..0eac76f 100644 --- a/src/PatchageModule.hpp +++ b/src/PatchageModule.hpp @@ -40,7 +40,6 @@ public: , _app(app) , _type(type) { - } virtual ~PatchageModule() { delete _menu; _menu = NULL; } -- cgit v1.2.1