Changeset 269077 in webkit
- Timestamp:
- Oct 27, 2020 3:21:28 PM (21 months ago)
- Location:
- trunk
- Files:
-
- 2 added
- 5 edited
-
PerformanceTests/ChangeLog (modified) (1 diff)
-
PerformanceTests/Media/AudioElementCreation.html (added)
-
PerformanceTests/Media/VideoElementCreation.html (added)
-
Source/WebCore/ChangeLog (modified) (1 diff)
-
Source/WebCore/platform/audio/PlatformMediaSessionManager.cpp (modified) (6 diffs)
-
Source/WebCore/platform/audio/PlatformMediaSessionManager.h (modified) (3 diffs)
-
Source/WebCore/platform/audio/mac/AudioSessionMac.mm (modified) (8 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/PerformanceTests/ChangeLog
r269028 r269077 1 2020-10-27 Jer Noble <jer.noble@apple.com> 2 3 [Mac] Audio and Video element creation up to 300x slower than other browsers 4 https://bugs.webkit.org/show_bug.cgi?id=218206 5 <rdar://problem/62451019> 6 7 Reviewed by Eric Carlson. 8 9 * Media/AudioElementCreation.html: Added. 10 * Media/VideoElementCreation.html: Added. 11 1 12 2020-10-27 Caio Lima <ticaiolima@gmail.com> 2 13 -
trunk/Source/WebCore/ChangeLog
r269073 r269077 1 2020-10-27 Jer Noble <jer.noble@apple.com> 2 3 [Mac] Audio and Video element creation up to 300x slower than other browsers 4 https://bugs.webkit.org/show_bug.cgi?id=218206 5 <rdar://problem/62451019> 6 7 Reviewed by Eric Carlson. 8 9 Tests: PerformanceTests/Media/AudioElementCreation.html 10 PerformanceTests/Media/VideoElementCreation.html 11 12 Currently, a large percent of the element creation code occurrs as a result of adding its 13 session to PlatformMediaSessionManager, which forces iterating over all extant sessions and 14 then to set various properties of the audio hardware in response. This patch addresses the 15 bulk of those expensive calls, but more performance optimizations are available to further 16 reduce media element creation costs. 17 18 When an <audio> element is created, we set the preferred audio output buffer size to a large 19 value for performance reasons. However, there's no need to repeatedly call into CoreAudio if 20 the buffer size is already set to that same high value. Store the result of setting the 21 preferred buffer size, and also add a property change listener to detect other callers 22 modifying that same value, so that all set operations with identical sizes become no-ops, 23 and all queries just return cached values. 24 25 When any media element is created, the entire list of extant sessions is iterated and 26 properties on each are queried. Rather than do these inside the same run-loop, use a 27 TaskQueue to enqueue a task to query the list of created elements during the next run-loop. 28 29 Between these two optimization, the runtime cost of creating 1000 audio elements is reduced 30 (on this engineer's machine) from 2s to 40ms. 31 32 * platform/audio/PlatformMediaSessionManager.cpp: 33 (WebCore::PlatformMediaSessionManager::beginInterruption): 34 (WebCore::PlatformMediaSessionManager::addSession): 35 (WebCore::PlatformMediaSessionManager::removeSession): 36 (WebCore::PlatformMediaSessionManager::sessionStateChanged): 37 (WebCore::PlatformMediaSessionManager::forEachDocumentSession): 38 (WebCore::PlatformMediaSessionManager::forEachSession): 39 (WebCore::PlatformMediaSessionManager::anyOfSessions const): 40 * platform/audio/PlatformMediaSessionManager.h: 41 * platform/audio/mac/AudioSessionMac.mm: 42 (WebCore::AudioSessionPrivate::addSampleRateObserverIfNeeded): 43 (WebCore::AudioSessionPrivate::handleSampleRateChange): 44 (WebCore::AudioSessionPrivate::addBufferSizeObserverIfNeeded): 45 (WebCore::AudioSessionPrivate::handleBufferSizeChange): 46 (WebCore::AudioSession::sampleRate const): 47 (WebCore::AudioSession::bufferSize const): 48 (WebCore::AudioSession::preferredBufferSize const): 49 (WebCore::AudioSession::setPreferredBufferSize): 50 1 51 2020-10-27 Chris Dumez <cdumez@apple.com> 2 52 -
trunk/Source/WebCore/platform/audio/PlatformMediaSessionManager.cpp
r268268 r269077 140 140 session.beginInterruption(type); 141 141 }); 142 updateSessionState();142 scheduleUpdateSessionState(); 143 143 } 144 144 … … 164 164 #endif 165 165 166 updateSessionState();166 scheduleUpdateSessionState(); 167 167 } 168 168 … … 191 191 #endif 192 192 193 updateSessionState();193 scheduleUpdateSessionState(); 194 194 } 195 195 … … 282 282 void PlatformMediaSessionManager::sessionStateChanged(PlatformMediaSession&) 283 283 { 284 updateSessionState();284 scheduleUpdateSessionState(); 285 285 } 286 286 … … 562 562 ASSERT(!m_audioCaptureSources.contains(source)); 563 563 m_audioCaptureSources.add(source); 564 updateSessionState();564 scheduleUpdateSessionState(); 565 565 } 566 566 … … 570 570 ASSERT(m_audioCaptureSources.contains(source)); 571 571 m_audioCaptureSources.remove(source); 572 updateSessionState(); 572 scheduleUpdateSessionState(); 573 } 574 575 void PlatformMediaSessionManager::scheduleUpdateSessionState() 576 { 577 if (updateSessionStateQueue.hasPendingTasks()) 578 return; 579 580 updateSessionStateQueue.enqueueTask([this] { 581 updateSessionState(); 582 }); 573 583 } 574 584 -
trunk/Source/WebCore/platform/audio/PlatformMediaSessionManager.h
r268268 r269077 28 28 29 29 #include "DocumentIdentifier.h" 30 #include "GenericTaskQueue.h" 30 31 #include "MediaSessionIdentifier.h" 31 32 #include "PlatformMediaSession.h" … … 180 181 friend class Internals; 181 182 183 void scheduleUpdateSessionState(); 182 184 virtual void updateSessionState() { } 183 185 … … 198 200 199 201 WeakHashSet<PlatformMediaSession::AudioCaptureSource> m_audioCaptureSources; 202 GenericTaskQueue<Timer> updateSessionStateQueue; 200 203 201 204 #if !RELEASE_LOG_DISABLED -
trunk/Source/WebCore/platform/audio/mac/AudioSessionMac.mm
r266559 r269077 82 82 public: 83 83 explicit AudioSessionPrivate() = default; 84 85 void addSampleRateObserverIfNeeded(); 86 void addBufferSizeObserverIfNeeded(); 87 88 static OSStatus handleSampleRateChange(AudioObjectID, UInt32, const AudioObjectPropertyAddress*, void* inClientData); 89 static OSStatus handleBufferSizeChange(AudioObjectID, UInt32, const AudioObjectPropertyAddress*, void* inClientData); 90 84 91 Optional<bool> lastMutedState; 85 92 AudioSession::CategoryType category { AudioSession::None }; … … 91 98 AudioSession::CategoryType m_categoryOverride; 92 99 bool inRoutingArbitration { false }; 100 bool hasSampleRateObserver { false }; 101 bool hasBufferSizeObserver { false }; 102 Optional<double> sampleRate; 103 Optional<size_t> bufferSize; 93 104 }; 105 106 void AudioSessionPrivate::addSampleRateObserverIfNeeded() 107 { 108 if (hasSampleRateObserver) 109 return; 110 hasSampleRateObserver = true; 111 112 AudioObjectPropertyAddress nominalSampleRateAddress = { kAudioDevicePropertyNominalSampleRate, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; 113 AudioObjectAddPropertyListener(defaultDevice(), &nominalSampleRateAddress, handleSampleRateChange, this); 114 } 115 116 OSStatus AudioSessionPrivate::handleSampleRateChange(AudioObjectID device, UInt32, const AudioObjectPropertyAddress* sampleRateAddress, void* inClientData) 117 { 118 ASSERT(inClientData); 119 if (!inClientData) 120 return noErr; 121 122 auto* sessionPrivate = static_cast<AudioSessionPrivate*>(inClientData); 123 124 Float64 nominalSampleRate; 125 UInt32 nominalSampleRateSize = sizeof(Float64); 126 OSStatus result = AudioObjectGetPropertyData(device, sampleRateAddress, 0, 0, &nominalSampleRateSize, (void*)&nominalSampleRate); 127 if (result) 128 return result; 129 130 sessionPrivate->sampleRate = narrowPrecisionToFloat(nominalSampleRate); 131 return noErr; 132 } 133 134 void AudioSessionPrivate::addBufferSizeObserverIfNeeded() 135 { 136 if (hasBufferSizeObserver) 137 return; 138 139 AudioObjectPropertyAddress bufferSizeAddress = { kAudioDevicePropertyBufferFrameSize, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; 140 AudioObjectAddPropertyListener(defaultDevice(), &bufferSizeAddress, handleBufferSizeChange, this); 141 } 142 143 OSStatus AudioSessionPrivate::handleBufferSizeChange(AudioObjectID device, UInt32, const AudioObjectPropertyAddress* bufferSizeAddress, void* inClientData) 144 { 145 ASSERT(inClientData); 146 if (!inClientData) 147 return noErr; 148 149 auto* sessionPrivate = static_cast<AudioSessionPrivate*>(inClientData); 150 151 UInt32 bufferSize; 152 UInt32 bufferSizeSize = sizeof(bufferSize); 153 OSStatus result = AudioObjectGetPropertyData(device, bufferSizeAddress, 0, 0, &bufferSizeSize, &bufferSize); 154 if (result) 155 return result; 156 157 sessionPrivate->bufferSize = bufferSize; 158 return noErr; 159 } 94 160 95 161 AudioSession::AudioSession() … … 188 254 float AudioSession::sampleRate() const 189 255 { 256 if (m_private->sampleRate) 257 return *m_private->sampleRate; 258 259 m_private->addSampleRateObserverIfNeeded(); 260 190 261 Float64 nominalSampleRate; 191 262 UInt32 nominalSampleRateSize = sizeof(Float64); … … 199 270 return 0; 200 271 272 m_private->sampleRate = narrowPrecisionToFloat(nominalSampleRate); 273 201 274 return narrowPrecisionToFloat(nominalSampleRate); 202 275 } … … 204 277 size_t AudioSession::bufferSize() const 205 278 { 279 if (m_private->bufferSize) 280 return *m_private->bufferSize; 281 282 m_private->addBufferSizeObserverIfNeeded(); 283 206 284 UInt32 bufferSize; 207 285 UInt32 bufferSizeSize = sizeof(bufferSize); … … 215 293 if (result) 216 294 return 0; 295 296 m_private->bufferSize = bufferSize; 297 217 298 return bufferSize; 218 299 } … … 268 349 size_t AudioSession::preferredBufferSize() const 269 350 { 270 UInt32 bufferSize; 271 UInt32 bufferSizeSize = sizeof(bufferSize); 272 273 AudioObjectPropertyAddress preferredBufferSizeAddress = { 274 kAudioDevicePropertyBufferFrameSize, 275 kAudioObjectPropertyScopeGlobal, 276 kAudioObjectPropertyElementMaster }; 277 OSStatus result = AudioObjectGetPropertyData(defaultDevice(), &preferredBufferSizeAddress, 0, 0, &bufferSizeSize, &bufferSize); 278 279 if (result) 280 return 0; 281 return bufferSize; 351 return bufferSize(); 282 352 } 283 353 284 354 void AudioSession::setPreferredBufferSize(size_t bufferSize) 285 355 { 356 if (m_private->bufferSize == bufferSize) 357 return; 358 286 359 AudioValueRange bufferSizeRange = {0, 0}; 287 360 UInt32 bufferSizeRangeSize = sizeof(AudioValueRange); … … 306 379 result = AudioObjectSetPropertyData(defaultDevice(), &preferredBufferSizeAddress, 0, 0, sizeof(bufferSizeOut), (void*)&bufferSizeOut); 307 380 308 #if LOG_DISABLED 309 UNUSED_PARAM(result); 310 #else 381 if (!result) 382 m_private->bufferSize = bufferSizeOut; 383 384 #if !LOG_DISABLED 311 385 if (result) 312 386 LOG(Media, "AudioSession::setPreferredBufferSize(%zu) - failed with error %d", bufferSize, static_cast<int>(result));
Note: See TracChangeset
for help on using the changeset viewer.