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 "ThreadedCompositor.h" |
28 | |
29 | #if USE(COORDINATED_GRAPHICS) |
30 | |
31 | #include "CompositingRunLoop.h" |
32 | #include "ThreadedDisplayRefreshMonitor.h" |
33 | #include <WebCore/PlatformDisplay.h> |
34 | #include <WebCore/TransformationMatrix.h> |
35 | #include <wtf/SetForScope.h> |
36 | |
37 | #if USE(LIBEPOXY) |
38 | #include <epoxy/gl.h> |
39 | #elif USE(OPENGL_ES) |
40 | #include <GLES2/gl2.h> |
41 | #else |
42 | #include <GL/gl.h> |
43 | #endif |
44 | |
45 | namespace WebKit { |
46 | using namespace WebCore; |
47 | |
48 | Ref<ThreadedCompositor> ThreadedCompositor::create(Client& client, ThreadedDisplayRefreshMonitor::Client& displayRefreshMonitorClient, PlatformDisplayID displayID, const IntSize& viewportSize, float scaleFactor, ShouldDoFrameSync doFrameSync, TextureMapper::PaintFlags paintFlags) |
49 | { |
50 | return adoptRef(*new ThreadedCompositor(client, displayRefreshMonitorClient, displayID, viewportSize, scaleFactor, doFrameSync, paintFlags)); |
51 | } |
52 | |
53 | ThreadedCompositor::ThreadedCompositor(Client& client, ThreadedDisplayRefreshMonitor::Client& displayRefreshMonitorClient, PlatformDisplayID displayID, const IntSize& viewportSize, float scaleFactor, ShouldDoFrameSync doFrameSync, TextureMapper::PaintFlags paintFlags) |
54 | : m_client(client) |
55 | , m_doFrameSync(doFrameSync) |
56 | , m_paintFlags(paintFlags) |
57 | , m_compositingRunLoop(std::make_unique<CompositingRunLoop>([this] { renderLayerTree(); })) |
58 | #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) |
59 | , m_displayRefreshMonitor(ThreadedDisplayRefreshMonitor::create(displayID, displayRefreshMonitorClient)) |
60 | #endif |
61 | { |
62 | { |
63 | // Locking isn't really necessary here, but it's done for consistency. |
64 | LockHolder locker(m_attributes.lock); |
65 | m_attributes.viewportSize = viewportSize; |
66 | m_attributes.scaleFactor = scaleFactor; |
67 | m_attributes.needsResize = !viewportSize.isEmpty(); |
68 | } |
69 | |
70 | m_compositingRunLoop->performTaskSync([this, protectedThis = makeRef(*this)] { |
71 | m_scene = adoptRef(new CoordinatedGraphicsScene(this)); |
72 | m_nativeSurfaceHandle = m_client.nativeSurfaceHandleForCompositing(); |
73 | |
74 | m_scene->setActive(!!m_nativeSurfaceHandle); |
75 | if (m_nativeSurfaceHandle) |
76 | createGLContext(); |
77 | }); |
78 | } |
79 | |
80 | ThreadedCompositor::~ThreadedCompositor() |
81 | { |
82 | } |
83 | |
84 | void ThreadedCompositor::createGLContext() |
85 | { |
86 | ASSERT(!RunLoop::isMain()); |
87 | |
88 | ASSERT(m_nativeSurfaceHandle); |
89 | |
90 | m_context = GLContext::createContextForWindow(reinterpret_cast<GLNativeWindowType>(m_nativeSurfaceHandle), &PlatformDisplay::sharedDisplayForCompositing()); |
91 | if (!m_context) |
92 | return; |
93 | |
94 | if (!m_context->makeContextCurrent()) |
95 | return; |
96 | |
97 | if (m_doFrameSync == ShouldDoFrameSync::No) |
98 | m_context->swapInterval(0); |
99 | } |
100 | |
101 | void ThreadedCompositor::invalidate() |
102 | { |
103 | m_scene->detach(); |
104 | m_compositingRunLoop->stopUpdates(); |
105 | #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) |
106 | m_displayRefreshMonitor->invalidate(); |
107 | #endif |
108 | m_compositingRunLoop->performTaskSync([this, protectedThis = makeRef(*this)] { |
109 | if (!m_context || !m_context->makeContextCurrent()) |
110 | return; |
111 | m_scene->purgeGLResources(); |
112 | m_context = nullptr; |
113 | m_client.didDestroyGLContext(); |
114 | m_scene = nullptr; |
115 | }); |
116 | m_compositingRunLoop = nullptr; |
117 | } |
118 | |
119 | void ThreadedCompositor::suspend() |
120 | { |
121 | if (++m_suspendedCount > 1) |
122 | return; |
123 | |
124 | m_compositingRunLoop->suspend(); |
125 | m_compositingRunLoop->performTaskSync([this, protectedThis = makeRef(*this)] { |
126 | m_scene->setActive(false); |
127 | }); |
128 | } |
129 | |
130 | void ThreadedCompositor::resume() |
131 | { |
132 | ASSERT(m_suspendedCount > 0); |
133 | if (--m_suspendedCount > 0) |
134 | return; |
135 | |
136 | m_compositingRunLoop->performTaskSync([this, protectedThis = makeRef(*this)] { |
137 | m_scene->setActive(true); |
138 | }); |
139 | m_compositingRunLoop->resume(); |
140 | } |
141 | |
142 | void ThreadedCompositor::setNativeSurfaceHandleForCompositing(uint64_t handle) |
143 | { |
144 | m_compositingRunLoop->stopUpdates(); |
145 | m_compositingRunLoop->performTaskSync([this, protectedThis = makeRef(*this), handle] { |
146 | // A new native handle can't be set without destroying the previous one first if any. |
147 | ASSERT(!!handle ^ !!m_nativeSurfaceHandle); |
148 | m_nativeSurfaceHandle = handle; |
149 | |
150 | m_scene->setActive(!!m_nativeSurfaceHandle); |
151 | if (m_nativeSurfaceHandle) |
152 | createGLContext(); |
153 | else |
154 | m_context = nullptr; |
155 | }); |
156 | } |
157 | |
158 | void ThreadedCompositor::setScaleFactor(float scale) |
159 | { |
160 | LockHolder locker(m_attributes.lock); |
161 | m_attributes.scaleFactor = scale; |
162 | m_compositingRunLoop->scheduleUpdate(); |
163 | } |
164 | |
165 | void ThreadedCompositor::setScrollPosition(const IntPoint& scrollPosition, float scale) |
166 | { |
167 | LockHolder locker(m_attributes.lock); |
168 | m_attributes.scrollPosition = scrollPosition; |
169 | m_attributes.scaleFactor = scale; |
170 | m_compositingRunLoop->scheduleUpdate(); |
171 | } |
172 | |
173 | void ThreadedCompositor::setViewportSize(const IntSize& viewportSize, float scale) |
174 | { |
175 | LockHolder locker(m_attributes.lock); |
176 | m_attributes.viewportSize = viewportSize; |
177 | m_attributes.scaleFactor = scale; |
178 | m_attributes.needsResize = true; |
179 | m_compositingRunLoop->scheduleUpdate(); |
180 | } |
181 | |
182 | void ThreadedCompositor::updateViewport() |
183 | { |
184 | m_compositingRunLoop->scheduleUpdate(); |
185 | } |
186 | |
187 | void ThreadedCompositor::forceRepaint() |
188 | { |
189 | // FIXME: Enable this for WPE once it's possible to do these forced updates |
190 | // in a way that doesn't starve out the underlying graphics buffers. |
191 | #if PLATFORM(GTK) |
192 | m_compositingRunLoop->performTaskSync([this, protectedThis = makeRef(*this)] { |
193 | SetForScope<bool> change(m_inForceRepaint, true); |
194 | renderLayerTree(); |
195 | }); |
196 | #endif |
197 | } |
198 | |
199 | void ThreadedCompositor::renderLayerTree() |
200 | { |
201 | if (!m_scene || !m_scene->isActive()) |
202 | return; |
203 | |
204 | if (!m_context || !m_context->makeContextCurrent()) |
205 | return; |
206 | |
207 | m_client.willRenderFrame(); |
208 | |
209 | // Retrieve the scene attributes in a thread-safe manner. |
210 | WebCore::IntSize viewportSize; |
211 | WebCore::IntPoint scrollPosition; |
212 | float scaleFactor; |
213 | bool needsResize; |
214 | |
215 | Vector<WebCore::CoordinatedGraphicsState> states; |
216 | |
217 | { |
218 | LockHolder locker(m_attributes.lock); |
219 | viewportSize = m_attributes.viewportSize; |
220 | scrollPosition = m_attributes.scrollPosition; |
221 | scaleFactor = m_attributes.scaleFactor; |
222 | needsResize = m_attributes.needsResize; |
223 | |
224 | states = WTFMove(m_attributes.states); |
225 | |
226 | if (!states.isEmpty()) { |
227 | // Client has to be notified upon finishing this scene update. |
228 | m_attributes.clientRendersNextFrame = true; |
229 | } |
230 | |
231 | // Reset the needsResize attribute to false. |
232 | m_attributes.needsResize = false; |
233 | } |
234 | |
235 | if (needsResize) { |
236 | m_client.resize(viewportSize); |
237 | glViewport(0, 0, viewportSize.width(), viewportSize.height()); |
238 | } |
239 | |
240 | TransformationMatrix viewportTransform; |
241 | viewportTransform.scale(scaleFactor); |
242 | viewportTransform.translate(-scrollPosition.x(), -scrollPosition.y()); |
243 | |
244 | glClearColor(0, 0, 0, 0); |
245 | glClear(GL_COLOR_BUFFER_BIT); |
246 | |
247 | m_scene->applyStateChanges(states); |
248 | m_scene->paintToCurrentGLContext(viewportTransform, FloatRect { FloatPoint { }, viewportSize }, m_paintFlags); |
249 | |
250 | m_context->swapBuffers(); |
251 | |
252 | if (m_scene->isActive()) |
253 | m_client.didRenderFrame(); |
254 | } |
255 | |
256 | void ThreadedCompositor::sceneUpdateFinished() |
257 | { |
258 | // The composition has finished. Now we have to determine how to manage |
259 | // the scene update completion. |
260 | |
261 | // The DisplayRefreshMonitor will be used to dispatch a callback on the client thread if: |
262 | // - clientRendersNextFrame is true (i.e. client has to be notified about the finished update), or |
263 | // - a DisplayRefreshMonitor callback was requested from the Web engine |
264 | bool shouldDispatchDisplayRefreshCallback { false }; |
265 | |
266 | { |
267 | LockHolder locker(m_attributes.lock); |
268 | shouldDispatchDisplayRefreshCallback = m_attributes.clientRendersNextFrame |
269 | #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) |
270 | || m_displayRefreshMonitor->requiresDisplayRefreshCallback(); |
271 | #else |
272 | ; |
273 | #endif |
274 | } |
275 | |
276 | LockHolder stateLocker(m_compositingRunLoop->stateLock()); |
277 | |
278 | #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) |
279 | // Schedule the DisplayRefreshMonitor callback, if necessary. |
280 | if (shouldDispatchDisplayRefreshCallback) |
281 | m_displayRefreshMonitor->dispatchDisplayRefreshCallback(); |
282 | #endif |
283 | |
284 | // Mark the scene update as completed. |
285 | m_compositingRunLoop->updateCompleted(stateLocker); |
286 | } |
287 | |
288 | void ThreadedCompositor::updateSceneState(const CoordinatedGraphicsState& state) |
289 | { |
290 | LockHolder locker(m_attributes.lock); |
291 | m_attributes.states.append(state); |
292 | m_compositingRunLoop->scheduleUpdate(); |
293 | } |
294 | |
295 | #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) |
296 | RefPtr<WebCore::DisplayRefreshMonitor> ThreadedCompositor::displayRefreshMonitor(PlatformDisplayID) |
297 | { |
298 | return m_displayRefreshMonitor.copyRef(); |
299 | } |
300 | #endif |
301 | |
302 | void ThreadedCompositor::frameComplete() |
303 | { |
304 | ASSERT(!RunLoop::isMain()); |
305 | sceneUpdateFinished(); |
306 | } |
307 | |
308 | } |
309 | #endif // USE(COORDINATED_GRAPHICS) |
310 | |