- Revision
- 274767
- Author
- [email protected]
- Date
- 2021-03-22 11:51:43 -0700 (Mon, 22 Mar 2021)
Log Message
Avoid heap allocation under AudioNodeInput::disable() / AudioNodeInput::enable()
https://bugs.webkit.org/show_bug.cgi?id=223529
Reviewed by Eric Carlson.
Avoid heap allocation under AudioNodeInput::disable() / AudioNodeInput::enable() since those can
get called on the audio thread.
AudioNodeInput used to have a m_disabledOutputs container to keep disabled outputs separated
from enabled ones (in m_outputs). Instead, we now store all outputs in m_outputs with a 'isEnabled'
flag. As a result, we no longer need to make any heap allocations when enabling/disabling an
output, we merely need to flip a flag in m_outputs.
For convenience, since clients of rendering outputs only care about enabled outputs, I introduced
a new RenderingOutputCollection container with iterators. This way clients do not need to worry
about disabled outputs.
* Modules/webaudio/AudioNodeInput.cpp:
(WebCore::AudioNodeInput::connect):
(WebCore::AudioNodeInput::disconnect):
(WebCore::AudioNodeInput::didDisableOutput):
(WebCore::AudioNodeInput::didEnableOutput):
(WebCore::AudioNodeInput::bus):
(WebCore::AudioNodeInput::sumAllConnections):
(WebCore::AudioNodeInput::pull):
* Modules/webaudio/AudioNodeInput.h:
* Modules/webaudio/AudioNodeOutput.cpp:
(WebCore::AudioNodeOutput::disable):
(WebCore::AudioNodeOutput::enable):
* Modules/webaudio/AudioParam.cpp:
(WebCore::AudioParam::calculateFinalValues):
* Modules/webaudio/AudioSummingJunction.cpp:
(WebCore::AudioSummingJunction::numberOfConnections const):
(WebCore::AudioSummingJunction::addOutput):
(WebCore::AudioSummingJunction::removeOutput):
(WebCore::AudioSummingJunction::updateRenderingState):
(WebCore::AudioSummingJunction::maximumNumberOfChannels const):
(WebCore::AudioSummingJunction::didDisableOutput):
(WebCore::AudioSummingJunction::didEnableOutput):
(WebCore::AudioSummingJunction::RenderingOutputCollection::RenderingOutput::RenderingOutput):
(WebCore::WebCore::AudioSummingJunction::RenderingOutputCollection::remove):
(WebCore::WebCore::AudioSummingJunction::RenderingOutputCollection::setEnabled):
(WebCore::WebCore::AudioSummingJunction::RenderingOutputCollection::RenderingOutputCollection):
* Modules/webaudio/AudioSummingJunction.h:
(WebCore::AudioSummingJunction::RenderingOutputCollection::isEmpty const):
(WebCore::AudioSummingJunction::RenderingOutputCollection::size const):
(WebCore::AudioSummingJunction::RenderingOutputCollection::clear):
(WebCore::AudioSummingJunction::RenderingOutputCollection::append):
(WebCore::AudioSummingJunction::RenderingOutputCollection::ConstIterator::ConstIterator):
(WebCore::AudioSummingJunction::RenderingOutputCollection::ConstIterator::operator* const):
(WebCore::AudioSummingJunction::RenderingOutputCollection::ConstIterator::operator!= const):
(WebCore::AudioSummingJunction::RenderingOutputCollection::ConstIterator::operator++):
(WebCore::AudioSummingJunction::RenderingOutputCollection::begin const):
(WebCore::AudioSummingJunction::RenderingOutputCollection::end const):
(WebCore::AudioSummingJunction::renderingOutputs const):
(WebCore::AudioSummingJunction::isConnected const):
* Modules/webaudio/WebKitAudioPannerNode.cpp:
(WebCore::WebKitAudioPannerNode::notifyAudioSourcesConnectedToNode):
Modified Paths
Diff
Modified: trunk/Source/WebCore/ChangeLog (274766 => 274767)
--- trunk/Source/WebCore/ChangeLog 2021-03-22 18:47:35 UTC (rev 274766)
+++ trunk/Source/WebCore/ChangeLog 2021-03-22 18:51:43 UTC (rev 274767)
@@ -1,3 +1,64 @@
+2021-03-22 Chris Dumez <[email protected]>
+
+ Avoid heap allocation under AudioNodeInput::disable() / AudioNodeInput::enable()
+ https://bugs.webkit.org/show_bug.cgi?id=223529
+
+ Reviewed by Eric Carlson.
+
+ Avoid heap allocation under AudioNodeInput::disable() / AudioNodeInput::enable() since those can
+ get called on the audio thread.
+
+ AudioNodeInput used to have a m_disabledOutputs container to keep disabled outputs separated
+ from enabled ones (in m_outputs). Instead, we now store all outputs in m_outputs with a 'isEnabled'
+ flag. As a result, we no longer need to make any heap allocations when enabling/disabling an
+ output, we merely need to flip a flag in m_outputs.
+
+ For convenience, since clients of rendering outputs only care about enabled outputs, I introduced
+ a new RenderingOutputCollection container with iterators. This way clients do not need to worry
+ about disabled outputs.
+
+ * Modules/webaudio/AudioNodeInput.cpp:
+ (WebCore::AudioNodeInput::connect):
+ (WebCore::AudioNodeInput::disconnect):
+ (WebCore::AudioNodeInput::didDisableOutput):
+ (WebCore::AudioNodeInput::didEnableOutput):
+ (WebCore::AudioNodeInput::bus):
+ (WebCore::AudioNodeInput::sumAllConnections):
+ (WebCore::AudioNodeInput::pull):
+ * Modules/webaudio/AudioNodeInput.h:
+ * Modules/webaudio/AudioNodeOutput.cpp:
+ (WebCore::AudioNodeOutput::disable):
+ (WebCore::AudioNodeOutput::enable):
+ * Modules/webaudio/AudioParam.cpp:
+ (WebCore::AudioParam::calculateFinalValues):
+ * Modules/webaudio/AudioSummingJunction.cpp:
+ (WebCore::AudioSummingJunction::numberOfConnections const):
+ (WebCore::AudioSummingJunction::addOutput):
+ (WebCore::AudioSummingJunction::removeOutput):
+ (WebCore::AudioSummingJunction::updateRenderingState):
+ (WebCore::AudioSummingJunction::maximumNumberOfChannels const):
+ (WebCore::AudioSummingJunction::didDisableOutput):
+ (WebCore::AudioSummingJunction::didEnableOutput):
+ (WebCore::AudioSummingJunction::RenderingOutputCollection::RenderingOutput::RenderingOutput):
+ (WebCore::WebCore::AudioSummingJunction::RenderingOutputCollection::remove):
+ (WebCore::WebCore::AudioSummingJunction::RenderingOutputCollection::setEnabled):
+ (WebCore::WebCore::AudioSummingJunction::RenderingOutputCollection::RenderingOutputCollection):
+ * Modules/webaudio/AudioSummingJunction.h:
+ (WebCore::AudioSummingJunction::RenderingOutputCollection::isEmpty const):
+ (WebCore::AudioSummingJunction::RenderingOutputCollection::size const):
+ (WebCore::AudioSummingJunction::RenderingOutputCollection::clear):
+ (WebCore::AudioSummingJunction::RenderingOutputCollection::append):
+ (WebCore::AudioSummingJunction::RenderingOutputCollection::ConstIterator::ConstIterator):
+ (WebCore::AudioSummingJunction::RenderingOutputCollection::ConstIterator::operator* const):
+ (WebCore::AudioSummingJunction::RenderingOutputCollection::ConstIterator::operator!= const):
+ (WebCore::AudioSummingJunction::RenderingOutputCollection::ConstIterator::operator++):
+ (WebCore::AudioSummingJunction::RenderingOutputCollection::begin const):
+ (WebCore::AudioSummingJunction::RenderingOutputCollection::end const):
+ (WebCore::AudioSummingJunction::renderingOutputs const):
+ (WebCore::AudioSummingJunction::isConnected const):
+ * Modules/webaudio/WebKitAudioPannerNode.cpp:
+ (WebCore::WebKitAudioPannerNode::notifyAudioSourcesConnectedToNode):
+
2021-03-22 Youenn Fablet <[email protected]>
Implement RTCDataChannel transfer out of process
Modified: trunk/Source/WebCore/Modules/webaudio/AudioNodeInput.cpp (274766 => 274767)
--- trunk/Source/WebCore/Modules/webaudio/AudioNodeInput.cpp 2021-03-22 18:47:35 UTC (rev 274766)
+++ trunk/Source/WebCore/Modules/webaudio/AudioNodeInput.cpp 2021-03-22 18:51:43 UTC (rev 274767)
@@ -53,13 +53,7 @@
if (!output || !node())
return;
- auto addPotentiallyDisabledOutput = [this](AudioNodeOutput& output) {
- if (output.isEnabled())
- return addOutput(output);
- return m_disabledOutputs.add(&output).isNewEntry;
- };
-
- if (addPotentiallyDisabledOutput(*output))
+ if (addOutput(*output))
output->addInput(this);
}
@@ -76,48 +70,23 @@
output->removeInput(this); // Note: it's important to return immediately after this since the node may be deleted.
return;
}
-
- // Otherwise, try to disconnect from disabled connections.
- if (m_disabledOutputs.remove(output)) {
- output->removeInput(this); // Note: it's important to return immediately after this since the node may be deleted.
- return;
- }
ASSERT_NOT_REACHED();
}
-void AudioNodeInput::disable(AudioNodeOutput* output)
+void AudioNodeInput::outputEnabledStateChanged(AudioNodeOutput& output)
{
ASSERT(context().isGraphOwner());
+ AudioSummingJunction::outputEnabledStateChanged(output);
- ASSERT(output && node());
- if (!output || !node())
- return;
-
- m_disabledOutputs.add(output);
- bool wasRemoved = removeOutput(*output);
- ASSERT_UNUSED(wasRemoved, wasRemoved);
-
// Propagate disabled state to outputs.
- node()->disableOutputsIfNecessary();
-}
-
-void AudioNodeInput::enable(AudioNodeOutput* output)
-{
- ASSERT(context().isGraphOwner());
-
- ASSERT(output && node());
- if (!output || !node())
+ ASSERT(node());
+ if (!node())
return;
-
- ASSERT(m_disabledOutputs.contains(output));
-
- // Move output from disabled list to active list.
- addOutput(*output);
- m_disabledOutputs.remove(output);
-
- // Propagate enabled state to outputs.
- node()->enableOutputsIfNecessary();
+ if (output.isEnabled())
+ node()->enableOutputsIfNecessary();
+ else
+ node()->disableOutputsIfNecessary();
}
void AudioNodeInput::didUpdate()
@@ -158,7 +127,7 @@
// Handle single connection specially to allow for in-place processing.
if (numberOfRenderingConnections() == 1 && node()->channelCountMode() == ChannelCountMode::Max)
- return renderingOutput(0)->bus();
+ return (*renderingOutputs().begin())->bus();
// Multiple connections case or complex ChannelCountMode (or no connections).
return internalSummingBus();
@@ -186,7 +155,7 @@
auto interpretation = node()->channelInterpretation();
- for (auto& output : m_renderingOutputs) {
+ for (auto* output : renderingOutputs()) {
ASSERT(output);
// Render audio from this output.
@@ -201,16 +170,17 @@
{
ASSERT(context().isAudioThread());
+ auto numberOfRenderingConnections = this->numberOfRenderingConnections();
// Handle single connection case.
- if (numberOfRenderingConnections() == 1 && node()->channelCountMode() == ChannelCountMode::Max) {
+ if (numberOfRenderingConnections == 1 && node()->channelCountMode() == ChannelCountMode::Max) {
// The output will optimize processing using inPlaceBus if it's able.
- AudioNodeOutput* output = this->renderingOutput(0);
+ AudioNodeOutput* output = *renderingOutputs().begin();
return output->pull(inPlaceBus, framesToProcess);
}
AudioBus* internalSummingBus = this->internalSummingBus();
- if (!numberOfRenderingConnections()) {
+ if (!numberOfRenderingConnections) {
// At least, generate silence if we're not connected to anything.
// FIXME: if we wanted to get fancy, we could propagate a 'silent hint' here to optimize the downstream graph processing.
internalSummingBus->zero();
Modified: trunk/Source/WebCore/Modules/webaudio/AudioNodeInput.h (274766 => 274767)
--- trunk/Source/WebCore/Modules/webaudio/AudioNodeInput.h 2021-03-22 18:47:35 UTC (rev 274766)
+++ trunk/Source/WebCore/Modules/webaudio/AudioNodeInput.h 2021-03-22 18:51:43 UTC (rev 274767)
@@ -55,11 +55,8 @@
void connect(AudioNodeOutput*);
void disconnect(AudioNodeOutput*);
- // disable() will take the output out of the active connections list and set aside in a disabled list.
- // enable() will put the output back into the active connections list.
// Must be called with the context's graph lock.
- void enable(AudioNodeOutput*);
- void disable(AudioNodeOutput*);
+ void outputEnabledStateChanged(AudioNodeOutput&) final;
// pull() processes all of the AudioNodes connected to us.
// In the case of multiple connections it sums the result into an internal summing bus.
@@ -82,11 +79,6 @@
private:
AudioNode* m_node;
- // m_disabledOutputs contains the AudioNodeOutputs which are disabled (will not be processed) by the audio graph rendering.
- // But, from _javascript_'s perspective, these outputs are still connected to us.
- // Generally, these represent disabled connections from "notes" which have finished playing but are not yet garbage collected.
- HashSet<AudioNodeOutput*> m_disabledOutputs;
-
// Called from context's audio thread.
AudioBus* internalSummingBus();
void sumAllConnections(AudioBus* summingBus, size_t framesToProcess);
Modified: trunk/Source/WebCore/Modules/webaudio/AudioNodeOutput.cpp (274766 => 274767)
--- trunk/Source/WebCore/Modules/webaudio/AudioNodeOutput.cpp 2021-03-22 18:47:35 UTC (rev 274766)
+++ trunk/Source/WebCore/Modules/webaudio/AudioNodeOutput.cpp 2021-03-22 18:51:43 UTC (rev 274767)
@@ -225,11 +225,12 @@
{
ASSERT(context().isGraphOwner());
- if (m_isEnabled) {
- for (auto& input : m_inputs.keys())
- input->disable(this);
- m_isEnabled = false;
- }
+ if (!m_isEnabled)
+ return;
+
+ m_isEnabled = false;
+ for (auto& input : m_inputs.keys())
+ input->outputEnabledStateChanged(*this);
}
void AudioNodeOutput::enable()
@@ -236,11 +237,12 @@
{
ASSERT(context().isGraphOwner());
- if (!m_isEnabled) {
- for (auto& input : m_inputs.keys())
- input->enable(this);
- m_isEnabled = true;
- }
+ if (m_isEnabled)
+ return;
+
+ m_isEnabled = true;
+ for (auto& input : m_inputs.keys())
+ input->outputEnabledStateChanged(*this);
}
} // namespace WebCore
Modified: trunk/Source/WebCore/Modules/webaudio/AudioParam.cpp (274766 => 274767)
--- trunk/Source/WebCore/Modules/webaudio/AudioParam.cpp 2021-03-22 18:47:35 UTC (rev 274766)
+++ trunk/Source/WebCore/Modules/webaudio/AudioParam.cpp 2021-03-22 18:51:43 UTC (rev 274767)
@@ -281,7 +281,7 @@
ASSERT(numberOfValues <= AudioUtilities::renderQuantumSize);
m_summingBus->setChannelMemory(0, values, sampleAccurate ? numberOfValues : 1);
- for (auto& output : m_renderingOutputs) {
+ for (auto* output : renderingOutputs()) {
ASSERT(output);
// Render audio from this output.
Modified: trunk/Source/WebCore/Modules/webaudio/AudioSummingJunction.cpp (274766 => 274767)
--- trunk/Source/WebCore/Modules/webaudio/AudioSummingJunction.cpp 2021-03-22 18:47:35 UTC (rev 274766)
+++ trunk/Source/WebCore/Modules/webaudio/AudioSummingJunction.cpp 2021-03-22 18:51:43 UTC (rev 274767)
@@ -60,10 +60,13 @@
if (!m_outputs.add(&output).isNewEntry)
return false;
- if (m_pendingRenderingOutputs.isEmpty())
- m_pendingRenderingOutputs = copyToVector(m_outputs);
+ if (!output.isEnabled())
+ return true;
+
+ if (!m_pendingRenderingOutputs)
+ m_pendingRenderingOutputs.emplace(m_outputs);
else
- m_pendingRenderingOutputs.append(&output);
+ m_pendingRenderingOutputs->append(output);
markRenderingStateAsDirty();
return true;
@@ -75,11 +78,16 @@
if (!m_outputs.remove(&output))
return false;
- if (m_pendingRenderingOutputs.isEmpty())
- m_pendingRenderingOutputs = copyToVector(m_outputs);
- else
- m_pendingRenderingOutputs.removeFirst(&output);
+ if (!output.isEnabled())
+ return true;
+ if (!m_pendingRenderingOutputs)
+ m_pendingRenderingOutputs.emplace(m_outputs);
+ else {
+ bool wasRemoved = m_pendingRenderingOutputs->remove(output);
+ ASSERT_UNUSED(wasRemoved, wasRemoved);
+ }
+
markRenderingStateAsDirty();
return true;
}
@@ -90,9 +98,13 @@
if (m_renderingStateNeedUpdating && canUpdateState()) {
// Copy from m_outputs to m_renderingOutputs.
- m_renderingOutputs = std::exchange(m_pendingRenderingOutputs, { });
- for (auto& output : m_renderingOutputs)
- output->updateRenderingState();
+ if (!m_pendingRenderingOutputs)
+ m_renderingOutputs = { };
+ else {
+ m_renderingOutputs = *std::exchange(m_pendingRenderingOutputs, WTF::nullopt);
+ for (auto* output : m_renderingOutputs)
+ output->updateRenderingState();
+ }
didUpdate();
@@ -104,6 +116,9 @@
{
unsigned maxChannels = 0;
for (auto* output : m_outputs) {
+ if (!output->isEnabled())
+ continue;
+
// Use output()->numberOfChannels() instead of output->bus()->numberOfChannels(),
// because the calling of AudioNodeOutput::bus() is not safe here.
maxChannels = std::max(maxChannels, output->numberOfChannels());
@@ -111,6 +126,30 @@
return maxChannels;
}
+void AudioSummingJunction::outputEnabledStateChanged(AudioNodeOutput& output)
+{
+ ASSERT(context().isGraphOwner());
+ if (!m_pendingRenderingOutputs)
+ m_pendingRenderingOutputs.emplace(m_outputs);
+ else
+ m_pendingRenderingOutputs->updatedEnabledState(output);
+ markRenderingStateAsDirty();
+}
+
+inline AudioSummingJunction::RenderingOutputCollection::RenderingOutput::RenderingOutput(AudioNodeOutput* output)
+ : output(output)
+ , isEnabled(output->isEnabled())
+{ }
+
+void WebCore::AudioSummingJunction::RenderingOutputCollection::updatedEnabledState(AudioNodeOutput& output)
+{
+ auto index = m_outputs.find(&output);
+ ASSERT(index != notFound);
+ if (index == notFound)
+ return;
+ m_outputs[index].isEnabled = output.isEnabled();
+}
+
} // namespace WebCore
#endif // ENABLE(WEB_AUDIO)
Modified: trunk/Source/WebCore/Modules/webaudio/AudioSummingJunction.h (274766 => 274767)
--- trunk/Source/WebCore/Modules/webaudio/AudioSummingJunction.h 2021-03-22 18:47:35 UTC (rev 274766)
+++ trunk/Source/WebCore/Modules/webaudio/AudioSummingJunction.h 2021-03-22 18:51:43 UTC (rev 274767)
@@ -25,6 +25,7 @@
#pragma once
#include "AudioBus.h"
+#include <iterator>
#include <wtf/HashSet.h>
#include <wtf/Vector.h>
@@ -48,11 +49,76 @@
// This must be called when we own the context's graph lock in the audio thread at the very start or end of the render quantum.
void updateRenderingState();
+ class RenderingOutputCollection {
+ public:
+ RenderingOutputCollection() = default;
+ RenderingOutputCollection(const HashSet<AudioNodeOutput*>& outputs)
+ : m_outputs(copyToVectorOf<RenderingOutput>(outputs))
+ {
+ }
+
+ bool isEmpty() const { return begin() == end(); }
+ size_t size() const { return std::distance(begin(), end()); }
+
+ void append(AudioNodeOutput& output) { m_outputs.append(&output); }
+ bool remove(AudioNodeOutput& output) { return m_outputs.removeFirst(&output); }
+ void updatedEnabledState(AudioNodeOutput&);
+
+ struct RenderingOutput {
+ RenderingOutput(AudioNodeOutput*);
+ bool operator==(const AudioNodeOutput* other) const { return output == other; }
+ AudioNodeOutput* output;
+ bool isEnabled;
+ };
+
+ class ConstIterator {
+ public:
+ using difference_type = size_t;
+ using value_type = AudioNodeOutput*;
+ using pointer = AudioNodeOutput**;
+ using reference = AudioNodeOutput*&;
+ using iterator_category = std::forward_iterator_tag;
+
+ ConstIterator(const Vector<RenderingOutput>& outputs, Vector<RenderingOutput>::const_iterator position)
+ : m_position(position)
+ , m_end(outputs.end())
+ {
+ skipDisabledOutputs();
+ }
+
+ AudioNodeOutput* operator*() const { return m_position->output; }
+ constexpr bool operator==(const ConstIterator& other) const { return m_position == other.m_position; }
+ constexpr bool operator!=(const ConstIterator& other) const { return !(*this == other); }
+ ConstIterator& operator++()
+ {
+ ASSERT(m_position != m_end);
+ ++m_position;
+ skipDisabledOutputs();
+ return *this;
+ }
+
+ private:
+ void skipDisabledOutputs()
+ {
+ while (m_position != m_end && !m_position->isEnabled)
+ ++m_position;
+ }
+
+ Vector<RenderingOutput>::const_iterator m_position;
+ Vector<RenderingOutput>::const_iterator m_end;
+ };
+
+ ConstIterator begin() const { return ConstIterator { m_outputs, m_outputs.begin() }; }
+ ConstIterator end() const { return ConstIterator { m_outputs, m_outputs.end() }; }
+
+ private:
+ Vector<RenderingOutput> m_outputs;
+ };
+
// Rendering code accesses its version of the current connections here.
+ const RenderingOutputCollection& renderingOutputs() const { return m_renderingOutputs; }
unsigned numberOfRenderingConnections() const { return m_renderingOutputs.size(); }
- AudioNodeOutput* renderingOutput(unsigned i) { return m_renderingOutputs[i]; }
- const AudioNodeOutput* renderingOutput(unsigned i) const { return m_renderingOutputs[i]; }
- bool isConnected() const { return numberOfRenderingConnections() > 0; }
+ bool isConnected() const { return !m_renderingOutputs.isEmpty(); }
virtual bool canUpdateState() = 0;
virtual void didUpdate() = 0;
@@ -62,29 +128,27 @@
void markRenderingStateAsDirty();
+ virtual void outputEnabledStateChanged(AudioNodeOutput&);
+
protected:
+ unsigned maximumNumberOfChannels() const;
+
+private:
Ref<BaseAudioContext> m_context;
- // numberOfConnections() should never be called from the audio rendering thread.
- // Instead numberOfRenderingConnections() and renderingOutput() should be used.
- unsigned numberOfConnections() const { return m_outputs.size(); }
-
- unsigned maximumNumberOfChannels() const;
-
// m_renderingOutputs is a copy of m_outputs which will never be modified during the graph rendering on the audio thread.
// This is the list which is used by the rendering code.
// Whenever m_outputs is modified, the context is told so it can later update m_renderingOutputs from m_outputs at a safe time.
// Most of the time, m_renderingOutputs is identical to m_outputs.
- Vector<AudioNodeOutput*> m_renderingOutputs;
+ RenderingOutputCollection m_renderingOutputs;
// m_renderingStateNeedUpdating keeps track if m_outputs is modified.
bool m_renderingStateNeedUpdating { false };
-private:
// m_outputs contains the AudioNodeOutputs representing current connections which are not disabled.
// The rendering code should never use this directly, but instead uses m_renderingOutputs.
HashSet<AudioNodeOutput*> m_outputs;
- Vector<AudioNodeOutput*> m_pendingRenderingOutputs;
+ Optional<RenderingOutputCollection> m_pendingRenderingOutputs;
};
} // namespace WebCore
Modified: trunk/Source/WebCore/Modules/webaudio/WebKitAudioPannerNode.cpp (274766 => 274767)
--- trunk/Source/WebCore/Modules/webaudio/WebKitAudioPannerNode.cpp 2021-03-22 18:47:35 UTC (rev 274766)
+++ trunk/Source/WebCore/Modules/webaudio/WebKitAudioPannerNode.cpp 2021-03-22 18:51:43 UTC (rev 274767)
@@ -319,8 +319,7 @@
AudioNodeInput* input = node->input(i);
// For each input, go through all of its connections, looking for AudioBufferSourceNodes.
- for (unsigned j = 0; j < input->numberOfRenderingConnections(); ++j) {
- AudioNodeOutput* connectedOutput = input->renderingOutput(j);
+ for (auto* connectedOutput : input->renderingOutputs()) {
AudioNode* connectedNode = connectedOutput->node();
if (visitedNodes.contains(connectedNode))
continue;