Title: [270907] trunk/Source
Revision
270907
Author
[email protected]
Date
2020-12-16 13:07:59 -0800 (Wed, 16 Dec 2020)

Log Message

[GPUProcess] Avoid doing an IPC per rendering quantum when using WebAudio
https://bugs.webkit.org/show_bug.cgi?id=219818

Reviewed by Geoff Garen.

Source/WebCore:

* platform/audio/AudioDestination.h:
Make sure AudioDestination always gets destroyed on the main thread.

* platform/audio/cocoa/CARingBuffer.cpp:
(WebCore::CARingBuffer::lastReadFrame const):
(WebCore::CARingBufferStorageVector::setLastReadFrame):
(WebCore::CARingBufferStorageVector::lastReadFrame const):
(WebCore::CARingBuffer::fetchIfHasEnoughData):
(WebCore::CARingBuffer::fetch):
* platform/audio/cocoa/CARingBuffer.h:
Add API to CARingBuffer to query the last frame that the reader attempted to
read. This is helpful to make sure the writer keeps up with the reader and
does not stray too far ahead.

Source/WebKit:

Previously, the GPUProcess was sending an IPC to the WebProcess to request to rendering
of each quantum, which was very inefficient and a big regression compared to when we
do not use a GPUProcess.

To address the issue, we now start a rendering timer on the WebProcess side when rendering
starts, so that the WebProcess renders each quantum as needed, without needing an IPC from
the GPUProcess. Whenever a quantum is rendered, the WebProcess adds it to the RingBuffer
it shares with the GPUProcess.

To make sure that the WebProcess' writer keeps up with the GPUProcess' reader and that it
does not stray too far ahead, the writer keeps track of the last frame that the reader
attempted to read. It uses this information to try and stay a few rendering quantums ahead
of the GPUProcess, to avoid missed frames.

* CMakeLists.txt:
* DerivedSources-input.xcfilelist:
* DerivedSources-output.xcfilelist:
* DerivedSources.make:
* GPUProcess/media/RemoteAudioDestinationManager.cpp:
(WebKit::RemoteAudioDestination::audioSamplesStorageChanged):
(WebKit::RemoteAudioDestination::RemoteAudioDestination):
(WebKit::RemoteAudioDestination::render):
* Platform/SharedMemory.h:
(WebKit::SharedMemory::protection const):
* Shared/Cocoa/SharedRingBufferStorage.cpp:
(WebKit::SharedRingBufferStorage::sharedFrameBounds const):
(WebKit::SharedRingBufferStorage::setCurrentFrameBounds):
(WebKit::SharedRingBufferStorage::setLastReadFrame):
(WebKit::SharedRingBufferStorage::lastReadFrame const):
* Shared/Cocoa/SharedRingBufferStorage.h:
* SourcesCocoa.txt:
* WebKit.xcodeproj/project.pbxproj:
* WebProcess/GPU/media/RemoteAudioDestinationProxy.cpp:
(WebKit::RemoteAudioDestinationProxy::RemoteAudioDestinationProxy):
(WebKit::RemoteAudioDestinationProxy::startRenderingThread):
(WebKit::RemoteAudioDestinationProxy::stopRenderingThreadIfNecessary):
(WebKit::RemoteAudioDestinationProxy::connectToGPUProcess):
(WebKit::RemoteAudioDestinationProxy::~RemoteAudioDestinationProxy):
(WebKit::RemoteAudioDestinationProxy::start):
(WebKit::RemoteAudioDestinationProxy::startRendering):
(WebKit::RemoteAudioDestinationProxy::stop):
(WebKit::RemoteAudioDestinationProxy::stopRenderingIfNecessary):
(WebKit::RemoteAudioDestinationProxy::renderQuantum):
(WebKit::RemoteAudioDestinationProxy::storageChanged):
* WebProcess/GPU/media/RemoteAudioDestinationProxy.h:
(WebKit::RemoteAudioDestinationProxy::stopRenderingIfNecessary):
* WebProcess/GPU/media/RemoteAudioDestinationProxy.messages.in: Removed.

Modified Paths

Removed Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (270906 => 270907)


--- trunk/Source/WebCore/ChangeLog	2020-12-16 20:57:12 UTC (rev 270906)
+++ trunk/Source/WebCore/ChangeLog	2020-12-16 21:07:59 UTC (rev 270907)
@@ -1,3 +1,24 @@
+2020-12-16  Chris Dumez  <[email protected]>
+
+        [GPUProcess] Avoid doing an IPC per rendering quantum when using WebAudio
+        https://bugs.webkit.org/show_bug.cgi?id=219818
+
+        Reviewed by Geoff Garen.
+
+        * platform/audio/AudioDestination.h:
+        Make sure AudioDestination always gets destroyed on the main thread.
+
+        * platform/audio/cocoa/CARingBuffer.cpp:
+        (WebCore::CARingBuffer::lastReadFrame const):
+        (WebCore::CARingBufferStorageVector::setLastReadFrame):
+        (WebCore::CARingBufferStorageVector::lastReadFrame const):
+        (WebCore::CARingBuffer::fetchIfHasEnoughData):
+        (WebCore::CARingBuffer::fetch):
+        * platform/audio/cocoa/CARingBuffer.h:
+        Add API to CARingBuffer to query the last frame that the reader attempted to
+        read. This is helpful to make sure the writer keeps up with the reader and
+        does not stray too far ahead.
+
 2020-12-16  Víctor Manuel Jáquez Leal  <[email protected]>
 
         [Debug][GStreamer] Crash in fast/mediastream/change-tracks-media-stream-being-played.html

