Changeset 283740 in webkit


Ignore:
Timestamp:
Oct 7, 2021 1:36:32 PM (10 months ago)
Author:
Chris Dumez
Message:

Cache PannerNode's azimuth, elevation and coneGain
https://bugs.webkit.org/show_bug.cgi?id=231314

Reviewed by Eric Carlson.

Cache PannerNode's azimuth, elevation and coneGain for better performance. I have noticed while profiling
https://downloads.scirra.com/labs/bugs/safaripannerquality/ that PannerNode::process() spends most of its
CPU time under PannerNode::calculateAzimuthElevation(). We shouldn't have to re-calculate those properties
for every rendering quantum.

  • Modules/webaudio/AudioListener.cpp:

(WebCore::AudioListener::updateDirtyState):

  • Modules/webaudio/AudioListener.h:

(WebCore::AudioListener::isPositionDirty const):
(WebCore::AudioListener::isOrientationDirty const):
(WebCore::AudioListener::isUpVectorDirty const):

  • Modules/webaudio/BaseAudioContext.cpp:

(WebCore::BaseAudioContext::handlePreRenderTasks):

  • Modules/webaudio/PannerNode.cpp:

(WebCore::PannerNode::process):
(WebCore::PannerNode::processSampleAccurateValues):
(WebCore::PannerNode::setDistanceModelForBindings):
(WebCore::PannerNode::setRefDistanceForBindings):
(WebCore::PannerNode::setMaxDistanceForBindings):
(WebCore::PannerNode::setRolloffFactorForBindings):
(WebCore::PannerNode::setConeOuterGainForBindings):
(WebCore::PannerNode::setConeOuterAngleForBindings):
(WebCore::PannerNode::setConeInnerAngleForBindings):
(WebCore::PannerNode::calculateAzimuthElevation):
(WebCore::PannerNode::azimuthElevation const):
(WebCore::PannerNode::calculateDistanceConeGain):
(WebCore::PannerNode::distanceConeGain):
(WebCore::PannerNode::invalidateCachedPropertiesIfNecessary):
(WebCore::PannerNode::azimuthElevation): Deleted.

  • Modules/webaudio/PannerNode.h:
  • platform/audio/Cone.cpp:

(WebCore::ConeEffect::gain const):
(WebCore::ConeEffect::gain): Deleted.

  • platform/audio/Cone.h:
  • platform/audio/Distance.cpp:

(WebCore::DistanceEffect::gain const):
(WebCore::DistanceEffect::linearGain const):
(WebCore::DistanceEffect::inverseGain const):
(WebCore::DistanceEffect::exponentialGain const):
(WebCore::DistanceEffect::gain): Deleted.
(WebCore::DistanceEffect::linearGain): Deleted.
(WebCore::DistanceEffect::inverseGain): Deleted.
(WebCore::DistanceEffect::exponentialGain): Deleted.

  • platform/audio/Distance.h:
