Title: [283322] trunk/Source/WebCore
Revision
283322
Author
cdu...@apple.com
Date
2021-09-30 12:23:43 -0700 (Thu, 30 Sep 2021)

Log Message

Web Audio panner node quality deteriorates over time
https://bugs.webkit.org/show_bug.cgi?id=230950
<rdar://problem/83675934>

Reviewed by Eric Carlson.

When profiling the test case, I noticed what we were spending most of the CPU time
under AudioParamTimeline::valuesForFrameRangeImpl() / isEventCurrent() called from
PannerNode::process(). The reason for that is that the number of events in the
AudioParamTimeline would keep growing unboundedly and it would make the
valuesForFrameRangeImpl() implementation more and more expensive over time, since
it has to iterate over all events.

To address the issue, valuesForFrameRangeImpl() now keeps track of the events
that it had to skip because they were in the past. Then, at the end of the loop,
it removes the outdated events so it won't have to iterate over them the next
time around. This behavior is similar to what Blink does.

* Modules/webaudio/AudioParamTimeline.cpp:
(WebCore::AudioParamTimeline::removeOldEvents):
(WebCore::AudioParamTimeline::valuesForFrameRangeImpl):
* Modules/webaudio/AudioParamTimeline.h:

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (283321 => 283322)


--- trunk/Source/WebCore/ChangeLog	2021-09-30 19:15:38 UTC (rev 283321)
+++ trunk/Source/WebCore/ChangeLog	2021-09-30 19:23:43 UTC (rev 283322)
@@ -1,3 +1,28 @@
+2021-09-30  Chris Dumez  <cdu...@apple.com>
+
+        Web Audio panner node quality deteriorates over time
+        https://bugs.webkit.org/show_bug.cgi?id=230950
+        <rdar://problem/83675934>
+
+        Reviewed by Eric Carlson.
+
+        When profiling the test case, I noticed what we were spending most of the CPU time
+        under AudioParamTimeline::valuesForFrameRangeImpl() / isEventCurrent() called from
+        PannerNode::process(). The reason for that is that the number of events in the
+        AudioParamTimeline would keep growing unboundedly and it would make the
+        valuesForFrameRangeImpl() implementation more and more expensive over time, since
+        it has to iterate over all events.
+
+        To address the issue, valuesForFrameRangeImpl() now keeps track of the events
+        that it had to skip because they were in the past. Then, at the end of the loop,
+        it removes the outdated events so it won't have to iterate over them the next
+        time around. This behavior is similar to what Blink does.
+
+        * Modules/webaudio/AudioParamTimeline.cpp:
+        (WebCore::AudioParamTimeline::removeOldEvents):
+        (WebCore::AudioParamTimeline::valuesForFrameRangeImpl):
+        * Modules/webaudio/AudioParamTimeline.h:
+
 2021-09-30  Ziran Sun  <z...@igalia.com>
 
         [css-grid]  Transfer sizes from the aspect-ratio while resolving min-length for auto repetitions

Modified: trunk/Source/WebCore/Modules/webaudio/AudioParamTimeline.cpp (283321 => 283322)


--- trunk/Source/WebCore/Modules/webaudio/AudioParamTimeline.cpp	2021-09-30 19:15:38 UTC (rev 283321)
+++ trunk/Source/WebCore/Modules/webaudio/AudioParamTimeline.cpp	2021-09-30 19:23:43 UTC (rev 283322)
@@ -324,6 +324,16 @@
     m_events.remove(firstEventToRemove, m_events.size() - firstEventToRemove);
 }
 
+void AudioParamTimeline::removeOldEvents(size_t eventCount)
+{
+    ASSERT(eventCount <= m_events.size());
+    if (m_events.isEmpty())
+        return;
+
+    // Always leave at least one event in the list.
+    m_events.remove(0, std::min(eventCount, m_events.size() - 1));
+}
+
 std::optional<float> AudioParamTimeline::valueForContextTime(BaseAudioContext& context, float defaultValue, float minValue, float maxValue)
 {
     {
@@ -397,6 +407,7 @@
     }
 
     float value = defaultValue;
+    size_t numberOfSkippedEvents = 0;
 
     // Go through each event and render the value buffer where the times overlap,
     // stopping when we've rendered all the requested values.
@@ -408,8 +419,10 @@
         auto* nextEvent = i < n - 1 ? &m_events[i + 1] : nullptr;
 
         // Wait until we get a more recent event.
-        if (!isEventCurrent(*event, nextEvent, currentFrame, sampleRate))
+        if (!isEventCurrent(*event, nextEvent, currentFrame, sampleRate)) {
+            ++numberOfSkippedEvents;
             continue;
+        }
 
         auto nextEventType = nextEvent ? static_cast<ParamEvent::Type>(nextEvent->type()) : ParamEvent::LastType /* unknown */;
 
@@ -482,6 +495,10 @@
         }
     }
 
+    // Drop outdated events that we skipped so we don't have to go through them again in the future.
+    if (numberOfSkippedEvents > 0)
+        removeOldEvents(numberOfSkippedEvents);
+
     // If there's any time left after processing the last event then just propagate the last value
     // to the end of the values buffer.
     fillWithValue(values, value, numberOfValues, writeIndex);

Modified: trunk/Source/WebCore/Modules/webaudio/AudioParamTimeline.h (283321 => 283322)


--- trunk/Source/WebCore/Modules/webaudio/AudioParamTimeline.h	2021-09-30 19:15:38 UTC (rev 283321)
+++ trunk/Source/WebCore/Modules/webaudio/AudioParamTimeline.h	2021-09-30 19:23:43 UTC (rev 283322)
@@ -190,6 +190,7 @@
     };
 
     void removeCancelledEvents(size_t firstEventToRemove) WTF_REQUIRES_LOCK(m_eventsLock);
+    void removeOldEvents(size_t eventCount) WTF_REQUIRES_LOCK(m_eventsLock);
     ExceptionOr<void> insertEvent(ParamEvent&&) WTF_REQUIRES_LOCK(m_eventsLock);
     float valuesForFrameRangeImpl(size_t startFrame, size_t endFrame, float defaultValue, float* values, unsigned numberOfValues, double sampleRate, double controlRate) WTF_REQUIRES_LOCK(m_eventsLock);
     float linearRampAtTime(Seconds t, float value1, Seconds time1, float value2, Seconds time2);
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to