Modified: trunk/Source/WebCore/platform/audio/AudioDestination.h (270906 => 270907)


--- trunk/Source/WebCore/platform/audio/AudioDestination.h	2020-12-16 20:57:12 UTC (rev 270906)
+++ trunk/Source/WebCore/platform/audio/AudioDestination.h	2020-12-16 21:07:59 UTC (rev 270907)
@@ -43,7 +43,7 @@
 // The audio hardware periodically calls the AudioIOCallback render() method asking it to render/output the next render quantum of audio.
 // It optionally will pass in local/live audio input when it calls render().
 
-class AudioDestination : public ThreadSafeRefCounted<AudioDestination> {
+class AudioDestination : public ThreadSafeRefCounted<AudioDestination, WTF::DestructionThread::Main> {
     WTF_MAKE_FAST_ALLOCATED;
 public:
     // Pass in (numberOfInputChannels > 0) if live/local audio input is desired.

Modified: trunk/Source/WebCore/platform/audio/cocoa/CARingBuffer.cpp (270906 => 270907)


--- trunk/Source/WebCore/platform/audio/cocoa/CARingBuffer.cpp	2020-12-16 20:57:12 UTC (rev 270906)
+++ trunk/Source/WebCore/platform/audio/cocoa/CARingBuffer.cpp	2020-12-16 21:07:59 UTC (rev 270907)
@@ -181,6 +181,7 @@
         timeBounds.m_updateCounter = 0;
     }
     m_timeBoundsQueuePtr = 0;
+    m_lastReadFrame = 0;
 }
 
 CARingBuffer::Error CARingBuffer::store(const AudioBufferList* list, size_t framesToWrite, uint64_t startFrame)
@@ -269,6 +270,11 @@
     m_buffers->getCurrentFrameBounds(startFrame, endFrame);
 }
 
+uint64_t CARingBuffer::lastReadFrame() const
+{
+    return m_buffers->lastReadFrame();
+}
+
 void CARingBufferStorageVector::getCurrentFrameBounds(uint64_t& startFrame, uint64_t& endFrame)
 {
     uint32_t curPtr = m_timeBoundsQueuePtr.load();
@@ -279,6 +285,16 @@
     endFrame = bounds.m_endFrame;
 }
 
