Changeset 220672 in webkit


Ignore:
Timestamp:
Aug 14, 2017 3:23:35 AM (7 years ago)
Author:
zandobersek@gmail.com
Message:

[ThreadedCompositor] Improve composition and update state handling
https://bugs.webkit.org/show_bug.cgi?id=172448

Reviewed by Carlos Garcia Campos.

In the ThreadedCompositor and related classes, we now track composition
and scene update states separately. This enables properly piping in the
DisplayRefreshMonitor object, leveraging it for both display refresh
notifications and signalling to the client that the scene update is
completed.

In CompositingRunLoop, two separate states are now used, one for
compositing and the other for scene update. Access to both should be done
while the related Lock object is held. The pending update state is also
tracked in order to schedule any required updates as soon as the current
state update is completed.

In ThreadedCompositor, the information about client notification and
update completion coordination is now also kept under a Lock object,
moving away from atomics. DisplayRefreshMonitor can now utilize just
two methods in order to either schedule an update or report that update
as finished.

  • Shared/CoordinatedGraphics/threadedcompositor/CompositingRunLoop.cpp:

(WebKit::CompositingRunLoop::CompositingRunLoop):
(WebKit::CompositingRunLoop::scheduleUpdate):
(WebKit::CompositingRunLoop::stopUpdates):
(WebKit::CompositingRunLoop::compositionCompleted):
(WebKit::CompositingRunLoop::updateCompleted):
(WebKit::CompositingRunLoop::updateTimerFired):
(WebKit::CompositingRunLoop::isActive): Deleted.

  • Shared/CoordinatedGraphics/threadedcompositor/CompositingRunLoop.h:

(WebKit::CompositingRunLoop::stateLock):

  • Shared/CoordinatedGraphics/threadedcompositor/ThreadedCompositor.cpp:

(WebKit::m_displayRefreshMonitor):
(WebKit::ThreadedCompositor::sceneUpdateFinished):
(WebKit::ThreadedCompositor::updateSceneState):
(WebKit::ThreadedCompositor::requestDisplayRefreshMonitorUpdate):
(WebKit::ThreadedCompositor::handleDisplayRefreshMonitorUpdate):
(WebKit::ThreadedCompositor::renderNextFrameIfNeeded): Deleted.
(WebKit::ThreadedCompositor::completeCoordinatedUpdateIfNeeded): Deleted.
(WebKit::ThreadedCompositor::coordinateUpdateCompletionWithClient): Deleted.

  • Shared/CoordinatedGraphics/threadedcompositor/ThreadedCompositor.h:
  • Shared/CoordinatedGraphics/threadedcompositor/ThreadedDisplayRefreshMonitor.cpp:

(WebKit::ThreadedDisplayRefreshMonitor::requestRefreshCallback):
(WebKit::ThreadedDisplayRefreshMonitor::displayRefreshCallback):