Location:
trunk/Source/WebCore
Files:
10 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r283736 r283740  
     12021-10-07  Chris Dumez  <cdumez@apple.com>
     2
     3        Cache PannerNode's azimuth, elevation and coneGain
     4        https://bugs.webkit.org/show_bug.cgi?id=231314
     5
     6        Reviewed by Eric Carlson.
     7
     8        Cache PannerNode's azimuth, elevation and coneGain for better performance. I have noticed while profiling
     9        https://downloads.scirra.com/labs/bugs/safaripannerquality/ that PannerNode::process() spends most of its
     10        CPU time under PannerNode::calculateAzimuthElevation(). We shouldn't have to re-calculate those properties
     11        for every rendering quantum.
     12
     13        * Modules/webaudio/AudioListener.cpp:
     14        (WebCore::AudioListener::updateDirtyState):
     15        * Modules/webaudio/AudioListener.h:
     16        (WebCore::AudioListener::isPositionDirty const):
     17        (WebCore::AudioListener::isOrientationDirty const):
     18        (WebCore::AudioListener::isUpVectorDirty const):
     19        * Modules/webaudio/BaseAudioContext.cpp:
     20        (WebCore::BaseAudioContext::handlePreRenderTasks):
     21        * Modules/webaudio/PannerNode.cpp:
     22        (WebCore::PannerNode::process):
     23        (WebCore::PannerNode::processSampleAccurateValues):
     24        (WebCore::PannerNode::setDistanceModelForBindings):
     25        (WebCore::PannerNode::setRefDistanceForBindings):
     26        (WebCore::PannerNode::setMaxDistanceForBindings):
     27        (WebCore::PannerNode::setRolloffFactorForBindings):
     28        (WebCore::PannerNode::setConeOuterGainForBindings):
     29        (WebCore::PannerNode::setConeOuterAngleForBindings):
     30        (WebCore::PannerNode::setConeInnerAngleForBindings):
     31        (WebCore::PannerNode::calculateAzimuthElevation):
     32        (WebCore::PannerNode::azimuthElevation const):
     33        (WebCore::PannerNode::calculateDistanceConeGain):
     34        (WebCore::PannerNode::distanceConeGain):
     35        (WebCore::PannerNode::invalidateCachedPropertiesIfNecessary):
     36        (WebCore::PannerNode::azimuthElevation): Deleted.
     37        * Modules/webaudio/PannerNode.h:
     38        * platform/audio/Cone.cpp:
     39        (WebCore::ConeEffect::gain const):
     40        (WebCore::ConeEffect::gain): Deleted.
     41        * platform/audio/Cone.h:
     42        * platform/audio/Distance.cpp:
     43        (WebCore::DistanceEffect::gain const):
     44        (WebCore::DistanceEffect::linearGain const):
     45        (WebCore::DistanceEffect::inverseGain const):
     46        (WebCore::DistanceEffect::exponentialGain const):
     47        (WebCore::DistanceEffect::gain): Deleted.
     48        (WebCore::DistanceEffect::linearGain): Deleted.
     49        (WebCore::DistanceEffect::inverseGain): Deleted.
     50        (WebCore::DistanceEffect::exponentialGain): Deleted.
     51        * platform/audio/Distance.h:
     52
    1532021-10-07  Michael Catanzaro  <mcatanzaro@gnome.org>
    254
  • trunk/Source/WebCore/Modules/webaudio/AudioListener.cpp

    r275668 r283740  
    124124}
    125125
     126void AudioListener::updateDirtyState()
     127{
     128    ASSERT(!isMainThread());
     129
     130    auto lastPosition = std::exchange(m_lastPosition, position());
     131    m_isPositionDirty = lastPosition != m_lastPosition;
     132
     133    auto lastOrientation = std::exchange(m_lastOrientation, orientation());
     134    m_isOrientationDirty = lastOrientation != m_lastOrientation;
     135
     136    auto lastUpVector = std::exchange(m_lastUpVector, upVector());
     137    m_isUpVectorDirty = lastUpVector != m_lastUpVector;
     138}
     139
    126140const float* AudioListener::positionXValues(size_t framesToProcess)
    127141{
  • trunk/Source/WebCore/Modules/webaudio/AudioListener.h

    r277530 r283740  
    8888    void updateValuesIfNeeded(size_t framesToProcess);
    8989
     90    void updateDirtyState();
     91    bool isPositionDirty() const { return m_isPositionDirty; }
     92    bool isOrientationDirty() const { return m_isOrientationDirty; }
     93    bool isUpVectorDirty() const { return m_isUpVectorDirty; }
     94
    9095protected:
    9196    explicit AudioListener(BaseAudioContext&);
     
    117122    AudioFloatArray m_upYValues;
    118123    AudioFloatArray m_upZValues;
     124
     125    FloatPoint3D m_lastPosition;
     126    FloatPoint3D m_lastOrientation;
     127    FloatPoint3D m_lastUpVector;
     128    bool m_isPositionDirty { false };
     129    bool m_isOrientationDirty { false };
     130    bool m_isUpVectorDirty { false };
    119131};
    120132
  • trunk/Source/WebCore/Modules/webaudio/BaseAudioContext.cpp

    r282755 r283740  
    550550        updateAutomaticPullNodes();
    551551        m_outputPosition = outputPosition;
     552
     553        m_listener->updateDirtyState();
    552554    }
    553555}
  • trunk/Source/WebCore/Modules/webaudio/PannerNode.cpp

    r278284 r283740  
    149149    }
    150150
     151    invalidateCachedPropertiesIfNecessary();
     152
    151153    // Apply the panning effect.
    152     double azimuth;
    153     double elevation;
    154     azimuthElevation(&azimuth, &elevation);
     154    auto [azimuth, elevation] = azimuthElevation();
    155155    m_panner->pan(azimuth, elevation, source, destination, framesToProcess);
    156156
     
    227227        FloatPoint3D listenerUp(upX[k], upY[k], upZ[k]);
    228228
    229         calculateAzimuthElevation(&azimuth[k], &elevation[k], pannerPosition, listenerPosition, listenerFront, listenerUp);
     229        auto [calculatedAzimuth, calculatedElevation] = calculateAzimuthElevation(pannerPosition, listenerPosition, listenerFront, listenerUp);
     230        azimuth[k] = calculatedAzimuth;
     231        elevation[k] = calculatedElevation;
    230232
    231233        // Get distance and cone gain
    232         totalGain[k] = calculateDistanceConeGain(pannerPosition, orientation, listenerPosition);
     234        totalGain[k] = calculateDistanceConeGain(pannerPosition, orientation, listenerPosition, m_distanceEffect, m_coneEffect);
    233235    }
    234236
     
    341343    Locker locker { m_processLock };
    342344
     345    if (m_distanceEffect.model() == model)
     346        return;
     347
    343348    m_distanceEffect.setModel(model, true);
     349    m_cachedConeGain = std::nullopt;
    344350}
    345351
     
    354360    Locker locker { m_processLock };
    355361
     362    if (m_distanceEffect.refDistance() == refDistance)
     363        return { };
     364
    356365    m_distanceEffect.setRefDistance(refDistance);
     366    m_cachedConeGain = std::nullopt;
    357367    return { };
    358368}
     
    368378    Locker locker { m_processLock };
    369379
     380    if (m_distanceEffect.maxDistance() == maxDistance)
     381        return { };
     382
    370383    m_distanceEffect.setMaxDistance(maxDistance);
     384    m_cachedConeGain = std::nullopt;
    371385    return { };
    372386}
     
    382396    Locker locker { m_processLock };
    383397
     398    if (m_distanceEffect.rolloffFactor() == rolloffFactor)
     399        return { };
     400
    384401    m_distanceEffect.setRolloffFactor(rolloffFactor);
     402    m_cachedConeGain = std::nullopt;
    385403    return { };
    386404}
     
    396414    Locker locker { m_processLock };
    397415
     416    if (m_coneEffect.outerGain() == gain)
     417        return { };
     418
    398419    m_coneEffect.setOuterGain(gain);
     420    m_cachedConeGain = std::nullopt;
    399421    return { };
    400422}
     
    407429    Locker locker { m_processLock };
    408430
     431    if (m_coneEffect.outerAngle() == angle)
     432        return;
     433
    409434    m_coneEffect.setOuterAngle(angle);
     435    m_cachedConeGain = std::nullopt;
    410436}
    411437
     
    417443    Locker locker { m_processLock };
    418444
     445    if (m_coneEffect.innerAngle() == angle)
     446        return;
     447
    419448    m_coneEffect.setInnerAngle(angle);
     449    m_cachedConeGain = std::nullopt;
    420450}
    421451
     
    440470}
    441471
    442 void PannerNode::calculateAzimuthElevation(double* outAzimuth, double* outElevation, const FloatPoint3D& position, const FloatPoint3D& listenerPosition, const FloatPoint3D& listenerFront, const FloatPoint3D& listenerUp)
    443 {
    444     // FIXME: we should cache azimuth and elevation (if possible), so we only re-calculate if a change has been made.
    445 
     472auto PannerNode::calculateAzimuthElevation(const FloatPoint3D& position, const FloatPoint3D& listenerPosition, const FloatPoint3D& listenerFront, const FloatPoint3D& listenerUp) -> AzimuthElevation
     473{
    446474    // Calculate the source-listener vector
    447475    FloatPoint3D sourceListener = position - listenerPosition;
     
    449477    if (sourceListener.isZero()) {
    450478        // degenerate case if source and listener are at the same point
    451         *outAzimuth = 0.0;
    452         *outElevation = 0.0;
    453         return;
     479        return { };
    454480    }
    455481
     
    493519        elevation = -180.0 - elevation;
    494520
    495     if (outAzimuth)
    496         *outAzimuth = azimuth;
    497     if (outElevation)
    498         *outElevation = elevation;
    499 }
    500 
    501 void PannerNode::azimuthElevation(double* outAzimuth, double* outElevation)
     521    return { azimuth, elevation };
     522}
     523
     524auto PannerNode::azimuthElevation() -> const AzimuthElevation&
    502525{
    503526    ASSERT(context().isAudioThread());
    504 
    505     calculateAzimuthElevation(outAzimuth, outElevation, position(), listener().position(), listener().orientation(), listener().upVector());
     527    auto& listener = this->listener();
     528    if (!m_cachedAzimuthElevation)
     529        m_cachedAzimuthElevation = calculateAzimuthElevation(position(), listener.position(), listener.orientation(), listener.upVector());
     530    return *m_cachedAzimuthElevation;
    506531}
    507532
     
    517542}
    518543
    519 float PannerNode::calculateDistanceConeGain(const FloatPoint3D& sourcePosition, const FloatPoint3D& orientation, const FloatPoint3D& listenerPosition)
     544float PannerNode::calculateDistanceConeGain(const FloatPoint3D& sourcePosition, const FloatPoint3D& orientation, const FloatPoint3D& listenerPosition, const DistanceEffect& distanceEffect, const ConeEffect& coneEffect)
    520545{
    521546    double listenerDistance = sourcePosition.distanceTo(listenerPosition);
    522     double distanceGain = m_distanceEffect.gain(listenerDistance);
    523 
    524     // FIXME: could optimize by caching coneGain
    525     double coneGain = m_coneEffect.gain(sourcePosition, orientation, listenerPosition);
     547    double distanceGain = distanceEffect.gain(listenerDistance);
     548    double coneGain = coneEffect.gain(sourcePosition, orientation, listenerPosition);
    526549
    527550    return float(distanceGain * coneGain);
     
    531554{
    532555    ASSERT(context().isAudioThread());
    533 
    534     return calculateDistanceConeGain(position(), orientation(), listener().position());
     556    if (!m_cachedConeGain)
     557        m_cachedConeGain = calculateDistanceConeGain(position(), orientation(), listener().position(), m_distanceEffect, m_coneEffect);
     558    return *m_cachedConeGain;
    535559}
    536560
     
    551575}
    552576
     577void PannerNode::invalidateCachedPropertiesIfNecessary()
     578{
     579    auto lastPosition = std::exchange(m_lastPosition, position());
     580    bool hasPositionChanged = m_lastPosition != lastPosition;
     581    auto lastOrientation = std::exchange(m_lastOrientation, position());
     582    bool hasOrientationChanged = m_lastOrientation != lastOrientation;
     583    auto& listener = this->listener();
     584
     585    if (hasPositionChanged || listener.isPositionDirty() || listener.isOrientationDirty() || listener.isUpVectorDirty())
     586        m_cachedAzimuthElevation = std::nullopt;
     587
     588    if (hasPositionChanged || hasOrientationChanged || listener.isPositionDirty())
     589        m_cachedConeGain = std::nullopt;
     590}
     591
    553592} // namespace WebCore
    554593
  • trunk/Source/WebCore/Modules/webaudio/PannerNode.h

    r278284 r283740  
    114114    PannerNode(BaseAudioContext&, const PannerOptions&);
    115115
    116     void calculateAzimuthElevation(double* outAzimuth, double* outElevation, const FloatPoint3D& position, const FloatPoint3D& listenerPosition, const FloatPoint3D& listenerForward, const FloatPoint3D& listenerUp) WTF_REQUIRES_LOCK(m_processLock);
    117     float calculateDistanceConeGain(const FloatPoint3D& position, const FloatPoint3D& orientation, const FloatPoint3D& listenerPosition) WTF_REQUIRES_LOCK(m_processLock);
     116    struct AzimuthElevation {
     117        double azimuth { 0. };
     118        double elevation { 0. };
     119    };
     120    static AzimuthElevation calculateAzimuthElevation(const FloatPoint3D& position, const FloatPoint3D& listenerPosition, const FloatPoint3D& listenerForward, const FloatPoint3D& listenerUp);
     121    static float calculateDistanceConeGain(const FloatPoint3D& position, const FloatPoint3D& orientation, const FloatPoint3D& listenerPosition, const DistanceEffect&, const ConeEffect&);
    118122
    119123    // Returns the combined distance and cone gain attenuation.
     
    122126    bool requiresTailProcessing() const final;
    123127
    124     void azimuthElevation(double* outAzimuth, double* outElevation) WTF_REQUIRES_LOCK(m_processLock);
     128    void invalidateCachedPropertiesIfNecessary() WTF_REQUIRES_LOCK(m_processLock);
     129
     130    const AzimuthElevation& azimuthElevation() WTF_REQUIRES_LOCK(m_processLock);
    125131    void processSampleAccurateValues(AudioBus* destination, const AudioBus* source, size_t framesToProcess) WTF_REQUIRES_LOCK(m_processLock);
    126132    bool hasSampleAccurateValues() const WTF_REQUIRES_LOCK(m_processLock);
     
    146152    Ref<AudioParam> m_orientationZ WTF_GUARDED_BY_LOCK(m_processLock);
    147153
     154    mutable std::optional<AzimuthElevation> m_cachedAzimuthElevation WTF_GUARDED_BY_LOCK(m_processLock);
     155    mutable std::optional<float> m_cachedConeGain WTF_GUARDED_BY_LOCK(m_processLock);
     156    FloatPoint3D m_lastPosition WTF_GUARDED_BY_LOCK(m_processLock);
     157    FloatPoint3D m_lastOrientation WTF_GUARDED_BY_LOCK(m_processLock);
     158
    148159    // Synchronize process() with setting of the panning model, source's location
    149160    // information, listener, distance parameters and sound cones.
  • trunk/Source/WebCore/platform/audio/Cone.cpp

    r267544 r283740  
    3838ConeEffect::ConeEffect() = default;
    3939
    40 double ConeEffect::gain(FloatPoint3D sourcePosition, FloatPoint3D sourceOrientation, FloatPoint3D listenerPosition)
     40double ConeEffect::gain(FloatPoint3D sourcePosition, FloatPoint3D sourceOrientation, FloatPoint3D listenerPosition) const
    4141{
    4242    if (sourceOrientation.isZero() || ((m_innerAngle == 360.0) && (m_outerAngle == 360.0)))
  • trunk/Source/WebCore/platform/audio/Cone.h

    r267544 r283740  
    4242
    4343    // Returns scalar gain for the given source/listener positions/orientations
    44     double gain(FloatPoint3D sourcePosition, FloatPoint3D sourceOrientation, FloatPoint3D listenerPosition);
     44    double gain(FloatPoint3D sourcePosition, FloatPoint3D sourceOrientation, FloatPoint3D listenerPosition) const;
    4545
    4646    // Angles in degrees
  • trunk/Source/WebCore/platform/audio/Distance.cpp

    r267544 r283740  
    4040DistanceEffect::DistanceEffect() = default;
    4141
    42 double DistanceEffect::gain(double distance)
     42double DistanceEffect::gain(double distance) const
    4343{
    4444    // don't go beyond maximum distance
     
    6161}
    6262
    63 double DistanceEffect::linearGain(double distance)
     63double DistanceEffect::linearGain(double distance) const
    6464{
    6565    auto clampedRolloffFactor = std::clamp(m_rolloffFactor, 0.0, 1.0);
     
    6969}
    7070
    71 double DistanceEffect::inverseGain(double distance)
     71double DistanceEffect::inverseGain(double distance) const
    7272{
    7373    return m_refDistance / (m_refDistance + m_rolloffFactor * (distance - m_refDistance));
    7474}
    7575
    76 double DistanceEffect::exponentialGain(double distance)
     76double DistanceEffect::exponentialGain(double distance) const
    7777{
    7878    return pow(distance / m_refDistance, -m_rolloffFactor);
  • trunk/Source/WebCore/platform/audio/Distance.h

    r278284 r283740  
    4747
    4848    // Returns scalar gain for the given distance the current distance model is used
    49     double gain(double distance);
     49    double gain(double distance) const;
    5050
    5151    DistanceModelType model() const { return m_model; }
     
    6767
    6868protected:
    69     double linearGain(double distance);
    70     double inverseGain(double distance);
    71     double exponentialGain(double distance);
     69    double linearGain(double distance) const;
     70    double inverseGain(double distance) const;
     71    double exponentialGain(double distance) const;
    7272
    7373    DistanceModelType m_model { DistanceModelType::Inverse };
Note: See TracChangeset for help on using the changeset viewer.