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 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;
bool addConnection (const Nodes& n, const Connection& c)
@@ -179,7 +188,7 @@ public:
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;
pair.second.erase (range.first, range.second);
}
@@ -231,8 +240,8 @@ public:
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; }
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
{
std::set<NodeID> visited;
@@ -430,6 +474,20 @@ public:
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:
std::mutex mutex;
std::set<NodeID> preparedNodes;
@@ -964,6 +1022,7 @@ private:
//==============================================================================
template <typename RenderSequence>
int findBufferForInputAudioChannel (const Connections& c,
const Connections::DestinationsForSources& reversed,
RenderSequence& sequence,
Node& node,
const int inputChan,
@@ -1001,7 +1060,7 @@ private:
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,
// so we need to use a copy of it..
@@ -1028,7 +1087,7 @@ private:
{
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..
reusableInputIndex = i;
@@ -1082,7 +1141,7 @@ private:
if (nodeDelay < maxLatency)
{
if (! isBufferNeededLater (c, ourRenderingIndex, inputChan, src))
if (! isBufferNeededLater (reversed, ourRenderingIndex, inputChan, src))
{
sequence.addDelayChannelOp (srcIndex, maxLatency - nodeDelay);
}
@@ -1108,6 +1167,7 @@ private:
template <typename RenderSequence>
int findBufferForInputMidiChannel (const Connections& c,
const Connections::DestinationsForSources& reversed,
RenderSequence& sequence,
Node& node,
int ourRenderingIndex)
@@ -1134,7 +1194,7 @@ private:
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
// need to use a copy of it..
@@ -1163,7 +1223,7 @@ private:
auto sourceBufIndex = getBufferContaining (src);
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..
reusableInputIndex = i;
@@ -1212,6 +1272,7 @@ private:
template <typename RenderSequence>
void createRenderingOpsForNode (const Connections& c,
const Connections::DestinationsForSources& reversed,
RenderSequence& sequence,
Node& node,
const int ourRenderingIndex)
@@ -1228,6 +1289,7 @@ private:
{
// get a list of all the inputs to this node
auto index = findBufferForInputAudioChannel (c,
reversed,
sequence,
node,
inputChan,
@@ -1250,7 +1312,7 @@ private:
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())
midiBuffers.getReference (midiBufferToUse).channel = { node.nodeID, midiChannelIndex };
@@ -1289,7 +1351,7 @@ private:
return -1;
}
void markAnyUnusedBuffersAsFree (const Connections& c,
void markAnyUnusedBuffersAsFree (const Connections::DestinationsForSources& c,
Array<AssignedBuffer>& buffers,
const int stepIndex)
{
@@ -1298,33 +1360,25 @@ private:
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>
@@ -1334,11 +1388,13 @@ private:
audioBuffers.add (AssignedBuffer::createReadOnlyEmpty()); // first buffer is read-only zeros
midiBuffers .add (AssignedBuffer::createReadOnlyEmpty());
const auto reversed = c.getDestinationsForSources();
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();
@@ -1388,7 +1444,7 @@ public:
private:
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<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
whether two graph configurations match.
*/
@@ -1508,7 +1564,7 @@ public:
isNew = true;
}
/** Call from the audio thread only. */
/* Call from the audio thread only. */
void updateAudioThreadState()
{
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(); }
private:
@@ -1587,6 +1643,7 @@ public:
nodes = Nodes{};
connections = Connections{};
nodeStates.clear();
topologyChanged (updateKind);
}
@@ -1625,6 +1682,7 @@ public:
{
connections.disconnectNode (nodeID);
auto result = nodes.removeNode (nodeID);
nodeStates.removeNode (nodeID);
topologyChanged (updateKind);
return result;
}


Loading…
Cancel
Save