+void CARingBufferStorageVector::setLastReadFrame(uint64_t lastReadFrame)
+{
+    m_lastReadFrame.store(lastReadFrame, std::memory_order_release);
+}
+
+uint64_t CARingBufferStorageVector::lastReadFrame() const
+{
+    return m_lastReadFrame.load(std::memory_order_acquire);
+}
+
 void CARingBuffer::clipTimeBounds(uint64_t& startRead, uint64_t& endRead)
 {
     uint64_t startTime;
@@ -325,6 +341,8 @@
 
 bool CARingBuffer::fetchIfHasEnoughData(AudioBufferList* list, size_t frameCount, uint64_t startFrame, FetchMode mode)
 {
+    m_buffers->setLastReadFrame(startFrame + frameCount);
+
     // When the RingBuffer is backed by shared memory, getCurrentFrameBounds() makes sure we pull frame bounds from shared memory before fetching.
     uint64_t start, end;
     getCurrentFrameBounds(start, end);
@@ -337,6 +355,8 @@
 
 void CARingBuffer::fetch(AudioBufferList* list, size_t frameCount, uint64_t startRead, FetchMode mode)
 {
+    m_buffers->setLastReadFrame(startRead + frameCount);
+
     // When the RingBuffer is backed by shared memory, make sure we pull frame bounds from shared memory before fetching.
     updateFrameBounds();
     fetchInternal(list, frameCount, startRead, mode);

Modified: trunk/Source/WebCore/platform/audio/cocoa/CARingBuffer.h (270906 => 270907)


--- trunk/Source/WebCore/platform/audio/cocoa/CARingBuffer.h	2020-12-16 20:57:12 UTC (rev 270906)
+++ trunk/Source/WebCore/platform/audio/cocoa/CARingBuffer.h	2020-12-16 21:07:59 UTC (rev 270907)
@@ -51,6 +51,8 @@
     virtual uint64_t currentStartFrame() const = 0;
     virtual uint64_t currentEndFrame() const = 0;
     virtual void flush() = 0;
+    virtual void setLastReadFrame(uint64_t) = 0;
+    virtual uint64_t lastReadFrame() const = 0;
 };
 
 class CARingBufferStorageVector final : public CARingBufferStorage {
@@ -64,9 +66,11 @@
     void* data() final { return m_buffer.data(); }
     void getCurrentFrameBounds(uint64_t& startTime, uint64_t& endTime) final;
     void setCurrentFrameBounds(uint64_t startFrame, uint64_t endFrame) final;
-    uint64_t currentStartFrame() const;
-    uint64_t currentEndFrame() const;
+    uint64_t currentStartFrame() const final;
+    uint64_t currentEndFrame() const final;
     void flush() final;
+    void setLastReadFrame(uint64_t) final;
+    uint64_t lastReadFrame() const final;
 
     struct TimeBounds {
         TimeBounds()
@@ -84,6 +88,7 @@
     Vector<TimeBounds> m_timeBoundsQueue;
     Lock m_currentFrameBoundsLock;
     std::atomic<int32_t> m_timeBoundsQueuePtr { 0 };
+    std::atomic<uint64_t> m_lastReadFrame { 0 };
 };
 
 class CARingBuffer {
@@ -113,6 +118,9 @@
 
     WEBCORE_EXPORT void getCurrentFrameBounds(uint64_t& startFrame, uint64_t& endFrame);
 
+    // Note that this will only return useful values if the reader has read-write access to the storage.
+    WEBCORE_EXPORT uint64_t lastReadFrame() const;
+
     uint32_t channelCount() const { return m_channelCount; }
     CARingBufferStorage& storage() { return m_buffers; }
 

Modified: trunk/Source/WebKit/CMakeLists.txt (270906 => 270907)


--- trunk/Source/WebKit/CMakeLists.txt	2020-12-16 20:57:12 UTC (rev 270906)
+++ trunk/Source/WebKit/CMakeLists.txt	2020-12-16 21:07:59 UTC (rev 270907)
@@ -226,7 +226,6 @@
 
     WebProcess/GPU/media/MediaPlayerPrivateRemote
     WebProcess/GPU/media/MediaSourcePrivateRemote
-    WebProcess/GPU/media/RemoteAudioDestinationProxy
     WebProcess/GPU/media/RemoteAudioSession
     WebProcess/GPU/media/RemoteAudioSourceProviderManager
     WebProcess/GPU/media/RemoteCDMInstanceSession

Modified: trunk/Source/WebKit/ChangeLog (270906 => 270907)


--- trunk/Source/WebKit/ChangeLog	2020-12-16 20:57:12 UTC (rev 270906)
+++ trunk/Source/WebKit/ChangeLog	2020-12-16 21:07:59 UTC (rev 270907)
@@ -1,3 +1,58 @@
+2020-12-16  Chris Dumez  <[email protected]>
+
+        [GPUProcess] Avoid doing an IPC per rendering quantum when using WebAudio
+        https://bugs.webkit.org/show_bug.cgi?id=219818
+
+        Reviewed by Geoff Garen.
+
+        Previously, the GPUProcess was sending an IPC to the WebProcess to request to rendering
+        of each quantum, which was very inefficient and a big regression compared to when we
+        do not use a GPUProcess.
+
+        To address the issue, we now start a rendering timer on the WebProcess side when rendering
+        starts, so that the WebProcess renders each quantum as needed, without needing an IPC from
+        the GPUProcess. Whenever a quantum is rendered, the WebProcess adds it to the RingBuffer
+        it shares with the GPUProcess.
+
+        To make sure that the WebProcess' writer keeps up with the GPUProcess' reader and that it
+        does not stray too far ahead, the writer keeps track of the last frame that the reader
+        attempted to read. It uses this information to try and stay a few rendering quantums ahead
+        of the GPUProcess, to avoid missed frames.
+
+        * CMakeLists.txt:
+        * DerivedSources-input.xcfilelist:
+        * DerivedSources-output.xcfilelist:
+        * DerivedSources.make:
+        * GPUProcess/media/RemoteAudioDestinationManager.cpp:
+        (WebKit::RemoteAudioDestination::audioSamplesStorageChanged):
+        (WebKit::RemoteAudioDestination::RemoteAudioDestination):
+        (WebKit::RemoteAudioDestination::render):
+        * Platform/SharedMemory.h:
+        (WebKit::SharedMemory::protection const):
+        * Shared/Cocoa/SharedRingBufferStorage.cpp:
+        (WebKit::SharedRingBufferStorage::sharedFrameBounds const):
+        (WebKit::SharedRingBufferStorage::setCurrentFrameBounds):
+        (WebKit::SharedRingBufferStorage::setLastReadFrame):
+        (WebKit::SharedRingBufferStorage::lastReadFrame const):
+        * Shared/Cocoa/SharedRingBufferStorage.h:
+        * SourcesCocoa.txt:
+        * WebKit.xcodeproj/project.pbxproj:
+        * WebProcess/GPU/media/RemoteAudioDestinationProxy.cpp:
+        (WebKit::RemoteAudioDestinationProxy::RemoteAudioDestinationProxy):
+        (WebKit::RemoteAudioDestinationProxy::startRenderingThread):
+        (WebKit::RemoteAudioDestinationProxy::stopRenderingThreadIfNecessary):
+        (WebKit::RemoteAudioDestinationProxy::connectToGPUProcess):
+        (WebKit::RemoteAudioDestinationProxy::~RemoteAudioDestinationProxy):
+        (WebKit::RemoteAudioDestinationProxy::start):
+        (WebKit::RemoteAudioDestinationProxy::startRendering):
+        (WebKit::RemoteAudioDestinationProxy::stop):
+        (WebKit::RemoteAudioDestinationProxy::stopRenderingIfNecessary):
+        (WebKit::RemoteAudioDestinationProxy::renderQuantum):
+        (WebKit::RemoteAudioDestinationProxy::storageChanged):
+        * WebProcess/GPU/media/RemoteAudioDestinationProxy.h:
+        (WebKit::RemoteAudioDestinationProxy::stopRenderingIfNecessary):
+        * WebProcess/GPU/media/RemoteAudioDestinationProxy.messages.in: Removed.
+
 2020-12-16  Youenn Fablet  <[email protected]>
 
         REGRESSION [iOS] Flaky GPU Process crash under -[RTCVideoEncoderH264 destroyCompressionSession]

Modified: trunk/Source/WebKit/DerivedSources-input.xcfilelist (270906 => 270907)


--- trunk/Source/WebKit/DerivedSources-input.xcfilelist	2020-12-16 20:57:12 UTC (rev 270906)
+++ trunk/Source/WebKit/DerivedSources-input.xcfilelist	2020-12-16 21:07:59 UTC (rev 270907)
@@ -141,7 +141,6 @@
 $(PROJECT_DIR)/WebProcess/GPU/graphics/RemoteRenderingBackendProxy.messages.in
 $(PROJECT_DIR)/WebProcess/GPU/media/MediaPlayerPrivateRemote.messages.in
 $(PROJECT_DIR)/WebProcess/GPU/media/MediaSourcePrivateRemote.messages.in
-$(PROJECT_DIR)/WebProcess/GPU/media/RemoteAudioDestinationProxy.messages.in
 $(PROJECT_DIR)/WebProcess/GPU/media/RemoteAudioSession.messages.in
 $(PROJECT_DIR)/WebProcess/GPU/media/RemoteAudioSourceProviderManager.messages.in
 $(PROJECT_DIR)/WebProcess/GPU/media/RemoteCDMInstanceSession.messages.in

Modified: trunk/Source/WebKit/DerivedSources-output.xcfilelist (270906 => 270907)


--- trunk/Source/WebKit/DerivedSources-output.xcfilelist	2020-12-16 20:57:12 UTC (rev 270906)
+++ trunk/Source/WebKit/DerivedSources-output.xcfilelist	2020-12-16 21:07:59 UTC (rev 270907)
@@ -138,10 +138,6 @@
 $(BUILT_PRODUCTS_DIR)/DerivedSources/WebKit2/RemoteAudioDestinationManagerMessages.cpp
 $(BUILT_PRODUCTS_DIR)/DerivedSources/WebKit2/RemoteAudioDestinationManagerMessages.h
 $(BUILT_PRODUCTS_DIR)/DerivedSources/WebKit2/RemoteAudioDestinationManagerMessagesReplies.h
-$(BUILT_PRODUCTS_DIR)/DerivedSources/WebKit2/RemoteAudioDestinationProxyMessageReceiver.cpp
-$(BUILT_PRODUCTS_DIR)/DerivedSources/WebKit2/RemoteAudioDestinationProxyMessages.cpp
-$(BUILT_PRODUCTS_DIR)/DerivedSources/WebKit2/RemoteAudioDestinationProxyMessages.h
-$(BUILT_PRODUCTS_DIR)/DerivedSources/WebKit2/RemoteAudioDestinationProxyMessagesReplies.h
 $(BUILT_PRODUCTS_DIR)/DerivedSources/WebKit2/RemoteAudioMediaStreamTrackRendererManagerMessageReceiver.cpp
 $(BUILT_PRODUCTS_DIR)/DerivedSources/WebKit2/RemoteAudioMediaStreamTrackRendererManagerMessages.h
 $(BUILT_PRODUCTS_DIR)/DerivedSources/WebKit2/RemoteAudioMediaStreamTrackRendererManagerMessagesReplies.h

Modified: trunk/Source/WebKit/DerivedSources.make (270906 => 270907)


--- trunk/Source/WebKit/DerivedSources.make	2020-12-16 20:57:12 UTC (rev 270906)
+++ trunk/Source/WebKit/DerivedSources.make	2020-12-16 21:07:59 UTC (rev 270907)
@@ -178,7 +178,6 @@
 	WebProcess/GPU/webrtc/SampleBufferDisplayLayer \
 	WebProcess/GPU/media/MediaPlayerPrivateRemote \
 	WebProcess/GPU/media/MediaSourcePrivateRemote \
-	WebProcess/GPU/media/RemoteAudioDestinationProxy \
 	WebProcess/GPU/media/RemoteAudioSession \
 	WebProcess/GPU/media/RemoteAudioSourceProviderManager \
 	WebProcess/GPU/media/RemoteCDMInstanceSession \

Modified: trunk/Source/WebKit/GPUProcess/media/RemoteAudioDestinationManager.cpp (270906 => 270907)


--- trunk/Source/WebKit/GPUProcess/media/RemoteAudioDestinationManager.cpp	2020-12-16 20:57:12 UTC (rev 270906)
+++ trunk/Source/WebKit/GPUProcess/media/RemoteAudioDestinationManager.cpp	2020-12-16 21:07:59 UTC (rev 270907)
@@ -29,7 +29,6 @@
 #if ENABLE(GPU_PROCESS) && ENABLE(WEB_AUDIO)
 
 #include "GPUConnectionToWebProcess.h"
-#include "RemoteAudioDestinationProxyMessages.h"
 #include <WebCore/AudioUtilities.h>
 #include <wtf/ThreadSafeRefCounted.h>
 #include <wtf/threads/BinarySemaphore.h>
@@ -79,7 +78,7 @@
             return;
         }
 
-        auto memory = SharedMemory::map(ipcHandle.handle, SharedMemory::Protection::ReadOnly);
+        auto memory = SharedMemory::map(ipcHandle.handle, SharedMemory::Protection::ReadWrite);
         storage().setStorage(WTFMove(memory));
         storage().setReadOnly(true);
         m_ringBuffer->allocate(description, numberOfFrames);
@@ -114,9 +113,8 @@
     bool isPlaying() const { return m_isPlaying; }
 
 private:
-    RemoteAudioDestination(GPUConnectionToWebProcess& connection, RemoteAudioDestinationIdentifier identifier, const String& inputDeviceId, uint32_t numberOfInputChannels, uint32_t numberOfOutputChannels, float sampleRate, float hardwareSampleRate)
-        : m_connection(connection)
-        , m_id(identifier)
+    RemoteAudioDestination(GPUConnectionToWebProcess&, RemoteAudioDestinationIdentifier identifier, const String& inputDeviceId, uint32_t numberOfInputChannels, uint32_t numberOfOutputChannels, float sampleRate, float hardwareSampleRate)
+        : m_id(identifier)
 #if PLATFORM(COCOA)
         , m_audioOutputUnitAdaptor(*this)
         , m_ringBuffer(makeUniqueRef<WebCore::CARingBuffer>(makeUniqueRef<SharedRingBufferStorage>()))
@@ -147,13 +145,10 @@
             status = noErr;
         }
 
-        m_connection.connection().send(Messages::RemoteAudioDestinationProxy::RequestBuffer(sampleTime, hostTime, numberOfFrames), m_id.toUInt64());
-
         return status;
     }
 #endif
 
-    GPUConnectionToWebProcess& m_connection;
     RemoteAudioDestinationIdentifier m_id;
 
     RefPtr<RemoteAudioDestination> m_protectThisDuringGracefulShutdown;

Modified: trunk/Source/WebKit/Platform/SharedMemory.h (270906 => 270907)


--- trunk/Source/WebKit/Platform/SharedMemory.h	2020-12-16 20:57:12 UTC (rev 270906)
+++ trunk/Source/WebKit/Platform/SharedMemory.h	2020-12-16 21:07:59 UTC (rev 270907)
@@ -142,6 +142,10 @@
     HANDLE handle() const { return m_handle; }
 #endif
 
+#if PLATFORM(COCOA)
+    Protection protection() const { return m_protection; }
+#endif
+
     // Return the system page size in bytes.
     static unsigned systemPageSize();
 

Modified: trunk/Source/WebKit/Shared/Cocoa/SharedRingBufferStorage.cpp (270906 => 270907)


--- trunk/Source/WebKit/Shared/Cocoa/SharedRingBufferStorage.cpp	2020-12-16 20:57:12 UTC (rev 270906)
+++ trunk/Source/WebKit/Shared/Cocoa/SharedRingBufferStorage.cpp	2020-12-16 21:07:59 UTC (rev 270907)
@@ -59,7 +59,7 @@
         setStorage(nullptr);
 }
 
