[ThreadedCompositor] Simply the compositing run loop worker thread
[WebKit-https.git] / Source / WebKit / Shared / CoordinatedGraphics / threadedcompositor / CompositingRunLoop.cpp
1 /*
2  * Copyright (C) 2014 Igalia S.L.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "CompositingRunLoop.h"
28
29 #if USE(COORDINATED_GRAPHICS)
30
31 #include <wtf/HashMap.h>
32 #include <wtf/MainThread.h>
33 #include <wtf/Threading.h>
34 #include <wtf/threads/BinarySemaphore.h>
35
36 #if USE(GLIB_EVENT_LOOP)
37 #include <wtf/glib/RunLoopSourcePriority.h>
38 #endif
39
40 namespace WebKit {
41
42 static RunLoop* createRunLoop()
43 {
44     RunLoop* runLoop = nullptr;
45     BinarySemaphore semaphore;
46     Thread::create("org.webkit.ThreadedCompositor", [&] {
47         runLoop = &RunLoop::current();
48         semaphore.signal();
49         runLoop->run();
50     })->detach();
51     semaphore.wait();
52
53     return runLoop;
54 }
55
56 CompositingRunLoop::CompositingRunLoop(Function<void ()>&& updateFunction)
57     : m_runLoop(createRunLoop())
58     , m_updateTimer(*m_runLoop, this, &CompositingRunLoop::updateTimerFired)
59     , m_updateFunction(WTFMove(updateFunction))
60 {
61 #if USE(GLIB_EVENT_LOOP)
62     m_updateTimer.setPriority(RunLoopSourcePriority::CompositingThreadUpdateTimer);
63     m_updateTimer.setName("[WebKit] CompositingRunLoop");
64 #endif
65 }
66
67 CompositingRunLoop::~CompositingRunLoop()
68 {
69     ASSERT(RunLoop::isMain());
70     // Make sure the RunLoop is stopped after the CompositingRunLoop, because m_updateTimer has a reference.
71     RunLoop::main().dispatch([runLoop = makeRef(*m_runLoop)] {
72         runLoop->stop();
73         runLoop->dispatch([] {
74             RunLoop::current().stop();
75         });
76     });
77 }
78
79 void CompositingRunLoop::performTask(Function<void ()>&& function)
80 {
81     ASSERT(RunLoop::isMain());
82     m_runLoop->dispatch(WTFMove(function));
83 }
84
85 void CompositingRunLoop::performTaskSync(Function<void ()>&& function)
86 {
87     ASSERT(RunLoop::isMain());
88     LockHolder locker(m_dispatchSyncConditionMutex);
89     m_runLoop->dispatch([this, function = WTFMove(function)] {
90         function();
91         LockHolder locker(m_dispatchSyncConditionMutex);
92         m_dispatchSyncCondition.notifyOne();
93     });
94     m_dispatchSyncCondition.wait(m_dispatchSyncConditionMutex);
95 }
96
97 void CompositingRunLoop::scheduleUpdate()
98 {
99     LockHolder stateLocker(m_state.lock);
100     scheduleUpdate(stateLocker);
101 }
102
103 void CompositingRunLoop::scheduleUpdate(LockHolder& stateLocker)
104 {
105     // An update was requested. Depending on the state:
106     //  - if Idle, enter the Scheduled state and start the update timer,
107     //  - if Scheduled, do nothing,
108     //  - if InProgress or PendingCompletion, mark an update as pending, meaning another
109     //    update will be scheduled as soon as the current one is completed.
110
111     UNUSED_PARAM(stateLocker);
112
113     switch (m_state.update) {
114     case UpdateState::Idle:
115         m_state.update = UpdateState::Scheduled;
116         m_updateTimer.startOneShot(0_s);
117         return;
118     case UpdateState::Scheduled:
119         return;
120     case UpdateState::InProgress:
121     case UpdateState::PendingCompletion:
122         m_state.pendingUpdate = true;
123         return;
124     }
125 }
126
127 void CompositingRunLoop::stopUpdates()
128 {
129     // Stop everything.
130
131     LockHolder locker(m_state.lock);
132     m_updateTimer.stop();
133     m_state.composition = CompositionState::Idle;
134     m_state.update = UpdateState::Idle;
135     m_state.pendingUpdate = false;
136 }
137
138 void CompositingRunLoop::compositionCompleted(LockHolder& stateLocker)
139 {
140     // Composition has been signaled as completed, pushing the state into Idle.
141     // Depending on the state of the scene update:
142     //  - if Idle, Scheduled or InProgress, do nothing,
143     //  - if PendingCompletion, schedule a new update in case a pending update was marked,
144     //    or push the scene update state into Idle otherwise.
145
146     UNUSED_PARAM(stateLocker);
147
148     m_state.composition = CompositionState::Idle;
149
150     switch (m_state.update) {
151     case UpdateState::Idle:
152     case UpdateState::Scheduled:
153     case UpdateState::InProgress:
154         return;
155     case UpdateState::PendingCompletion:
156         if (m_state.pendingUpdate) {
157             m_state.pendingUpdate = false;
158             m_state.update = UpdateState::Scheduled;
159             m_updateTimer.startOneShot(0_s);
160             return;
161         }
162
163         m_state.update = UpdateState::Idle;
164         return;
165     }
166 }
167
168 void CompositingRunLoop::updateCompleted(LockHolder& stateLocker)
169 {
170     // Scene update has been signaled as completed. Depending on the state:
171     //  - if Idle, Scheduled or InProgress, do nothing,
172     //  - if InProgress, push the state into PendingCompletion if the composition state is
173     //    InProgress, otherwise schedule a new update in case a pending update was marked,
174     //    otherwise push the scene update state into Idle.
175
176     UNUSED_PARAM(stateLocker);
177
178     switch (m_state.update) {
179     case UpdateState::Idle:
180     case UpdateState::Scheduled:
181         return;
182     case UpdateState::InProgress:
183         if (m_state.composition == CompositionState::InProgress) {
184             m_state.update = UpdateState::PendingCompletion;
185             return;
186         }
187
188         if (m_state.pendingUpdate) {
189             m_state.pendingUpdate = false;
190             m_state.update = UpdateState::Scheduled;
191             m_updateTimer.startOneShot(0_s);
192             return;
193         }
194
195         m_state.update = UpdateState::Idle;
196         return;
197     case UpdateState::PendingCompletion:
198         return;
199     }
200 }
201
202 void CompositingRunLoop::updateTimerFired()
203 {
204     {
205         // Both composition and scene update are now in progress.
206         LockHolder locker(m_state.lock);
207         m_state.composition = CompositionState::InProgress;
208         m_state.update = UpdateState::InProgress;
209     }
210     m_updateFunction();
211 }
212
213 } // namespace WebKit
214
215 #endif // USE(COORDINATED_GRAPHICS)