⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 276189 in webkit


Ignore:
Timestamp:
Apr 16, 2021, 6:30:19 PM (5 years ago)
Author:
Chris Dumez
Message:

RemoteAudioDestinationProxy should not launch / relaunch the GPUProcess unless it is actually rendering
https://bugs.webkit.org/show_bug.cgi?id=224691

Reviewed by Geoffrey Garen.

Source/WebKit:

RemoteAudioDestinationProxy was initiating a connection to the GPUProcess in its constructor and
re-initiating the connection right away upon GPUProcess crash. This goes against our recent efforts
to run the GPUProcess only when it is actually needed. The RemoteAudioDestinationProxy really only
needs the GPUProcess when it is actually rendering / playing.

  • GPUProcess/media/RemoteAudioDestinationManager.cpp:

(WebKit::RemoteAudioDestinationManager::allowsExitUnderMemoryPressure const):
Allow the GPUProcess to exit when under memory pressure even if it has AudioDestinations, as long
as they are not playing.

  • WebProcess/GPU/media/RemoteAudioDestinationProxy.cpp:

(WebKit::RemoteAudioDestinationProxy::RemoteAudioDestinationProxy):
(WebKit::RemoteAudioDestinationProxy::ensureGPUProcessConnection):
(WebKit::RemoteAudioDestinationProxy::~RemoteAudioDestinationProxy):
(WebKit::RemoteAudioDestinationProxy::startRendering):
(WebKit::RemoteAudioDestinationProxy::stopRendering):
(WebKit::RemoteAudioDestinationProxy::storageChanged):
(WebKit::RemoteAudioDestinationProxy::gpuProcessConnectionDidClose):

  • WebProcess/GPU/media/RemoteAudioDestinationProxy.h:

Tools:

Add API test coverage.

  • TestWebKitAPI/Tests/WebKitCocoa/GPUProcess.mm:

(TEST):

  • TestWebKitAPI/Tests/WebKitCocoa/audio-context-playing.html:
Location:
trunk
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebKit/ChangeLog

    r276188 r276189  
     12021-04-16  Chris Dumez  <cdumez@apple.com>
     2
     3        RemoteAudioDestinationProxy should not launch / relaunch the GPUProcess unless it is actually rendering
     4        https://bugs.webkit.org/show_bug.cgi?id=224691
     5
     6        Reviewed by Geoffrey Garen.
     7
     8        RemoteAudioDestinationProxy was initiating a connection to the GPUProcess in its constructor and
     9        re-initiating the connection right away upon GPUProcess crash. This goes against our recent efforts
     10        to run the GPUProcess only when it is actually needed. The RemoteAudioDestinationProxy really only
     11        needs the GPUProcess when it is actually rendering / playing.
     12
     13        * GPUProcess/media/RemoteAudioDestinationManager.cpp:
     14        (WebKit::RemoteAudioDestinationManager::allowsExitUnderMemoryPressure const):
     15        Allow the GPUProcess to exit when under memory pressure even if it has AudioDestinations, as long
     16        as they are not playing.
     17
     18        * WebProcess/GPU/media/RemoteAudioDestinationProxy.cpp:
     19        (WebKit::RemoteAudioDestinationProxy::RemoteAudioDestinationProxy):
     20        (WebKit::RemoteAudioDestinationProxy::ensureGPUProcessConnection):
     21        (WebKit::RemoteAudioDestinationProxy::~RemoteAudioDestinationProxy):
     22        (WebKit::RemoteAudioDestinationProxy::startRendering):
     23        (WebKit::RemoteAudioDestinationProxy::stopRendering):
     24        (WebKit::RemoteAudioDestinationProxy::storageChanged):
     25        (WebKit::RemoteAudioDestinationProxy::gpuProcessConnectionDidClose):
     26        * WebProcess/GPU/media/RemoteAudioDestinationProxy.h:
     27
    1282021-04-16  Chris Dumez  <cdumez@apple.com>
    229
  • trunk/Source/WebKit/GPUProcess/media/RemoteAudioDestinationManager.cpp

    r276188 r276189  
    190190bool RemoteAudioDestinationManager::allowsExitUnderMemoryPressure() const
    191191{
    192     return m_audioDestinations.isEmpty();
     192    for (auto& audioDestination : m_audioDestinations.values()) {
     193        if (audioDestination->isPlaying())
     194            return false;
     195    }
     196    return true;
    193197}
    194198
  • trunk/Source/WebKit/WebProcess/GPU/media/RemoteAudioDestinationProxy.cpp

    r275553 r276189  
    7070    , m_numberOfInputChannels(numberOfInputChannels)
    7171{
    72     connectToGPUProcess();
    7372}
    7473
     
    10099}
    101100
    102 void RemoteAudioDestinationProxy::connectToGPUProcess()
    103 {
    104     auto& connection = WebProcess::singleton().ensureGPUProcessConnection();
    105     connection.addClient(*this);
    106     auto didSucceed = connection.connection().sendSync(
    107         Messages::RemoteAudioDestinationManager::CreateAudioDestination(m_inputDeviceId, m_numberOfInputChannels, numberOfOutputChannels(), sampleRate(), hardwareSampleRate(), m_renderSemaphore), Messages::RemoteAudioDestinationManager::CreateAudioDestination::Reply(m_destinationID), 0);
    108 
    109     if (!didSucceed) {
    110         // The GPUProcess likely crashed during this synchronous IPC. gpuProcessConnectionDidClose() will get called to reconnect to the GPUProcess.
    111         RELEASE_LOG_ERROR(Media, "RemoteAudioDestinationProxy::connectToGPUProcess: Failed to send RemoteAudioDestinationManager::CreateAudioDestination() IPC (GPU process likely crashed)");
    112         return;
     101GPUProcessConnection& RemoteAudioDestinationProxy::ensureGPUProcessConnection()
     102{
     103    if (!m_gpuProcessConnection) {
     104        m_gpuProcessConnection = makeWeakPtr(WebProcess::singleton().ensureGPUProcessConnection());
     105        m_gpuProcessConnection->addClient(*this);
     106
     107#if PLATFORM(COCOA)
     108        m_currentFrame = 0;
     109        AudioStreamBasicDescription streamFormat;
     110        getAudioStreamBasicDescription(streamFormat);
     111        m_ringBuffer->allocate(streamFormat, m_numberOfFrames);
     112        m_audioBufferList = makeUnique<WebCore::WebAudioBufferList>(streamFormat);
     113        m_audioBufferList->setSampleCount(WebCore::AudioUtilities::renderQuantumSize);
     114#endif
     115
     116        startRenderingThread();
    113117    }
    114 
    115 
    116 #if PLATFORM(COCOA)
    117     m_currentFrame = 0;
    118     AudioStreamBasicDescription streamFormat;
    119     getAudioStreamBasicDescription(streamFormat);
    120     m_ringBuffer->allocate(streamFormat, m_numberOfFrames);
    121     m_audioBufferList = makeUnique<WebCore::WebAudioBufferList>(streamFormat);
    122     m_audioBufferList->setSampleCount(WebCore::AudioUtilities::renderQuantumSize);
    123 #endif
    124 
    125     startRenderingThread();
     118    return *m_gpuProcessConnection;
     119}
     120
     121RemoteAudioDestinationIdentifier RemoteAudioDestinationProxy::destinationID()
     122{
     123    if (!m_destinationID)
     124        ensureGPUProcessConnection().connection().sendSync(Messages::RemoteAudioDestinationManager::CreateAudioDestination(m_inputDeviceId, m_numberOfInputChannels, numberOfOutputChannels(), sampleRate(), hardwareSampleRate(), m_renderSemaphore), Messages::RemoteAudioDestinationManager::CreateAudioDestination::Reply(m_destinationID), 0);
     125    return m_destinationID;
    126126}
    127127
    128128RemoteAudioDestinationProxy::~RemoteAudioDestinationProxy()
    129129{
    130     auto& connection =  WebProcess::singleton().ensureGPUProcessConnection();
    131 
    132     connection.connection().sendWithAsyncReply(
    133         Messages::RemoteAudioDestinationManager::DeleteAudioDestination(m_destinationID), [] {
    134         // Can't remove this from proxyMap() here because the object would have been already deleted.
    135     });
     130    if (m_gpuProcessConnection && m_destinationID) {
     131        m_gpuProcessConnection->connection().sendWithAsyncReply(
     132            Messages::RemoteAudioDestinationManager::DeleteAudioDestination(m_destinationID), [] {
     133            // Can't remove this from proxyMap() here because the object would have been already deleted.
     134        });
     135    }
    136136
    137137    stopRenderingThread();
     
    140140void RemoteAudioDestinationProxy::startRendering(CompletionHandler<void(bool)>&& completionHandler)
    141141{
    142     WebProcess::singleton().ensureGPUProcessConnection().connection().sendWithAsyncReply(Messages::RemoteAudioDestinationManager::StartAudioDestination(m_destinationID), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](bool isPlaying) mutable {
     142    ensureGPUProcessConnection().connection().sendWithAsyncReply(Messages::RemoteAudioDestinationManager::StartAudioDestination(destinationID()), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](bool isPlaying) mutable {
    143143        setIsPlaying(isPlaying);
    144144        completionHandler(isPlaying);
     
    148148void RemoteAudioDestinationProxy::stopRendering(CompletionHandler<void(bool)>&& completionHandler)
    149149{
    150     WebProcess::singleton().ensureGPUProcessConnection().connection().sendWithAsyncReply(Messages::RemoteAudioDestinationManager::StopAudioDestination(m_destinationID), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](bool isPlaying) mutable {
     150    ensureGPUProcessConnection().connection().sendWithAsyncReply(Messages::RemoteAudioDestinationManager::StopAudioDestination(destinationID()), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](bool isPlaying) mutable {
    151151        setIsPlaying(isPlaying);
    152152        completionHandler(!isPlaying);
     
    168168void RemoteAudioDestinationProxy::storageChanged(SharedMemory* storage, const WebCore::CAAudioStreamDescription& format, size_t frameCount)
    169169{
     170    if (!m_gpuProcessConnection)
     171        return;
     172
    170173    SharedMemory::Handle handle;
    171174    if (storage)
     
    179182#endif
    180183
    181     WebProcess::singleton().ensureGPUProcessConnection().connection().send(Messages::RemoteAudioDestinationManager::AudioSamplesStorageChanged { m_destinationID, SharedMemory::IPCHandle { WTFMove(handle), dataSize }, format, frameCount }, 0);
     184    m_gpuProcessConnection->connection().send(Messages::RemoteAudioDestinationManager::AudioSamplesStorageChanged { destinationID(), SharedMemory::IPCHandle { WTFMove(handle), dataSize }, format, frameCount }, 0);
    182185}
    183186#endif
     
    188191
    189192    stopRenderingThread();
    190 
    191     connectToGPUProcess();
     193    m_gpuProcessConnection = nullptr;
     194    m_destinationID = { };
    192195
    193196    if (isPlaying())
  • trunk/Source/WebKit/WebProcess/GPU/media/RemoteAudioDestinationProxy.h

    r272382 r276189  
    8080    void renderQuantum();
    8181
    82     void connectToGPUProcess();
     82    RemoteAudioDestinationIdentifier destinationID();
     83    GPUProcessConnection& ensureGPUProcessConnection();
    8384
    8485    // GPUProcessConnection::Client.
     
    9697#endif
    9798
    98     RemoteAudioDestinationIdentifier m_destinationID;
     99    RemoteAudioDestinationIdentifier m_destinationID; // Call destinationID() getter to make sure the destinationID is valid.
    99100
     101    WeakPtr<GPUProcessConnection> m_gpuProcessConnection;
    100102#if PLATFORM(COCOA)
    101103    uint64_t m_numberOfFrames { 0 };
  • trunk/Tools/ChangeLog

    r276184 r276189  
     12021-04-16  Chris Dumez  <cdumez@apple.com>
     2
     3        RemoteAudioDestinationProxy should not launch / relaunch the GPUProcess unless it is actually rendering
     4        https://bugs.webkit.org/show_bug.cgi?id=224691
     5
     6        Reviewed by Geoffrey Garen.
     7
     8        Add API test coverage.
     9
     10        * TestWebKitAPI/Tests/WebKitCocoa/GPUProcess.mm:
     11        (TEST):
     12        * TestWebKitAPI/Tests/WebKitCocoa/audio-context-playing.html:
     13
    1142021-04-16  Jiewen Tan  <jiewen_tan@apple.com>
    215
  • trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/GPUProcess.mm

    r276148 r276189  
    580580    });
    581581}
     582
     583TEST(GPUProcess, ExitsUnderMemoryPressureWebAudioNonRenderingAudioContext)
     584{
     585    auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
     586    WKPreferencesSetBoolValueForKeyForTesting((__bridge WKPreferencesRef)[configuration preferences], true, WKStringCreateWithUTF8CString("UseGPUProcessForMediaEnabled"));
     587    WKPreferencesSetBoolValueForKeyForTesting((__bridge WKPreferencesRef)[configuration preferences], true, WKStringCreateWithUTF8CString("CaptureVideoInGPUProcessEnabled"));
     588    WKPreferencesSetBoolValueForKeyForTesting((__bridge WKPreferencesRef)[configuration preferences], true, WKStringCreateWithUTF8CString("UseGPUProcessForCanvasRenderingEnabled"));
     589    WKPreferencesSetBoolValueForKeyForTesting((__bridge WKPreferencesRef)[configuration preferences], false, WKStringCreateWithUTF8CString("UseGPUProcessForDOMRenderingEnabled"));
     590
     591    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 400, 400) configuration:configuration.get()]);
     592    [webView synchronouslyLoadTestPageNamed:@"audio-context-playing"];
     593
     594    // evaluateJavaScript gives us the user gesture we need to reliably start audio playback on all platforms.
     595    __block bool done = false;
     596    [webView evaluateJavaScript:@"startPlaying()" completionHandler:^(id result, NSError *error) {
     597        EXPECT_TRUE(!error);
     598        done = true;
     599    }];
     600    TestWebKitAPI::Util::run(&done);
     601
     602    // A GPUProcess should get launched.
     603    while (![configuration.get().processPool _gpuProcessIdentifier])
     604        TestWebKitAPI::Util::sleep(0.1);
     605    auto gpuProcessPID = [configuration.get().processPool _gpuProcessIdentifier];
     606
     607    // Simulate memory pressure (notifyutil -p org.WebKit.lowMemory).
     608    notify_post("org.WebKit.lowMemory");
     609
     610    // Make sure the GPUProcess does not exit since it is still needed.
     611    TestWebKitAPI::Util::sleep(0.5);
     612    EXPECT_EQ(gpuProcessPID, [configuration.get().processPool _gpuProcessIdentifier]);
     613
     614    // Suspend audio rendering.
     615    [webView evaluateJavaScript:@"context.suspend() && true" completionHandler:^(id result, NSError *error) {
     616        EXPECT_TRUE(!error);
     617        done = true;
     618    }];
     619
     620    // The GPUProcess should exit on memory pressure.
     621    do {
     622        // Simulate memory pressure (notifyutil -p org.WebKit.lowMemory).
     623        notify_post("org.WebKit.lowMemory");
     624        TestWebKitAPI::Util::sleep(0.1);
     625    } while ([configuration.get().processPool _gpuProcessIdentifier]);
     626
     627    // The GPUProcess should not relaunch.
     628    TestWebKitAPI::Util::sleep(0.5);
     629    EXPECT_EQ(0, [configuration.get().processPool _gpuProcessIdentifier]);
     630}
  • trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/audio-context-playing.html

    r269698 r276189  
    55function startPlaying()
    66{
    7     let context = new AudioContext();
     7    context = new AudioContext();
    88    let oscillator = new OscillatorNode(context);
    99    oscillator.connect(context.destination);
Note: See TracChangeset for help on using the changeset viewer.