-auto SharedRingBufferStorage::sharedFrameBounds() -> FrameBounds*
+auto SharedRingBufferStorage::sharedFrameBounds() const -> FrameBounds*
 {
     return m_storage ? reinterpret_cast<FrameBounds*>(m_storage->data()) : nullptr;
 }
@@ -106,6 +106,20 @@
     m_endFrame = pair.second;
 }
 
+void SharedRingBufferStorage::setLastReadFrame(uint64_t lastReadFrame)
+{
+    if (!m_storage || m_storage->protection() != SharedMemory::Protection::ReadWrite)
+        return;
+
+    sharedFrameBounds()->lastReadFrame.store(lastReadFrame, std::memory_order_release);
+}
+
+uint64_t SharedRingBufferStorage::lastReadFrame() const
+{
+    auto* sharedBounds = sharedFrameBounds();
+    return sharedBounds ? sharedBounds->lastReadFrame.load(std::memory_order_acquire) : 0;
+}
+
 void SharedRingBufferStorage::flush()
 {
     m_startFrame = m_endFrame = 0;

Modified: trunk/Source/WebKit/Shared/Cocoa/SharedRingBufferStorage.h (270906 => 270907)


--- trunk/Source/WebKit/Shared/Cocoa/SharedRingBufferStorage.h	2020-12-16 20:57:12 UTC (rev 270906)
+++ trunk/Source/WebKit/Shared/Cocoa/SharedRingBufferStorage.h	2020-12-16 21:07:59 UTC (rev 270907)
@@ -59,6 +59,8 @@
     uint64_t currentEndFrame() const final { return m_endFrame; }
     void updateFrameBounds() final;
     void flush() final;
+    void setLastReadFrame(uint64_t) final;
+    uint64_t lastReadFrame() const final;
 
 private:
     static constexpr unsigned boundsBufferSize { 32 };
@@ -65,9 +67,10 @@
     struct FrameBounds {
         std::pair<uint64_t, uint64_t> boundsBuffer[boundsBufferSize];
         Atomic<unsigned> boundsBufferIndex { 0 };
+        Atomic<uint64_t> lastReadFrame { 0 };
     };
 
-    FrameBounds* sharedFrameBounds();
+    FrameBounds* sharedFrameBounds() const;
 
     Function<void(SharedMemory*)> m_storageChangedHandler;
     RefPtr<SharedMemory> m_storage;

Modified: trunk/Source/WebKit/SourcesCocoa.txt (270906 => 270907)


--- trunk/Source/WebKit/SourcesCocoa.txt	2020-12-16 20:57:12 UTC (rev 270906)
+++ trunk/Source/WebKit/SourcesCocoa.txt	2020-12-16 21:07:59 UTC (rev 270907)
@@ -690,7 +690,6 @@
 MessageArgumentDescriptions.cpp @no-unify
 MessageNames.cpp
 RemoteAudioDestinationManagerMessageReceiver.cpp
-RemoteAudioDestinationProxyMessageReceiver.cpp
 RemoteAudioSessionMessageReceiver.cpp
 RemoteAudioSessionProxyMessageReceiver.cpp
 RemoteAudioMediaStreamTrackRendererManagerMessageReceiver.cpp

Modified: trunk/Source/WebKit/WebKit.xcodeproj/project.pbxproj (270906 => 270907)


--- trunk/Source/WebKit/WebKit.xcodeproj/project.pbxproj	2020-12-16 20:57:12 UTC (rev 270906)
+++ trunk/Source/WebKit/WebKit.xcodeproj/project.pbxproj	2020-12-16 21:07:59 UTC (rev 270907)
@@ -4879,7 +4879,6 @@
 		9B5BEC252400F4A90070C6EF /* WebMediaStrategy.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = WebMediaStrategy.cpp; sourceTree = "<group>"; };
 		9B5BEC28240101580070C6EF /* RemoteAudioDestinationProxy.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RemoteAudioDestinationProxy.h; sourceTree = "<group>"; };
 		9B5BEC29240101580070C6EF /* RemoteAudioDestinationProxy.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = RemoteAudioDestinationProxy.cpp; sourceTree = "<group>"; };
-		9B5BEC2E2401018C0070C6EF /* RemoteAudioDestinationProxy.messages.in */ = {isa = PBXFileReference; lastKnownFileType = text; path = RemoteAudioDestinationProxy.messages.in; sourceTree = "<group>"; };
 		9BC59D6C1EFCCCB6001E8D09 /* CallbackID.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CallbackID.h; sourceTree = "<group>"; };
 		9BC59D6D1EFCDC6D001E8D09 /* OptionalCallbackID.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OptionalCallbackID.h; sourceTree = "<group>"; };
 		9BD8AB4925820E52005386F8 /* FontReference.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FontReference.h; sourceTree = "<group>"; };
@@ -5939,7 +5938,6 @@
 				1DF29018257F202F003C28AF /* MediaSourcePrivateRemote.messages.in */,
 				9B5BEC29240101580070C6EF /* RemoteAudioDestinationProxy.cpp */,
 				9B5BEC28240101580070C6EF /* RemoteAudioDestinationProxy.h */,
-				9B5BEC2E2401018C0070C6EF /* RemoteAudioDestinationProxy.messages.in */,
 				CDBB49F5240D8AC60017C292 /* RemoteAudioSession.cpp */,
 				CDBB49F4240D8AC60017C292 /* RemoteAudioSession.h */,
 				CDD5356F240DD34300F7B8C4 /* RemoteAudioSession.messages.in */,

Modified: trunk/Source/WebKit/WebProcess/GPU/media/RemoteAudioDestinationProxy.cpp (270906 => 270907)


--- trunk/Source/WebKit/WebProcess/GPU/media/RemoteAudioDestinationProxy.cpp	2020-12-16 20:57:12 UTC (rev 270906)
+++ trunk/Source/WebKit/WebProcess/GPU/media/RemoteAudioDestinationProxy.cpp	2020-12-16 21:07:59 UTC (rev 270907)
@@ -31,13 +31,16 @@
 #include "GPUConnectionToWebProcess.h"
 #include "Logging.h"
 #include "RemoteAudioDestinationManagerMessages.h"
-#include "RemoteAudioDestinationProxyMessages.h"
 #include "WebProcess.h"
 #include <WebCore/AudioBus.h>
+#include <WebCore/AudioUtilities.h>
+#include <wtf/RunLoop.h>
+#include <wtf/threads/BinarySemaphore.h>
 
 #if PLATFORM(COCOA)
 #include <WebCore/CARingBuffer.h>
 #include <WebCore/WebAudioBufferList.h>
+#include <mach/mach_time.h>
 #endif
 
 namespace WebKit {
@@ -44,6 +47,7 @@
 
 // Allocate a ring buffer large enough to contain 2 seconds of audio.
 constexpr size_t ringBufferSizeInSecond = 2;
+constexpr uint64_t framesAheadOfReader = 2 * WebCore::AudioUtilities::renderQuantumSize;
 
 using AudioDestination = WebCore::AudioDestination;
 using AudioIOCallback = WebCore::AudioIOCallback;
@@ -66,15 +70,35 @@
     , m_inputDeviceId(inputDeviceId)
     , m_numberOfInputChannels(numberOfInputChannels)
 {
-    auto offThreadRendering = [this, protectedThis = makeRef(*this)]() mutable {
-        while (!m_threadTaskQueue.isKilled()) {
-            if (auto task = m_threadTaskQueue.waitForMessage())
-                task();
-        }
+    connectToGPUProcess();
+}
+
+void RemoteAudioDestinationProxy::startRenderingThread()
+{
+    ASSERT(!m_renderThread);
+
+    BinarySemaphore semaphore;
+    auto offThreadRendering = [this, protectedThis = makeRef(*this), &semaphore]() mutable {
+        m_dispatchToRenderThread = [runLoop = makeRef(RunLoop::current())](Function<void()>&& task) {
+            runLoop->dispatch(WTFMove(task));
+        };
+        semaphore.signal();
+        RunLoop::run();
     };
     m_renderThread = Thread::create("RemoteAudioDestinationProxy render thread", WTFMove(offThreadRendering), ThreadType::Audio, Thread::QOS::UserInteractive);
+    m_renderThread->detach();
+    semaphore.wait();
+}
 
-    connectToGPUProcess();
+void RemoteAudioDestinationProxy::stopRenderingThreadIfNecessary()
+{
+    if (!m_renderThread)
+        return;
+
+    m_dispatchToRenderThread([] {
+        RunLoop::current().stop();
+    });
+    m_renderThread = nullptr;
 }
 
 void RemoteAudioDestinationProxy::connectToGPUProcess()
@@ -93,7 +117,6 @@
         return;
     }
 
-    connection.connection().addThreadMessageReceiver(Messages::RemoteAudioDestinationProxy::messageReceiverName(), this, destinationID.toUInt64());
     m_destinationID = destinationID;
 
 #if PLATFORM(COCOA)
@@ -108,7 +131,6 @@
 RemoteAudioDestinationProxy::~RemoteAudioDestinationProxy()
 {
     auto& connection =  WebProcess::singleton().ensureGPUProcessConnection();
-    connection.connection().removeThreadMessageReceiver(Messages::RemoteAudioDestinationProxy::messageReceiverName(), m_destinationID.toUInt64());
 
     connection.connection().sendWithAsyncReply(
         Messages::RemoteAudioDestinationManager::DeleteAudioDestination(m_destinationID), [] {
@@ -115,66 +137,83 @@
         // Can't remove this from proxyMap() here because the object would have been already deleted.
     });
 
-    m_threadTaskQueue.kill();
-    m_renderThread->waitForCompletion();
+    RELEASE_ASSERT(!m_renderThread);
+    RELEASE_ASSERT(!m_dispatchToRenderThread);
 }
 
 void RemoteAudioDestinationProxy::start(Function<void(Function<void()>&&)>&& dispatchToRenderThread, CompletionHandler<void(bool)>&& completionHandler)
 {
     WebProcess::singleton().ensureGPUProcessConnection().connection().sendWithAsyncReply(Messages::RemoteAudioDestinationManager::StartAudioDestination(m_destinationID), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler), dispatchToRenderThread = WTFMove(dispatchToRenderThread)](bool isPlaying) mutable {
+        if (m_dispatchToRenderThread)
+            return completionHandler(true); // Already rendering. Can happen when start() gets called multiple times without stopping in between.
+
         m_dispatchToRenderThread = WTFMove(dispatchToRenderThread);
         setIsPlaying(isPlaying);
-        completionHandler(isPlaying);
+        if (!isPlaying)
+            return completionHandler(false);
+        startRendering([completionHandler = WTFMove(completionHandler)]() mutable {
+            completionHandler(true);
+        });
     });
 }
 
