Browse Source

AudioProcessorGraph: Improve lookup speed in isBufferNeedLater

v7.0.9
reuk 2 years ago
parent
commit
cd6939c073
No known key found for this signature in database GPG Key ID: 9ADCD339CFC98A11
1 changed files with 100 additions and 42 deletions
  1. +100
    -42
      modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp

+ 100
- 42
modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp View File

@@ -139,6 +139,15 @@ public:
using Connection = AudioProcessorGraph::Connection; using Connection = AudioProcessorGraph::Connection;
using NodeAndChannel = AudioProcessorGraph::NodeAndChannel; using NodeAndChannel = AudioProcessorGraph::NodeAndChannel;
private:
static auto equalRange (const std::set<NodeAndChannel>& pins, const NodeID node)
{
return std::equal_range (pins.cbegin(), pins.cend(), node, ImplicitNode::compare);
}
using Map = std::map<NodeAndChannel, std::set<NodeAndChannel>>;
public:
static constexpr auto midiChannelIndex = AudioProcessorGraph::midiChannelIndex; static constexpr auto midiChannelIndex = AudioProcessorGraph::midiChannelIndex;
bool addConnection (const Nodes& n, const Connection& c) bool addConnection (const Nodes& n, const Connection& c)
@@ -179,7 +188,7 @@ public:
for (auto& pair : sourcesForDestination) for (auto& pair : sourcesForDestination)
{ {
const auto range = std::equal_range (pair.second.cbegin(), pair.second.cend(), n, ImplicitNode::compare);
const auto range = equalRange (pair.second, n);
result |= range.first != range.second; result |= range.first != range.second;
pair.second.erase (range.first, range.second); pair.second.erase (range.first, range.second);
} }
@@ -231,8 +240,8 @@ public:
return std::any_of (matchingDestinations.first, matchingDestinations.second, [srcID] (const auto& pair) return std::any_of (matchingDestinations.first, matchingDestinations.second, [srcID] (const auto& pair)
{ {
const auto iter = std::lower_bound (pair.second.cbegin(), pair.second.cend(), srcID, ImplicitNode::compare);
return iter != pair.second.cend() && iter->nodeID == srcID;
const auto [begin, end] = equalRange (pair.second, srcID);
return begin != end;
}); });
} }
@@ -276,9 +285,44 @@ public:
bool operator== (const Connections& other) const { return sourcesForDestination == other.sourcesForDestination; } bool operator== (const Connections& other) const { return sourcesForDestination == other.sourcesForDestination; }
bool operator!= (const Connections& other) const { return sourcesForDestination != other.sourcesForDestination; } bool operator!= (const Connections& other) const { return sourcesForDestination != other.sourcesForDestination; }
private:
using Map = std::map<NodeAndChannel, std::set<NodeAndChannel>>;
class DestinationsForSources
{
public:
explicit DestinationsForSources (Map m) : map (std::move (m)) {}
bool isSourceConnectedToDestinationNodeIgnoringChannel (const NodeAndChannel& source, NodeID dest, int channel) const
{
if (const auto destIter = map.find (source); destIter != map.cend())
{
const auto [begin, end] = equalRange (destIter->second, dest);
return std::any_of (begin, end, [&] (const NodeAndChannel& nodeAndChannel)
{
return nodeAndChannel != NodeAndChannel { dest, channel };
});
}
return false;
}
private:
Map map;
};
/* Reverses the graph, to allow fast lookup by source.
This is expensive, don't call this more than necessary!
*/
auto getDestinationsForSources() const
{
Map destinationsForSources;
for (const auto& [destination, sources] : sourcesForDestination)
for (const auto& source : sources)
destinationsForSources[source].insert (destination);
return DestinationsForSources (std::move (destinationsForSources));
}
private:
struct SearchState struct SearchState
{ {
std::set<NodeID> visited; std::set<NodeID> visited;
@@ -430,6 +474,20 @@ public:
return current; return current;
} }
/* Call from the main thread to indicate that a node has been removed from the graph.
*/
void removeNode (const NodeID n)
{
preparedNodes.erase (n);
}
/* Call from the main thread to indicate that all nodes have been removed from the graph.
*/
void clear()
{
preparedNodes.clear();
}
private: private:
std::mutex mutex; std::mutex mutex;
std::set<NodeID> preparedNodes; std::set<NodeID> preparedNodes;
@@ -964,6 +1022,7 @@ private:
//============================================================================== //==============================================================================
template <typename RenderSequence> template <typename RenderSequence>
int findBufferForInputAudioChannel (const Connections& c, int findBufferForInputAudioChannel (const Connections& c,
const Connections::DestinationsForSources& reversed,
RenderSequence& sequence, RenderSequence& sequence,
Node& node, Node& node,
const int inputChan, const int inputChan,
@@ -1001,7 +1060,7 @@ private:
jassert (bufIndex >= 0); jassert (bufIndex >= 0);
} }
if (inputChan < numOuts && isBufferNeededLater (c, ourRenderingIndex, inputChan, src))
if (inputChan < numOuts && isBufferNeededLater (reversed, ourRenderingIndex, inputChan, src))
{ {
// can't mess up this channel because it's needed later by another node, // can't mess up this channel because it's needed later by another node,
// so we need to use a copy of it.. // so we need to use a copy of it..
@@ -1028,7 +1087,7 @@ private:
{ {
auto sourceBufIndex = getBufferContaining (src); auto sourceBufIndex = getBufferContaining (src);
if (sourceBufIndex >= 0 && ! isBufferNeededLater (c, ourRenderingIndex, inputChan, src))
if (sourceBufIndex >= 0 && ! isBufferNeededLater (reversed, ourRenderingIndex, inputChan, src))
{ {
// we've found one of our input chans that can be re-used.. // we've found one of our input chans that can be re-used..
reusableInputIndex = i; reusableInputIndex = i;
@@ -1082,7 +1141,7 @@ private:
if (nodeDelay < maxLatency) if (nodeDelay < maxLatency)
{ {
if (! isBufferNeededLater (c, ourRenderingIndex, inputChan, src))
if (! isBufferNeededLater (reversed, ourRenderingIndex, inputChan, src))
{ {
sequence.addDelayChannelOp (srcIndex, maxLatency - nodeDelay); sequence.addDelayChannelOp (srcIndex, maxLatency - nodeDelay);
} }
@@ -1108,6 +1167,7 @@ private:
template <typename RenderSequence> template <typename RenderSequence>
int findBufferForInputMidiChannel (const Connections& c, int findBufferForInputMidiChannel (const Connections& c,
const Connections::DestinationsForSources& reversed,
RenderSequence& sequence, RenderSequence& sequence,
Node& node, Node& node,
int ourRenderingIndex) int ourRenderingIndex)
@@ -1134,7 +1194,7 @@ private:
if (midiBufferToUse >= 0) if (midiBufferToUse >= 0)
{ {
if (isBufferNeededLater (c, ourRenderingIndex, midiChannelIndex, src))
if (isBufferNeededLater (reversed, ourRenderingIndex, midiChannelIndex, src))
{ {
// can't mess up this channel because it's needed later by another node, so we // can't mess up this channel because it's needed later by another node, so we
// need to use a copy of it.. // need to use a copy of it..
@@ -1163,7 +1223,7 @@ private:
auto sourceBufIndex = getBufferContaining (src); auto sourceBufIndex = getBufferContaining (src);
if (sourceBufIndex >= 0 if (sourceBufIndex >= 0
&& ! isBufferNeededLater (c, ourRenderingIndex, midiChannelIndex, src))
&& ! isBufferNeededLater (reversed, ourRenderingIndex, midiChannelIndex, src))
{ {
// we've found one of our input buffers that can be re-used.. // we've found one of our input buffers that can be re-used..
reusableInputIndex = i; reusableInputIndex = i;
@@ -1212,6 +1272,7 @@ private:
template <typename RenderSequence> template <typename RenderSequence>
void createRenderingOpsForNode (const Connections& c, void createRenderingOpsForNode (const Connections& c,
const Connections::DestinationsForSources& reversed,
RenderSequence& sequence, RenderSequence& sequence,
Node& node, Node& node,
const int ourRenderingIndex) const int ourRenderingIndex)
@@ -1228,6 +1289,7 @@ private:
{ {
// get a list of all the inputs to this node // get a list of all the inputs to this node
auto index = findBufferForInputAudioChannel (c, auto index = findBufferForInputAudioChannel (c,
reversed,
sequence, sequence,
node, node,
inputChan, inputChan,
@@ -1250,7 +1312,7 @@ private:
audioBuffers.getReference (index).channel = { node.nodeID, outputChan }; audioBuffers.getReference (index).channel = { node.nodeID, outputChan };
} }
auto midiBufferToUse = findBufferForInputMidiChannel (c, sequence, node, ourRenderingIndex);
auto midiBufferToUse = findBufferForInputMidiChannel (c, reversed, sequence, node, ourRenderingIndex);
if (processor.producesMidi()) if (processor.producesMidi())
midiBuffers.getReference (midiBufferToUse).channel = { node.nodeID, midiChannelIndex }; midiBuffers.getReference (midiBufferToUse).channel = { node.nodeID, midiChannelIndex };
@@ -1289,7 +1351,7 @@ private:
return -1; return -1;
} }
void markAnyUnusedBuffersAsFree (const Connections& c,
void markAnyUnusedBuffersAsFree (const Connections::DestinationsForSources& c,
Array<AssignedBuffer>& buffers, Array<AssignedBuffer>& buffers,
const int stepIndex) const int stepIndex)
{ {
@@ -1298,33 +1360,25 @@ private:
b.setFree(); b.setFree();
} }
bool isBufferNeededLater (const Connections& c,
int stepIndexToSearchFrom,
int inputChannelOfIndexToIgnore,
NodeAndChannel output) const
bool isBufferNeededLater (const Connections::DestinationsForSources& c,
const int stepIndexToSearchFrom,
const int inputChannelOfIndexToIgnore,
const NodeAndChannel output) const
{ {
while (stepIndexToSearchFrom < orderedNodes.size())
{
auto* node = orderedNodes.getUnchecked (stepIndexToSearchFrom);
if (output.isMIDI())
{
if (inputChannelOfIndexToIgnore != midiChannelIndex
&& c.isConnected ({ output, { node->nodeID, midiChannelIndex } }))
return true;
}
else
{
for (int i = 0; i < node->getProcessor()->getTotalNumInputChannels(); ++i)
if (i != inputChannelOfIndexToIgnore && c.isConnected ({ output, { node->nodeID, i } }))
return true;
}
if (orderedNodes.size() <= stepIndexToSearchFrom)
return false;
inputChannelOfIndexToIgnore = -1;
++stepIndexToSearchFrom;
if (c.isSourceConnectedToDestinationNodeIgnoringChannel (output,
orderedNodes.getUnchecked (stepIndexToSearchFrom)->nodeID,
inputChannelOfIndexToIgnore))
{
return true;
} }
return false;
return std::any_of (orderedNodes.begin() + stepIndexToSearchFrom + 1, orderedNodes.end(), [&] (const auto* node)
{
return c.isSourceConnectedToDestinationNodeIgnoringChannel (output, node->nodeID, -1);
});
} }
template <typename RenderSequence> template <typename RenderSequence>
@@ -1334,11 +1388,13 @@ private:
audioBuffers.add (AssignedBuffer::createReadOnlyEmpty()); // first buffer is read-only zeros audioBuffers.add (AssignedBuffer::createReadOnlyEmpty()); // first buffer is read-only zeros
midiBuffers .add (AssignedBuffer::createReadOnlyEmpty()); midiBuffers .add (AssignedBuffer::createReadOnlyEmpty());
const auto reversed = c.getDestinationsForSources();
for (int i = 0; i < orderedNodes.size(); ++i) for (int i = 0; i < orderedNodes.size(); ++i)
{ {
createRenderingOpsForNode (c, sequence, *orderedNodes.getUnchecked (i), i);
markAnyUnusedBuffersAsFree (c, audioBuffers, i);
markAnyUnusedBuffersAsFree (c, midiBuffers, i);
createRenderingOpsForNode (c, reversed, sequence, *orderedNodes.getUnchecked (i), i);
markAnyUnusedBuffersAsFree (reversed, audioBuffers, i);
markAnyUnusedBuffersAsFree (reversed, midiBuffers, i);
} }
sequence.numBuffersNeeded = audioBuffers.size(); sequence.numBuffersNeeded = audioBuffers.size();
@@ -1388,7 +1444,7 @@ public:
private: private:
template <typename This, typename Callback> template <typename This, typename Callback>
static decltype (auto) visitRenderSequence (This& t, Callback&& callback)
static void visitRenderSequence (This& t, Callback&& callback)
{ {
if (auto* sequence = std::get_if<GraphRenderSequence<float>> (&t.sequence.sequence)) return callback (*sequence); if (auto* sequence = std::get_if<GraphRenderSequence<float>> (&t.sequence.sequence)) return callback (*sequence);
if (auto* sequence = std::get_if<GraphRenderSequence<double>> (&t.sequence.sequence)) return callback (*sequence); if (auto* sequence = std::get_if<GraphRenderSequence<double>> (&t.sequence.sequence)) return callback (*sequence);
@@ -1447,7 +1503,7 @@ private:
}; };
//============================================================================== //==============================================================================
/** Holds information about a particular graph configuration, without sharing ownership of any
/* Holds information about a particular graph configuration, without sharing ownership of any
graph nodes. Can be checked for equality with other RenderSequenceSignature instances to see graph nodes. Can be checked for equality with other RenderSequenceSignature instances to see
whether two graph configurations match. whether two graph configurations match.
*/ */
@@ -1508,7 +1564,7 @@ public:
isNew = true; isNew = true;
} }
/** Call from the audio thread only. */
/* Call from the audio thread only. */
void updateAudioThreadState() void updateAudioThreadState()
{ {
const SpinLock::ScopedTryLockType lock (mutex); const SpinLock::ScopedTryLockType lock (mutex);
@@ -1521,7 +1577,7 @@ public:
} }
} }
/** Call from the audio thread only. */
/* Call from the audio thread only. */
RenderSequence* getAudioThreadState() const { return audioThreadState.get(); } RenderSequence* getAudioThreadState() const { return audioThreadState.get(); }
private: private:
@@ -1587,6 +1643,7 @@ public:
nodes = Nodes{}; nodes = Nodes{};
connections = Connections{}; connections = Connections{};
nodeStates.clear();
topologyChanged (updateKind); topologyChanged (updateKind);
} }
@@ -1625,6 +1682,7 @@ public:
{ {
connections.disconnectNode (nodeID); connections.disconnectNode (nodeID);
auto result = nodes.removeNode (nodeID); auto result = nodes.removeNode (nodeID);
nodeStates.removeNode (nodeID);
topologyChanged (updateKind); topologyChanged (updateKind);
return result; return result;
} }


Loading…
Cancel
Save