Location:
trunk/Source/WebKit
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebKit/ChangeLog

    r220639 r220672  
     12017-08-14  Zan Dobersek  <zdobersek@igalia.com>
     2
     3        [ThreadedCompositor] Improve composition and update state handling
     4        https://bugs.webkit.org/show_bug.cgi?id=172448
     5
     6        Reviewed by Carlos Garcia Campos.
     7
     8        In the ThreadedCompositor and related classes, we now track composition
     9        and scene update states separately. This enables properly piping in the
     10        DisplayRefreshMonitor object, leveraging it for both display refresh
     11        notifications and signalling to the client that the scene update is
     12        completed.
     13
     14        In CompositingRunLoop, two separate states are now used, one for
     15        compositing and the other for scene update. Access to both should be done
     16        while the related Lock object is held. The pending update state is also
     17        tracked in order to schedule any required updates as soon as the current
     18        state update is completed.
     19
     20        In ThreadedCompositor, the information about client notification and
     21        update completion coordination is now also kept under a Lock object,
     22        moving away from atomics. DisplayRefreshMonitor can now utilize just
     23        two methods in order to either schedule an update or report that update
     24        as finished.
     25
     26        * Shared/CoordinatedGraphics/threadedcompositor/CompositingRunLoop.cpp:
     27        (WebKit::CompositingRunLoop::CompositingRunLoop):
     28        (WebKit::CompositingRunLoop::scheduleUpdate):
     29        (WebKit::CompositingRunLoop::stopUpdates):
     30        (WebKit::CompositingRunLoop::compositionCompleted):
     31        (WebKit::CompositingRunLoop::updateCompleted):
     32        (WebKit::CompositingRunLoop::updateTimerFired):
     33        (WebKit::CompositingRunLoop::isActive): Deleted.
     34        * Shared/CoordinatedGraphics/threadedcompositor/CompositingRunLoop.h:
     35        (WebKit::CompositingRunLoop::stateLock):
     36        * Shared/CoordinatedGraphics/threadedcompositor/ThreadedCompositor.cpp:
     37        (WebKit::m_displayRefreshMonitor):
     38        (WebKit::ThreadedCompositor::sceneUpdateFinished):
     39        (WebKit::ThreadedCompositor::updateSceneState):
     40        (WebKit::ThreadedCompositor::requestDisplayRefreshMonitorUpdate):
     41        (WebKit::ThreadedCompositor::handleDisplayRefreshMonitorUpdate):
     42        (WebKit::ThreadedCompositor::renderNextFrameIfNeeded): Deleted.
     43        (WebKit::ThreadedCompositor::completeCoordinatedUpdateIfNeeded): Deleted.
     44        (WebKit::ThreadedCompositor::coordinateUpdateCompletionWithClient): Deleted.
     45        * Shared/CoordinatedGraphics/threadedcompositor/ThreadedCompositor.h:
     46        * Shared/CoordinatedGraphics/threadedcompositor/ThreadedDisplayRefreshMonitor.cpp:
     47        (WebKit::ThreadedDisplayRefreshMonitor::requestRefreshCallback):
     48        (WebKit::ThreadedDisplayRefreshMonitor::displayRefreshCallback):
     49
    1502017-08-13  Manuel Rego Casasnovas  <rego@igalia.com>
    251
  • trunk/Source/WebKit/Shared/CoordinatedGraphics/threadedcompositor/CompositingRunLoop.cpp

    r218763 r220672  
    110110    , m_updateFunction(WTFMove(updateFunction))
    111111{
    112     m_updateState.store(UpdateState::Completed);
    113 
    114112#if USE(GLIB_EVENT_LOOP)
    115113    m_updateTimer.setPriority(RunLoopSourcePriority::CompositingThreadUpdateTimer);
     
    145143}
    146144
    147 bool CompositingRunLoop::isActive()
    148 {
    149     return m_updateState.load() != UpdateState::Completed;
    150 }
    151 
    152145void CompositingRunLoop::scheduleUpdate()
    153146{
    154     if (m_updateState.compareExchangeStrong(UpdateState::Completed, UpdateState::InProgress) == UpdateState::Completed) {
     147    LockHolder stateLocker(m_state.lock);
     148    scheduleUpdate(stateLocker);
     149}
     150
     151void CompositingRunLoop::scheduleUpdate(LockHolder& stateLocker)
     152{
     153    // An update was requested. Depending on the state:
     154    //  - if Idle, enter the Scheduled state and start the update timer,
     155    //  - if Scheduled, do nothing,
     156    //  - if InProgress or PendingCompletion, mark an update as pending, meaning another
     157    //    update will be scheduled as soon as the current one is completed.
     158
     159    UNUSED_PARAM(stateLocker);
     160
     161    switch (m_state.update) {
     162    case UpdateState::Idle:
     163        m_state.update = UpdateState::Scheduled;
    155164        m_updateTimer.startOneShot(0);
    156165        return;
    157     }
    158 
    159     if (m_updateState.compareExchangeStrong(UpdateState::InProgress, UpdateState::PendingAfterCompletion) == UpdateState::InProgress)
    160         return;
     166    case UpdateState::Scheduled:
     167        return;
     168    case UpdateState::InProgress:
     169    case UpdateState::PendingCompletion:
     170        m_state.pendingUpdate = true;
     171        return;
     172    }
    161173}
    162174
    163175void CompositingRunLoop::stopUpdates()
    164176{
     177    // Stop everything.
     178
     179    LockHolder locker(m_state.lock);
    165180    m_updateTimer.stop();
    166     m_updateState.store(UpdateState::Completed);
    167 }
    168 
    169 void CompositingRunLoop::updateCompleted()
    170 {
    171     if (m_updateState.compareExchangeStrong(UpdateState::InProgress, UpdateState::Completed) == UpdateState::InProgress)
    172         return;
    173 
    174     if (m_updateState.compareExchangeStrong(UpdateState::PendingAfterCompletion, UpdateState::InProgress) == UpdateState::PendingAfterCompletion) {
    175         m_updateTimer.startOneShot(0);
    176         return;
    177     }
    178 
    179     ASSERT_NOT_REACHED();
     181    m_state.composition = CompositionState::Idle;
     182    m_state.update = UpdateState::Idle;
     183    m_state.pendingUpdate = false;
     184}
     185
     186void CompositingRunLoop::compositionCompleted(LockHolder& stateLocker)
     187{
     188    // Composition has been signaled as completed, pushing the state into Idle.
     189    // Depending on the state of the scene update:
     190    //  - if Idle, Scheduled or InProgress, do nothing,
     191    //  - if PendingCompletion, schedule a new update in case a pending update was marked,
     192    //    or push the scene update state into Idle otherwise.
     193
     194    UNUSED_PARAM(stateLocker);
     195
     196    m_state.composition = CompositionState::Idle;
     197
     198    switch (m_state.update) {
     199    case UpdateState::Idle:
     200    case UpdateState::Scheduled:
     201    case UpdateState::InProgress:
     202        return;
     203    case UpdateState::PendingCompletion:
     204        if (m_state.pendingUpdate) {
     205            m_state.pendingUpdate = false;
     206            m_state.update = UpdateState::Scheduled;
     207            m_updateTimer.startOneShot(0);
     208            return;
     209        }
     210
     211        m_state.update = UpdateState::Idle;
     212        return;
     213    }
     214}
     215
     216void CompositingRunLoop::updateCompleted(LockHolder& stateLocker)
     217{
     218    // Scene update has been signaled as completed. Depending on the state:
     219    //  - if Idle, Scheduled or InProgress, do nothing,
     220    //  - if InProgress, push the state into PendingCompletion if the composition state is
     221    //    InProgress, otherwise schedule a new update in case a pending update was marked,
     222    //    otherwise push the scene update state into Idle.
     223
     224    UNUSED_PARAM(stateLocker);
     225
     226    switch (m_state.update) {
     227    case UpdateState::Idle:
     228    case UpdateState::Scheduled:
     229        return;
     230    case UpdateState::InProgress:
     231        if (m_state.composition == CompositionState::InProgress) {
     232            m_state.update = UpdateState::PendingCompletion;
     233            return;
     234        }
     235
     236        if (m_state.pendingUpdate) {
     237            m_state.pendingUpdate = false;
     238            m_state.update = UpdateState::Scheduled;
     239            m_updateTimer.startOneShot(0);
     240            return;
     241        }
     242
     243        m_state.update = UpdateState::Idle;
     244        return;
     245    case UpdateState::PendingCompletion:
     246        return;
     247    }
    180248}
    181249
    182250void CompositingRunLoop::updateTimerFired()
    183251{
     252    {
     253        // Both composition and scene update are now in progress.
     254        LockHolder locker(m_state.lock);
     255        m_state.composition = CompositionState::InProgress;
     256        m_state.update = UpdateState::InProgress;
     257    }
    184258    m_updateFunction();
    185259}
  • trunk/Source/WebKit/Shared/CoordinatedGraphics/threadedcompositor/CompositingRunLoop.h

    r218457 r220672  
    4949    void performTaskSync(Function<void ()>&&);
    5050
    51     bool isActive();
     51    Lock& stateLock() { return m_state.lock; }
     52
    5253    void scheduleUpdate();
     54    void scheduleUpdate(LockHolder&);
    5355    void stopUpdates();
    5456
    55     void updateCompleted();
     57    void compositionCompleted(LockHolder&);
     58    void updateCompleted(LockHolder&);
    5659
    5760private:
     61    enum class CompositionState {
     62        Idle,
     63        InProgress,
     64    };
    5865    enum class UpdateState {
    59         Completed,
     66        Idle,
     67        Scheduled,
    6068        InProgress,
    61         PendingAfterCompletion,
     69        PendingCompletion,
    6270    };
    6371
     
    6674    RunLoop::Timer<CompositingRunLoop> m_updateTimer;
    6775    Function<void ()> m_updateFunction;
    68     Atomic<UpdateState> m_updateState;
    6976    Lock m_dispatchSyncConditionMutex;
    7077    Condition m_dispatchSyncCondition;
     78
     79
     80    struct {
     81        Lock lock;
     82        CompositionState composition { CompositionState::Idle };
     83        UpdateState update { UpdateState::Idle };
     84        bool pendingUpdate { false };
     85    } m_state;
    7186};
    7287
  • trunk/Source/WebKit/Shared/CoordinatedGraphics/threadedcompositor/ThreadedCompositor.cpp

    r219391 r220672  
    6969    }
    7070
    71     m_clientRendersNextFrame.store(false);
    72     m_coordinateUpdateCompletionWithClient.store(false);
    73 
    7471    m_compositingRunLoop->performTaskSync([this, protectedThis = makeRef(*this)] {
    7572        m_scene = adoptRef(new CoordinatedGraphicsScene(this));
     
    247244void ThreadedCompositor::sceneUpdateFinished()
    248245{
    249     bool shouldDispatchDisplayRefreshCallback = m_clientRendersNextFrame.load()
    250         || m_displayRefreshMonitor->requiresDisplayRefreshCallback();
    251     bool shouldCoordinateUpdateCompletionWithClient = m_coordinateUpdateCompletionWithClient.load();
    252 
     246    // The composition has finished. Now we have to determine how to manage
     247    // the scene update completion.
     248
     249    // The DisplayRefreshMonitor will be used to dispatch a callback on the client thread if:
     250    //  - clientRendersNextFrame is true (i.e. client has to be notified about the finished update), or
     251    //  - a DisplayRefreshMonitor callback was requested from the Web engine
     252    bool shouldDispatchDisplayRefreshCallback { false };
     253
     254    // If coordinateUpdateCompletionWithClient is true, the scene update completion has to be
     255    // delayed until the DisplayRefreshMonitor callback.
     256    bool shouldCoordinateUpdateCompletionWithClient { false };
     257
     258    {
     259        LockHolder locker(m_attributes.lock);
     260        shouldDispatchDisplayRefreshCallback = m_attributes.clientRendersNextFrame
     261            || m_displayRefreshMonitor->requiresDisplayRefreshCallback();
     262        shouldCoordinateUpdateCompletionWithClient = m_attributes.coordinateUpdateCompletionWithClient;
     263    }
     264
     265    LockHolder stateLocker(m_compositingRunLoop->stateLock());
     266
     267    // Schedule the DisplayRefreshMonitor callback, if necessary.
    253268    if (shouldDispatchDisplayRefreshCallback)
    254269        m_displayRefreshMonitor->dispatchDisplayRefreshCallback();
     270
     271    // Mark the scene update as completed if no coordination is required and if not in a forced repaint.
    255272    if (!shouldCoordinateUpdateCompletionWithClient && !m_inForceRepaint)
    256         m_compositingRunLoop->updateCompleted();
     273        m_compositingRunLoop->updateCompleted(stateLocker);
     274
     275    // Independent of the scene update, the composition itself is now completed.
     276    m_compositingRunLoop->compositionCompleted(stateLocker);
    257277}
    258278
     
    263283        scene->commitSceneState(state);
    264284
    265         m_clientRendersNextFrame.store(true);
     285        LockHolder locker(m_attributes.lock);
     286
     287        // Client has to be notified upon finishing this scene update.
     288        m_attributes.clientRendersNextFrame = true;
     289
     290        // Coordinate scene update completion with the client in case of changed or updated platform layers.
    266291        // Do not change m_coordinateUpdateCompletionWithClient while in force repaint.
    267         if (m_inForceRepaint)
    268             return;
    269         bool coordinateUpdate = std::any_of(state.layersToUpdate.begin(), state.layersToUpdate.end(),
     292        bool coordinateUpdate = !m_inForceRepaint && std::any_of(state.layersToUpdate.begin(), state.layersToUpdate.end(),
    270293            [](const std::pair<CoordinatedLayerID, CoordinatedGraphicsLayerState>& it) {
    271294                return it.second.platformLayerChanged || it.second.platformLayerUpdated;
    272295            });
    273         m_coordinateUpdateCompletionWithClient.store(coordinateUpdate);
     296
     297        m_attributes.coordinateUpdateCompletionWithClient |= coordinateUpdate;
    274298    });
    275299
     
    292316}
    293317
    294 void ThreadedCompositor::renderNextFrameIfNeeded()
    295 {
    296     if (m_clientRendersNextFrame.compareExchangeStrong(true, false))
    297         m_scene->renderNextFrame();
    298 }
    299 
    300 void ThreadedCompositor::completeCoordinatedUpdateIfNeeded()
    301 {
    302     if (m_coordinateUpdateCompletionWithClient.compareExchangeStrong(true, false))
    303         m_compositingRunLoop->updateCompleted();
    304 }
    305 
    306 void ThreadedCompositor::coordinateUpdateCompletionWithClient()
    307 {
    308     m_coordinateUpdateCompletionWithClient.store(true);
    309     if (!m_compositingRunLoop->isActive())
    310         m_compositingRunLoop->scheduleUpdate();
     318void ThreadedCompositor::requestDisplayRefreshMonitorUpdate()
     319{
     320    // This is invoked by ThreadedDisplayRefreshMonitor when a fresh update is required.
     321
     322    LockHolder stateLocker(m_compositingRunLoop->stateLock());
     323    {
     324        // coordinateUpdateCompletionWithClient is set to true in order to delay the scene update
     325        // completion until the DisplayRefreshMonitor is fired on the main thread after the composition
     326        // is completed.
     327        LockHolder locker(m_attributes.lock);
     328        m_attributes.coordinateUpdateCompletionWithClient = true;
     329    }
     330    m_compositingRunLoop->scheduleUpdate(stateLocker);
     331}
     332
     333void ThreadedCompositor::handleDisplayRefreshMonitorUpdate(bool hasBeenRescheduled)
     334{
     335    // Retrieve the clientRendersNextFrame and coordinateUpdateCompletionWithClient.
     336    bool clientRendersNextFrame { false };
     337    bool coordinateUpdateCompletionWithClient { false };
     338    {
     339        LockHolder locker(m_attributes.lock);
     340        clientRendersNextFrame = std::exchange(m_attributes.clientRendersNextFrame, false);
     341        coordinateUpdateCompletionWithClient = std::exchange(m_attributes.coordinateUpdateCompletionWithClient, false);
     342    }
     343
     344    // If clientRendersNextFrame is true, the client is finally notified about the scene update nearing
     345    // completion. The client will use this opportunity to clean up resources as appropriate. It can also
     346    // perform any layer flush that was requested during the composition, or by any DisplayRefreshMonitor
     347    // notifications that have been handled at this point.
     348    if (clientRendersNextFrame)
     349        m_client.renderNextFrame();
     350
     351    LockHolder stateLocker(m_compositingRunLoop->stateLock());
     352
     353    // If required, mark the current scene update as completed. CompositingRunLoop will take care of
     354    // scheduling a new update in case an update was marked as pending due to previous layer flushes
     355    // or DisplayRefreshMonitor notifications.
     356    if (coordinateUpdateCompletionWithClient)
     357        m_compositingRunLoop->updateCompleted(stateLocker);
     358
     359    // If the DisplayRefreshMonitor was scheduled again, we immediately demand the update completion
     360    // coordination (like we do in requestDisplayRefreshMonitorUpdate()) and request an update.
     361    if (hasBeenRescheduled) {
     362        {
     363            LockHolder locker(m_attributes.lock);
     364            m_attributes.coordinateUpdateCompletionWithClient = true;
     365        }
     366        m_compositingRunLoop->scheduleUpdate(stateLocker);
     367    }
    311368}
    312369#endif
  • trunk/Source/WebKit/Shared/CoordinatedGraphics/threadedcompositor/ThreadedCompositor.h

    r219067 r220672  
    8989#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
    9090    RefPtr<WebCore::DisplayRefreshMonitor> displayRefreshMonitor(WebCore::PlatformDisplayID);
    91     void renderNextFrameIfNeeded();
    92     void completeCoordinatedUpdateIfNeeded();
    93     void coordinateUpdateCompletionWithClient();
     91    void requestDisplayRefreshMonitorUpdate();
     92    void handleDisplayRefreshMonitorUpdate(bool hasBeenRescheduled);
    9493#endif
    9594
     
    127126        bool drawsBackground { true };
    128127        bool needsResize { false };
     128
     129        bool clientRendersNextFrame { false };
     130        bool coordinateUpdateCompletionWithClient { false };
    129131    } m_attributes;
    130132
     
    132134    Ref<ThreadedDisplayRefreshMonitor> m_displayRefreshMonitor;
    133135#endif
    134 
    135     Atomic<bool> m_clientRendersNextFrame;
    136     Atomic<bool> m_coordinateUpdateCompletionWithClient;
    137136};
    138137
  • trunk/Source/WebKit/Shared/CoordinatedGraphics/threadedcompositor/ThreadedDisplayRefreshMonitor.cpp

    r216970 r220672  
    5454    setIsScheduled(true);
    5555
     56    // Only request an update in case we're not currently handling the display
     57    // refresh notifications under ThreadedDisplayRefreshMonitor::displayRefreshCallback().
     58    // Any such schedule request is handled in that method after the notifications.
    5659    if (isPreviousFrameDone())
    57         m_compositor->coordinateUpdateCompletionWithClient();
     60        m_compositor->requestDisplayRefreshMonitorUpdate();
    5861
    5962    return true;
     
    8184void ThreadedDisplayRefreshMonitor::displayRefreshCallback()
    8285{
    83     bool shouldHandleDisplayRefreshNotification = false;
     86    bool shouldHandleDisplayRefreshNotification { false };
    8487    {
    8588        LockHolder locker(mutex());
     
    9295        DisplayRefreshMonitor::handleDisplayRefreshedNotificationOnMainThread(this);
    9396
    94     if (m_compositor) {
    95         m_compositor->renderNextFrameIfNeeded();
    96         m_compositor->completeCoordinatedUpdateIfNeeded();
    97         if (isScheduled())
    98             m_compositor->coordinateUpdateCompletionWithClient();
     97    // Retrieve the scheduled status for this DisplayRefreshMonitor.
     98    bool hasBeenRescheduled { false };
     99    {
     100        LockHolder locker(mutex());
     101        hasBeenRescheduled = isScheduled();
    99102    }
     103
     104    // Notify the compositor about the completed DisplayRefreshMonitor update, passing
     105    // along information about any schedule request that might have occurred during
     106    // the notification handling.
     107    if (m_compositor)
     108        m_compositor->handleDisplayRefreshMonitorUpdate(hasBeenRescheduled);
    100109}
    101110
Note: See TracChangeset for help on using the changeset viewer.