+void RemoteAudioDestinationProxy::startRendering(CompletionHandler<void()>&& completionHandler)
+{
+    ASSERT(isMainThread());
+
+    if (!m_dispatchToRenderThread)
+        startRenderingThread();
+
+    m_dispatchToRenderThread([this, protectedThis = makeRef(*this), sampleRate = hardwareSampleRate(), completionHandler = WTFMove(completionHandler)]() mutable {
+        m_sampleRate = sampleRate;
+        renderQuantum();
+        // To avoid the need for an IPC message from the GPUProcess for each rendering quantum, we use a timer to trigger the rendering of each quantum.
+        m_renderingTimer = makeUnique<RunLoop::Timer<RemoteAudioDestinationProxy>>(RunLoop::current(), this, &RemoteAudioDestinationProxy::renderQuantum);
+        m_renderingTimer->startRepeating(Seconds { WebCore::AudioUtilities::renderQuantumSize / static_cast<double>(sampleRate) });
+
+        RunLoop::main().dispatch(WTFMove(completionHandler));
+    });
+}
+
 void RemoteAudioDestinationProxy::stop(CompletionHandler<void(bool)>&& completionHandler)
 {
     WebProcess::singleton().ensureGPUProcessConnection().connection().sendWithAsyncReply(Messages::RemoteAudioDestinationManager::StopAudioDestination(m_destinationID), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](bool isPlaying) mutable {
         setIsPlaying(isPlaying);
-        auto dispatchToRenderThread = std::exchange(m_dispatchToRenderThread, nullptr);
-        auto callCompletionHandler = [completionHandler = WTFMove(completionHandler), isPlaying]() mutable {
+        stopRenderingIfNecessary([completionHandler = WTFMove(completionHandler), isPlaying]() mutable {
             completionHandler(!isPlaying);
-        };
+        });
+    });
+}
 
