From 92600e0011488c66175dea483515daec560cd580 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Sat, 11 Aug 2012 18:41:31 +0000 Subject: Considerable DSP performance improvements (mixing overhead, and per-event MIDI overhead with Note/Trigger nodes). git-svn-id: http://svn.drobilla.net/lad/trunk/ingen@4657 a436a847-0d15-0410-975c-d299462d15a1 --- src/server/Buffer.cpp | 12 ------- src/server/Buffer.hpp | 13 ++++++- src/server/CompiledPatch.hpp | 5 --- src/server/DuplexPort.cpp | 7 ++++ src/server/DuplexPort.hpp | 2 ++ src/server/EdgeImpl.cpp | 74 -------------------------------------- src/server/EdgeImpl.hpp | 26 +++----------- src/server/InputPort.cpp | 47 ++++++++++++++++++------ src/server/InputPort.hpp | 3 ++ src/server/PatchImpl.cpp | 18 ++-------- src/server/PortImpl.cpp | 54 +++++++++++++++++----------- src/server/PortImpl.hpp | 14 ++++---- src/server/events/CreatePatch.cpp | 2 +- src/server/events/CreatePort.cpp | 1 + src/server/events/Delta.cpp | 9 +++-- src/server/events/Get.cpp | 1 + src/server/events/SetPortValue.hpp | 5 --- 17 files changed, 120 insertions(+), 173 deletions(-) (limited to 'src/server') diff --git a/src/server/Buffer.cpp b/src/server/Buffer.cpp index a91ce1b8..b6b67008 100644 --- a/src/server/Buffer.cpp +++ b/src/server/Buffer.cpp @@ -109,18 +109,6 @@ Buffer::copy(const Context& context, const Buffer* src) } } -void -Buffer::set_block(Sample val, const SampleCount start, const SampleCount end) -{ - assert(end <= nframes()); - - // Note: This is done in this particular way so GCC can vectorize it - Sample* const buf = samples() + start; - for (SampleCount i = 0; i < (end - start); ++i) { - buf[i] = val; - } -} - void Buffer::resize(uint32_t capacity) { diff --git a/src/server/Buffer.hpp b/src/server/Buffer.hpp index 720b1073..fd4151b8 100644 --- a/src/server/Buffer.hpp +++ b/src/server/Buffer.hpp @@ -46,7 +46,6 @@ public: void clear(); void resize(uint32_t size); void copy(const Context& context, const Buffer* src); - void set_block(Sample val, SampleCount start, SampleCount end); void prepare_write(Context& context); void* port_data(PortType port_type); @@ -104,6 +103,18 @@ public: return samples()[offset]; } + inline void set_block(Sample val, + const SampleCount start, + const SampleCount end) + { + assert(end <= nframes()); + // Note: Do not change this without ensuring GCC can still vectorize it + Sample* const buf = samples() + start; + for (SampleCount i = 0; i < (end - start); ++i) { + buf[i] = val; + } + } + /// Audio buffers only float peak(const Context& context) const; diff --git a/src/server/CompiledPatch.hpp b/src/server/CompiledPatch.hpp index 4fe0c042..203f817b 100644 --- a/src/server/CompiledPatch.hpp +++ b/src/server/CompiledPatch.hpp @@ -67,11 +67,6 @@ class CompiledPatch : public std::vector , public Raul::Deletable , public boost::noncopyable { -public: - typedef std::vector QueuedEdges; - - /** All (audio context => other context) edges */ - std::vector queued_edges; }; } // namespace Server diff --git a/src/server/DuplexPort.cpp b/src/server/DuplexPort.cpp index c3b1a6b7..c7b81dc8 100644 --- a/src/server/DuplexPort.cpp +++ b/src/server/DuplexPort.cpp @@ -21,6 +21,7 @@ #include "Buffer.hpp" #include "DuplexPort.hpp" #include "OutputPort.hpp" +#include "PatchImpl.hpp" using namespace std; @@ -60,6 +61,12 @@ DuplexPort::get_buffers(Context& context, } } +uint32_t +DuplexPort::max_tail_poly(Context& context) const +{ + return parent_patch()->internal_poly_process(); +} + /** Prepare for the execution of parent patch */ void DuplexPort::pre_process(Context& context) diff --git a/src/server/DuplexPort.hpp b/src/server/DuplexPort.hpp index c12a0b56..5371680d 100644 --- a/src/server/DuplexPort.hpp +++ b/src/server/DuplexPort.hpp @@ -53,6 +53,8 @@ public: virtual ~DuplexPort() {} + uint32_t max_tail_poly(Context& context) const; + bool get_buffers(Context& context, BufferFactory& bufs, Raul::Array* buffers, diff --git a/src/server/EdgeImpl.cpp b/src/server/EdgeImpl.cpp index a88290e8..4a3c5ce9 100644 --- a/src/server/EdgeImpl.cpp +++ b/src/server/EdgeImpl.cpp @@ -39,25 +39,11 @@ namespace Server { EdgeImpl::EdgeImpl(PortImpl* tail, PortImpl* head) : _tail(tail) , _head(head) - , _queue(NULL) { assert(tail); assert(head); assert(tail != head); assert(tail->path() != head->path()); - - if (must_queue()) - _queue = new Raul::RingBuffer(tail->buffer_size() * 2); -} - -void -EdgeImpl::dump() const -{ - Raul::debug << _tail->path() << " -> " << _head->path() - << (must_mix() ? " (MIX) " : " (DIRECT) ") - << (must_queue() ? " (QUEUE)" : " (NOQUEUE) ") - << "POLY: " << _tail->poly() << " => " << _head->poly() - << std::endl; } const Raul::Path& @@ -72,63 +58,10 @@ EdgeImpl::head_path() const return _head->path(); } -void -EdgeImpl::get_sources(Context& context, - uint32_t voice, - Buffer** srcs, - uint32_t max_num_srcs, - uint32_t& num_srcs) -{ - if (must_queue() && _queue->read_space() > 0) { - LV2_Atom obj; - _queue->peek(sizeof(LV2_Atom), &obj); - BufferRef buf = context.engine().buffer_factory()->get( - context, head()->buffer_type(), sizeof(LV2_Atom) + obj.size); - void* data = buf->port_data(PortType::ATOM); - _queue->read(sizeof(LV2_Atom) + obj.size, (LV2_Atom*)data); - srcs[num_srcs++] = buf.get(); - } else if (must_mix()) { - // Mixing down voices: every src voice mixed into every dst voice - for (uint32_t v = 0; v < _tail->poly(); ++v) { - assert(num_srcs < max_num_srcs); - srcs[num_srcs++] = _tail->buffer(v).get(); - } - } else { - // Matching polyphony: each src voice mixed into corresponding dst voice - assert(_tail->poly() == _head->poly()); - assert(num_srcs < max_num_srcs); - srcs[num_srcs++] = _tail->buffer(voice).get(); - } -} - -void -EdgeImpl::queue(Context& context) -{ - if (!must_queue()) - return; - - const Ingen::URIs& uris = _tail->bufs().uris(); - - BufferRef src_buf = _tail->buffer(0); - if (src_buf->atom()->type != uris.atom_Sequence) { - Raul::error << "Queued edge source is not a Sequence" << std::endl; - return; - } - - LV2_Atom_Sequence* seq = (LV2_Atom_Sequence*)src_buf->atom(); - LV2_ATOM_SEQUENCE_FOREACH(seq, ev) { - _queue->write(sizeof(LV2_Atom) + ev->body.size, &ev->body); - context.engine().message_context().run( - context, _head->parent_node(), context.start() + ev->time.frames); - - } -} - BufferRef EdgeImpl::buffer(uint32_t voice) const { assert(!must_mix()); - assert(!must_queue()); assert(_tail->poly() == 1 || _tail->poly() > voice); if (_tail->poly() == 1) { return _tail->buffer(0); @@ -143,13 +76,6 @@ EdgeImpl::must_mix() const return _tail->poly() > _head->poly(); } -bool -EdgeImpl::must_queue() const -{ - return _tail->parent_node()->context() - != _head->parent_node()->context(); -} - bool EdgeImpl::can_connect(const OutputPort* src, const InputPort* dst) { diff --git a/src/server/EdgeImpl.hpp b/src/server/EdgeImpl.hpp index 23a22ba3..fd5b5ca0 100644 --- a/src/server/EdgeImpl.hpp +++ b/src/server/EdgeImpl.hpp @@ -27,7 +27,7 @@ #include "raul/Deletable.hpp" #include "raul/log.hpp" -#include "BufferFactory.hpp" +#include "BufferRef.hpp" #include "Context.hpp" namespace Ingen { @@ -36,8 +36,6 @@ namespace Server { class PortImpl; class OutputPort; class InputPort; -class Buffer; -class BufferFactory; /** Represents a single inbound connection for an InputPort. * @@ -60,20 +58,12 @@ class EdgeImpl public: EdgeImpl(PortImpl* tail, PortImpl* head); - PortImpl* tail() const { return _tail; } - PortImpl* head() const { return _head; } + inline PortImpl* tail() const { return _tail; } + inline PortImpl* head() const { return _head; } const Raul::Path& tail_path() const; const Raul::Path& head_path() const; - void queue(Context& context); - - void get_sources(Context& context, - uint32_t voice, - Buffer** srcs, - uint32_t max_num_srcs, - uint32_t& num_srcs); - /** Get the buffer for a particular voice. * An Edge is smart - it knows the destination port requesting the * buffer, and will return accordingly (e.g. the same buffer for every @@ -84,17 +74,11 @@ public: /** Whether this edge must mix down voices into a local buffer */ bool must_mix() const; - /** Whether this edge crosses contexts and must buffer */ - bool must_queue() const; - static bool can_connect(const OutputPort* src, const InputPort* dst); protected: - void dump() const; - - PortImpl* const _tail; - PortImpl* const _head; - Raul::RingBuffer* _queue; + PortImpl* const _tail; + PortImpl* const _head; }; } // namespace Server diff --git a/src/server/InputPort.cpp b/src/server/InputPort.cpp index 48b79822..6ac2eb5e 100644 --- a/src/server/InputPort.cpp +++ b/src/server/InputPort.cpp @@ -24,6 +24,7 @@ #include "InputPort.hpp" #include "NodeImpl.hpp" #include "OutputPort.hpp" +#include "PatchImpl.hpp" #include "ProcessContext.hpp" #include "ingen/URIs.hpp" #include "mix.hpp" @@ -91,8 +92,7 @@ InputPort::get_buffers(Context& context, } else if (num_edges == 1) { if (is_process_context) { - if (!_edges.front().must_mix() && - !_edges.front().must_queue()) { + if (!_edges.front().must_mix()) { // Single non-mixing connection, use buffers directly for (uint32_t v = 0; v < poly; ++v) { buffers->at(v) = _edges.front().buffer(v); @@ -156,6 +156,34 @@ InputPort::remove_edge(ProcessContext& context, const OutputPort* tail) return edge; } +uint32_t +InputPort::max_tail_poly(Context& context) const +{ + return parent_node()->parent_patch()->internal_poly_process(); +} + +static void +get_sources(const Context& context, + const EdgeImpl& edge, + uint32_t voice, + const Buffer** srcs, + uint32_t max_num_srcs, + uint32_t& num_srcs) +{ + if (edge.must_mix()) { + // Mixing down voices: all tail voices => one head voice + for (uint32_t v = 0; v < edge.tail()->poly(); ++v) { + assert(num_srcs < max_num_srcs); + srcs[num_srcs++] = edge.tail()->buffer(v).get(); + } + } else { + // Matching polyphony: each tail voice => corresponding head voice + assert(edge.tail()->poly() == edge.head()->poly()); + assert(num_srcs < max_num_srcs); + srcs[num_srcs++] = edge.tail()->buffer(voice).get(); + } +} + /** Prepare buffer for access, mixing if necessary. Realtime safe. */ void @@ -174,18 +202,18 @@ InputPort::pre_process(Context& context) _buffers->at(v) = _edges.front().buffer(v); } } else { - uint32_t max_num_srcs = 1; - for (Edges::const_iterator e = _edges.begin(); e != _edges.end(); ++e) { - max_num_srcs += e->tail()->poly(); - } + const uint32_t src_poly = max_tail_poly(context); + const uint32_t max_num_srcs = _edges.size() * src_poly; - Buffer* srcs[max_num_srcs]; + const Buffer* srcs[max_num_srcs]; for (uint32_t v = 0; v < _poly; ++v) { + // Get all the sources for this voice uint32_t num_srcs = 0; for (Edges::iterator e = _edges.begin(); e != _edges.end(); ++e) { - e->get_sources(context, v, srcs, max_num_srcs, num_srcs); + get_sources(context, *e, v, srcs, max_num_srcs, num_srcs); } + // Then mix them into out buffer for this voice mix(context, buffer(v).get(), srcs, num_srcs); } } @@ -213,8 +241,7 @@ InputPort::direct_connect() const { return _edges.size() == 1 && !_parent->path().is_root() - && !_edges.front().must_mix() - && !_edges.front().must_queue(); + && !_edges.front().must_mix(); } } // namespace Server diff --git a/src/server/InputPort.hpp b/src/server/InputPort.hpp index db614efa..d933344a 100644 --- a/src/server/InputPort.hpp +++ b/src/server/InputPort.hpp @@ -67,6 +67,9 @@ public: boost::intrusive::constant_time_size > Edges; + /** Return the maximum polyphony of an output connected to this input. */ + virtual uint32_t max_tail_poly(Context& context) const; + void add_edge(ProcessContext& context, EdgeImpl* c); EdgeImpl* remove_edge(ProcessContext& context, const OutputPort* tail); diff --git a/src/server/PatchImpl.cpp b/src/server/PatchImpl.cpp index 9abadadb..d8097568 100644 --- a/src/server/PatchImpl.cpp +++ b/src/server/PatchImpl.cpp @@ -21,6 +21,7 @@ #include "ingen/World.hpp" #include "raul/log.hpp" +#include "BufferFactory.hpp" #include "EdgeImpl.hpp" #include "DuplexPort.hpp" #include "Engine.hpp" @@ -52,6 +53,7 @@ PatchImpl::PatchImpl(Engine& engine, , _process(false) { assert(internal_poly >= 1); + assert(internal_poly <= 128); } PatchImpl::~PatchImpl() @@ -156,12 +158,6 @@ PatchImpl::process(ProcessContext& context) for (size_t i = 0; i < _compiled_patch->size(); ++i) { (*_compiled_patch)[i].node()->process(context); } - - // Queue any cross-context edges - for (CompiledPatch::QueuedEdges::iterator i = _compiled_patch->queued_edges.begin(); - i != _compiled_patch->queued_edges.end(); ++i) { - (*i)->queue(context); - } } NodeImpl::post_process(context); @@ -400,16 +396,6 @@ PatchImpl::compile() const compile_recursive(node, compiled_patch); } - // Add any queued edges that must be run after a cycle - for (Edges::const_iterator i = _edges.begin(); - i != _edges.end(); ++i) { - SharedPtr c = PtrCast(i->second); - if (c->tail()->parent_node()->context() == Context::AUDIO && - c->head()->parent_node()->context() == Context::MESSAGE) { - compiled_patch->queued_edges.push_back(c.get()); - } - } - assert(compiled_patch->size() == _nodes.size()); return compiled_patch; diff --git a/src/server/PortImpl.cpp b/src/server/PortImpl.cpp index aee380ff..6a57e6a2 100644 --- a/src/server/PortImpl.cpp +++ b/src/server/PortImpl.cpp @@ -135,34 +135,48 @@ PortImpl::cache_properties() } void -PortImpl::set_control_value(Context& context, FrameTime time, Sample value) +PortImpl::set_control_value(const Context& context, + FrameTime time, + Sample value) { - for (uint32_t v = 0; v < poly(); ++v) { + for (uint32_t v = 0; v < _poly; ++v) { set_voice_value(context, v, time, value); } } void -PortImpl::set_voice_value(Context& context, uint32_t voice, FrameTime time, Sample value) +PortImpl::set_voice_value(const Context& context, + uint32_t voice, + FrameTime time, + Sample value) { - // Time may be at end so internal nodes can set single sample triggers - assert(time >= context.start()); - assert(time <= context.start() + context.nframes()); - - if (_type == PortType::CONTROL) { - time = context.start(); + switch (_type.symbol()) { + case PortType::CONTROL: + buffer(voice)->samples()[0] = value; + _set_states->at(voice).state = SetState::SET; + break; + case PortType::AUDIO: + case PortType::CV: { + // Time may be at end so internal nodes can set triggers + assert(time >= context.start()); + assert(time <= context.start() + context.nframes()); + + const FrameTime offset = time - context.start(); + if (offset < context.nframes()) { + buffer(voice)->set_block(value, offset, context.nframes()); + } + /* else, this is a set at context.nframes(), used to reset a CV port's + value for the next block, particularly for triggers on the last + frame of a block (set nframes-1 to 1, then nframes to 0). */ + + SetState& state = _set_states->at(voice); + state.state = (offset == 0) ? SetState::SET : SetState::HALF_SET_CYCLE_1; + state.time = time; + state.value = value; + } + default: + break; } - - FrameTime offset = time - context.start(); - FrameTime end = (_type == PortType::CONTROL) ? 1 : context.nframes(); - if (offset < context.nframes()) { - buffer(voice)->set_block(value, offset, end); - } // else trigger at very end of block, and set to 0 at start of next block - - SetState& state = _set_states->at(voice); - state.state = (offset == 0) ? SetState::SET : SetState::HALF_SET_CYCLE_1; - state.time = time; - state.value = value; } void diff --git a/src/server/PortImpl.hpp b/src/server/PortImpl.hpp index 0a8415f7..e55448f5 100644 --- a/src/server/PortImpl.hpp +++ b/src/server/PortImpl.hpp @@ -99,12 +99,14 @@ public: void update_set_state(Context& context, uint32_t voice); - void set_voice_value(Context& context, - uint32_t voice, - FrameTime time, - Sample value); - - void set_control_value(Context& context, FrameTime time, Sample value); + void set_voice_value(const Context& context, + uint32_t voice, + FrameTime time, + Sample value); + + void set_control_value(const Context& context, + FrameTime time, + Sample value); /** Called once per process cycle */ virtual void pre_process(Context& context) = 0; diff --git a/src/server/events/CreatePatch.cpp b/src/server/events/CreatePatch.cpp index 476ccccb..b29ca667 100644 --- a/src/server/events/CreatePatch.cpp +++ b/src/server/events/CreatePatch.cpp @@ -69,7 +69,7 @@ CreatePatch::pre_process() int_poly = p->second.get_int32(); } - if (int_poly < 1) { + if (int_poly < 1 || int_poly > 128) { return Event::pre_process_done(INVALID_POLY, path); } diff --git a/src/server/events/CreatePort.cpp b/src/server/events/CreatePort.cpp index c62e858c..c156c13a 100644 --- a/src/server/events/CreatePort.cpp +++ b/src/server/events/CreatePort.cpp @@ -23,6 +23,7 @@ #include "raul/Path.hpp" #include "Broadcaster.hpp" +#include "BufferFactory.hpp" #include "CreatePort.hpp" #include "Driver.hpp" #include "DuplexPort.hpp" diff --git a/src/server/events/Delta.cpp b/src/server/events/Delta.cpp index b1408d7a..f5d5239a 100644 --- a/src/server/events/Delta.cpp +++ b/src/server/events/Delta.cpp @@ -206,8 +206,13 @@ Delta::pre_process() } } else if (key == uris.ingen_polyphony) { if (value.type() == uris.forge.Int) { - op = POLYPHONY; - _patch->prepare_internal_poly(*_engine.buffer_factory(), value.get_int32()); + if (value.get_int32() < 1 || value.get_int32() > 128) { + _status == INVALID_POLY; + } else { + op = POLYPHONY; + _patch->prepare_internal_poly( + *_engine.buffer_factory(), value.get_int32()); + } } else { _status = BAD_VALUE_TYPE; } diff --git a/src/server/events/Get.cpp b/src/server/events/Get.cpp index ae4174bb..8ffe9319 100644 --- a/src/server/events/Get.cpp +++ b/src/server/events/Get.cpp @@ -19,6 +19,7 @@ #include "ingen/Interface.hpp" #include "Broadcaster.hpp" +#include "BufferFactory.hpp" #include "Driver.hpp" #include "Engine.hpp" #include "EngineStore.hpp" diff --git a/src/server/events/SetPortValue.hpp b/src/server/events/SetPortValue.hpp index 93a4b528..a82553f5 100644 --- a/src/server/events/SetPortValue.hpp +++ b/src/server/events/SetPortValue.hpp @@ -31,11 +31,6 @@ class PortImpl; namespace Events { /** An event to change the value of a port. - * - * This event can either be queued or immediate, depending on the queued - * parameter passed to the constructor. It must be passed to the appropriate - * place (ie queued event passed to the event queue and non-queued event - * processed in the audio thread) or nasty things will happen. * * \ingroup engine */ -- cgit v1.2.1