-        if (dispatchToRenderThread) {
-            // Do a round-trip to the worklet thread to make sure we call the completion handler after
-            // the last rendering quantum has been processed by the worklet thread.
-            dispatchToRenderThread([callCompletionHandler = WTFMove(callCompletionHandler)]() mutable {
-                callOnMainThread(WTFMove(callCompletionHandler));
-            });
-        } else
-            callCompletionHandler();
+void RemoteAudioDestinationProxy::stopRenderingIfNecessary(CompletionHandler<void()>&& completionHandler)
+{
+    ASSERT(isMainThread());
+    if (!m_dispatchToRenderThread)
+        return completionHandler();
+
+    m_dispatchToRenderThread([this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)]() mutable {
+        m_renderingTimer = nullptr;
+        RunLoop::main().dispatch(WTFMove(completionHandler));
     });
+    stopRenderingThreadIfNecessary();
+    m_dispatchToRenderThread = nullptr;
 }
 
-#if PLATFORM(COCOA)
-void RemoteAudioDestinationProxy::requestBuffer(double sampleTime, uint64_t hostTime, uint64_t framesToRender)
+void RemoteAudioDestinationProxy::renderQuantum()
 {
     ASSERT(!isMainThread());
 
-    if (!hasEnoughFrames(framesToRender))
-        return;
+#if PLATFORM(COCOA)
+    m_audioBufferList->setSampleCount(WebCore::AudioUtilities::renderQuantumSize);
 
-    auto startFrame = m_currentFrame;
-    m_audioBufferList->setSampleCount(framesToRender);
-
-    AudioDestinationCocoa::render(sampleTime, hostTime, framesToRender, m_audioBufferList->list());
-
-    m_currentFrame += framesToRender;
-
-    m_ringBuffer->store(m_audioBufferList->list(), framesToRender, startFrame);
-}
+    // Try to stay a few rendering quantums ahead of the reader.
+    auto lastReadFrame = m_ringBuffer->lastReadFrame();
+    while (lastReadFrame >= m_currentFrame || (m_currentFrame - lastReadFrame) < framesAheadOfReader) {
+        AudioDestinationCocoa::render(m_currentFrame / static_cast<double>(m_sampleRate), mach_absolute_time(), WebCore::AudioUtilities::renderQuantumSize, m_audioBufferList->list());
+        m_ringBuffer->store(m_audioBufferList->list(), WebCore::AudioUtilities::renderQuantumSize, m_currentFrame);
+        m_currentFrame += WebCore::AudioUtilities::renderQuantumSize;
+    }
 #endif
-
-// IPC::Connection::ThreadMessageReceiver
-void RemoteAudioDestinationProxy::dispatchToThread(Function<void()>&& task)
-{
-    if (m_dispatchToRenderThread) {
-        m_dispatchToRenderThread(WTFMove(task));
-        return;
-    }
-    m_threadTaskQueue.append(WTFMove(task));
 }
 
 #if PLATFORM(COCOA)
@@ -182,7 +221,7 @@
 {
     SharedMemory::Handle handle;
     if (storage)
-        storage->createHandle(handle, SharedMemory::Protection::ReadOnly);
+        storage->createHandle(handle, SharedMemory::Protection::ReadWrite);
 
     // FIXME: Send the actual data size with IPCHandle.
 #if OS(DARWIN) || OS(WINDOWS)

Modified: trunk/Source/WebKit/WebProcess/GPU/media/RemoteAudioDestinationProxy.h (270906 => 270907)


--- trunk/Source/WebKit/WebProcess/GPU/media/RemoteAudioDestinationProxy.h	2020-12-16 20:57:12 UTC (rev 270906)
+++ trunk/Source/WebKit/WebProcess/GPU/media/RemoteAudioDestinationProxy.h	2020-12-16 21:07:59 UTC (rev 270907)
@@ -57,8 +57,7 @@
 #else
     : public WebCore::AudioDestination
 #endif
-    , public GPUProcessConnection::Client
-    , public IPC::Connection::ThreadMessageReceiver {
+    , public GPUProcessConnection::Client {
     WTF_MAKE_NONCOPYABLE(RemoteAudioDestinationProxy);
 public:
     using AudioIOCallback = WebCore::AudioIOCallback;
@@ -70,16 +69,16 @@
     RemoteAudioDestinationProxy(AudioIOCallback&, const String& inputDeviceId, unsigned numberOfInputChannels, unsigned numberOfOutputChannels, float sampleRate);
     ~RemoteAudioDestinationProxy();
 
-    void didReceiveMessageFromGPUProcess(IPC::Connection& connection, IPC::Decoder& decoder) { didReceiveMessage(connection, decoder); }
-
-#if PLATFORM(COCOA)
-    void requestBuffer(double sampleTime, uint64_t hostTime, uint64_t numberOfFrames);
-#endif
-
 private:
     void start(Function<void(Function<void()>&&)>&& dispatchToRenderThread, CompletionHandler<void(bool)>&&) final;
     void stop(CompletionHandler<void(bool)>&&) final;
 
+    void startRenderingThread();
+    void stopRenderingThreadIfNecessary();
+    void startRendering(CompletionHandler<void()>&&);
+    void stopRenderingIfNecessary(CompletionHandler<void()>&& = [] { });
+    void renderQuantum();
+
     void connectToGPUProcess();
 
     // GPUProcessConnection::Client.
@@ -97,14 +96,6 @@
     void storageChanged(SharedMemory*);
 #endif
 
-    // IPC::MessageReceiver
-    void didReceiveMessage(IPC::Connection&, IPC::Decoder&) final;
-
-    // IPC::Connection::ThreadMessageReceiver
-    void dispatchToThread(Function<void()>&&) final;
-    void refMessageReceiver() final { WebCore::AudioDestination::ref(); }
-    void derefMessageReceiver() final { WebCore::AudioDestination::deref(); }
-
     RemoteAudioDestinationIdentifier m_destinationID;
 
 #if PLATFORM(COCOA)
@@ -121,7 +112,9 @@
 
     Function<void(Function<void()>&&)> m_dispatchToRenderThread;
     RefPtr<Thread> m_renderThread;
-    CrossThreadQueue<Function<void()>> m_threadTaskQueue;
+
+    std::unique_ptr<RunLoop::Timer<RemoteAudioDestinationProxy>> m_renderingTimer;
+    float m_sampleRate { 0 };
 };
 
 } // namespace WebKit

Deleted: trunk/Source/WebKit/WebProcess/GPU/media/RemoteAudioDestinationProxy.messages.in (270906 => 270907)


--- trunk/Source/WebKit/WebProcess/GPU/media/RemoteAudioDestinationProxy.messages.in	2020-12-16 20:57:12 UTC (rev 270906)
+++ trunk/Source/WebKit/WebProcess/GPU/media/RemoteAudioDestinationProxy.messages.in	2020-12-16 21:07:59 UTC (rev 270907)
@@ -1,32 +0,0 @@
-# Copyright (C) 2020 Apple Inc. All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-# 1. Redistributions of source code must retain the above copyright
-#    notice, this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright
-#    notice, this list of conditions and the following disclaimer in the
-#    documentation and/or other materials provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
-# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
-# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
-# THE POSSIBILITY OF SUCH DAMAGE.
-
-#if ENABLE(GPU_PROCESS) && ENABLE(WEB_AUDIO)
-
-messages -> RemoteAudioDestinationProxy NotRefCounted {
-#if PLATFORM(COCOA)
-    RequestBuffer(double sampleTime, uint64_t hostTime, uint64_t numberOfFrames)
-#endif
-}
-
-#endif
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to