40bb07d9f71a2c7d8c833ab8a9ccb989e704cda0
[WebKit-https.git] / Source / WebKit2 / WebProcess / WebPage / gtk / LayerTreeHostGtk.cpp
1 /*
2  * Copyright (C) 2011 Apple Inc. All rights reserved.
3  * Copyright (C) 2012 Igalia S.L.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
15  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
18  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
24  * THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include "config.h"
28 #include "LayerTreeHostGtk.h"
29
30 #if USE(TEXTURE_MAPPER_GL)
31
32 #include "AcceleratedSurface.h"
33 #include "DrawingAreaImpl.h"
34 #include "TextureMapperGL.h"
35 #include "WebPage.h"
36 #include "WebProcess.h"
37
38 #if USE(OPENGL_ES_2)
39 #include <GLES2/gl2.h>
40 #else
41 #include <GL/gl.h>
42 #endif
43
44 #include <WebCore/FrameView.h>
45 #include <WebCore/GLContext.h>
46 #include <WebCore/GraphicsLayerTextureMapper.h>
47 #include <WebCore/MainFrame.h>
48 #include <WebCore/Page.h>
49 #include <WebCore/Settings.h>
50 #include <gdk/gdk.h>
51 #include <wtf/CurrentTime.h>
52
53 using namespace WebCore;
54
55 namespace WebKit {
56
57 LayerTreeHostGtk::RenderFrameScheduler::RenderFrameScheduler(std::function<bool()> renderer)
58     : m_renderer(WTFMove(renderer))
59     , m_timer(RunLoop::main(), this, &LayerTreeHostGtk::RenderFrameScheduler::renderFrame)
60 {
61     // We use a RunLoop timer because otherwise GTK+ event handling during dragging can starve WebCore timers, which have a lower priority.
62     // Use a higher priority than WebCore timers.
63     m_timer.setPriority(GDK_PRIORITY_REDRAW - 1);
64 }
65
66 LayerTreeHostGtk::RenderFrameScheduler::~RenderFrameScheduler()
67 {
68 }
69
70 void LayerTreeHostGtk::RenderFrameScheduler::start()
71 {
72     if (m_timer.isActive())
73         return;
74     m_fireTime = 0;
75     nextFrame();
76 }
77
78 void LayerTreeHostGtk::RenderFrameScheduler::stop()
79 {
80     m_timer.stop();
81 }
82
83 static inline bool shouldSkipNextFrameBecauseOfContinousImmediateFlushes(double current, double lastImmediateFlushTime)
84 {
85     // 100ms is about a perceptable delay in UI, so when scheduling layer flushes immediately for more than 100ms,
86     // we skip the next frame to ensure pending timers have a change to be fired.
87     static const double maxDurationOfImmediateFlushes = 0.100;
88     if (!lastImmediateFlushTime)
89         return false;
90     return lastImmediateFlushTime + maxDurationOfImmediateFlushes < current;
91 }
92
93 void LayerTreeHostGtk::RenderFrameScheduler::nextFrame()
94 {
95     static const double targetFramerate = 1 / 60.0;
96     // When rendering layers takes more time than the target delay (0.016), we end up scheduling layer flushes
97     // immediately. Since the layer flush timer has a higher priority than WebCore timers, these are never
98     // fired while we keep scheduling layer flushes immediately.
99     double current = monotonicallyIncreasingTime();
100     double timeToNextFlush = std::max(targetFramerate - (current - m_fireTime), 0.0);
101     if (timeToNextFlush)
102         m_lastImmediateFlushTime = 0;
103     else if (!m_lastImmediateFlushTime)
104         m_lastImmediateFlushTime = current;
105
106     if (shouldSkipNextFrameBecauseOfContinousImmediateFlushes(current, m_lastImmediateFlushTime)) {
107         timeToNextFlush = targetFramerate;
108         m_lastImmediateFlushTime = 0;
109     }
110
111     m_timer.startOneShot(timeToNextFlush);
112 }
113
114 void LayerTreeHostGtk::RenderFrameScheduler::renderFrame()
115 {
116     m_fireTime = monotonicallyIncreasingTime();
117     if (!m_renderer() || m_timer.isActive())
118         return;
119     nextFrame();
120 }
121
122 Ref<LayerTreeHostGtk> LayerTreeHostGtk::create(WebPage& webPage)
123 {
124     return adoptRef(*new LayerTreeHostGtk(webPage));
125 }
126
127 LayerTreeHostGtk::LayerTreeHostGtk(WebPage& webPage)
128     : LayerTreeHost(webPage)
129     , m_surface(AcceleratedSurface::create(webPage))
130     , m_renderFrameScheduler(std::bind(&LayerTreeHostGtk::renderFrame, this))
131 {
132     m_rootLayer = GraphicsLayer::create(graphicsLayerFactory(), *this);
133     m_rootLayer->setDrawsContent(false);
134     m_rootLayer->setSize(m_webPage.size());
135
136     m_scaleMatrix.makeIdentity();
137     m_scaleMatrix.scale(m_webPage.deviceScaleFactor() * m_webPage.pageScaleFactor());
138     downcast<GraphicsLayerTextureMapper>(*m_rootLayer).layer().setAnchorPoint(FloatPoint3D());
139     downcast<GraphicsLayerTextureMapper>(*m_rootLayer).layer().setTransform(m_scaleMatrix);
140
141     // The non-composited contents are a child of the root layer.
142     m_nonCompositedContentLayer = GraphicsLayer::create(graphicsLayerFactory(), *this);
143     m_nonCompositedContentLayer->setDrawsContent(true);
144     m_nonCompositedContentLayer->setContentsOpaque(m_webPage.drawsBackground());
145     m_nonCompositedContentLayer->setSize(m_webPage.size());
146     if (m_webPage.corePage()->settings().acceleratedDrawingEnabled())
147         m_nonCompositedContentLayer->setAcceleratesDrawing(true);
148
149 #ifndef NDEBUG
150     m_rootLayer->setName("LayerTreeHost root layer");
151     m_nonCompositedContentLayer->setName("LayerTreeHost non-composited content");
152 #endif
153
154     m_rootLayer->addChild(m_nonCompositedContentLayer.get());
155     m_nonCompositedContentLayer->setNeedsDisplay();
156
157     if (m_surface) {
158         createTextureMapper();
159         m_layerTreeContext.contextID = m_surface->surfaceID();
160     }
161 }
162
163 bool LayerTreeHostGtk::makeContextCurrent()
164 {
165     uint64_t nativeHandle = m_surface ? m_surface->window() : m_layerTreeContext.contextID;
166     if (!nativeHandle) {
167         m_context = nullptr;
168         return false;
169     }
170
171     if (m_context)
172         return m_context->makeContextCurrent();
173
174     m_context = GLContext::createContextForWindow(reinterpret_cast<GLNativeWindowType>(nativeHandle), &PlatformDisplay::sharedDisplayForCompositing());
175     if (!m_context)
176         return false;
177
178     if (!m_context->makeContextCurrent())
179         return false;
180
181     // Do not do frame sync when rendering offscreen in the web process to ensure that SwapBuffers never blocks.
182     // Rendering to the actual screen will happen later anyway since the UI process schedules a redraw for every update,
183     // the compositor will take care of syncing to vblank.
184     if (m_surface)
185         m_context->swapInterval(0);
186
187     return true;
188 }
189
190 LayerTreeHostGtk::~LayerTreeHostGtk()
191 {
192     ASSERT(!m_rootLayer);
193     cancelPendingLayerFlush();
194 }
195
196 void LayerTreeHostGtk::setRootCompositingLayer(GraphicsLayer* graphicsLayer)
197 {
198     m_nonCompositedContentLayer->removeAllChildren();
199
200     // Add the accelerated layer tree hierarchy.
201     if (graphicsLayer)
202         m_nonCompositedContentLayer->addChild(graphicsLayer);
203
204     scheduleLayerFlush();
205 }
206
207 void LayerTreeHostGtk::invalidate()
208 {
209     // This can trigger destruction of GL objects so let's make sure that
210     // we have the right active context
211     if (m_context)
212         m_context->makeContextCurrent();
213
214     cancelPendingLayerFlush();
215     m_rootLayer = nullptr;
216     m_nonCompositedContentLayer = nullptr;
217     m_textureMapper = nullptr;
218
219     m_context = nullptr;
220     LayerTreeHost::invalidate();
221
222     m_surface = nullptr;
223 }
224
225 void LayerTreeHostGtk::setNonCompositedContentsNeedDisplay()
226 {
227     m_nonCompositedContentLayer->setNeedsDisplay();
228     scheduleLayerFlush();
229 }
230
231 void LayerTreeHostGtk::setNonCompositedContentsNeedDisplayInRect(const IntRect& rect)
232 {
233     m_nonCompositedContentLayer->setNeedsDisplayInRect(rect);
234     scheduleLayerFlush();
235 }
236
237 void LayerTreeHostGtk::scrollNonCompositedContents(const IntRect& scrollRect)
238 {
239     setNonCompositedContentsNeedDisplayInRect(scrollRect);
240 }
241
242 void LayerTreeHostGtk::sizeDidChange(const IntSize& newSize)
243 {
244     if (m_rootLayer->size() == newSize)
245         return;
246     m_rootLayer->setSize(newSize);
247
248     // If the newSize exposes new areas of the non-composited content a setNeedsDisplay is needed
249     // for those newly exposed areas.
250     FloatSize oldSize = m_nonCompositedContentLayer->size();
251     m_nonCompositedContentLayer->setSize(newSize);
252
253     if (newSize.width() > oldSize.width()) {
254         float height = std::min(static_cast<float>(newSize.height()), oldSize.height());
255         m_nonCompositedContentLayer->setNeedsDisplayInRect(FloatRect(oldSize.width(), 0, newSize.width() - oldSize.width(), height));
256     }
257
258     if (newSize.height() > oldSize.height())
259         m_nonCompositedContentLayer->setNeedsDisplayInRect(FloatRect(0, oldSize.height(), newSize.width(), newSize.height() - oldSize.height()));
260     m_nonCompositedContentLayer->setNeedsDisplay();
261
262     if (m_surface && m_surface->resize(newSize))
263         m_layerTreeContext.contextID = m_surface->surfaceID();
264
265     compositeLayersToContext(ForResize);
266 }
267
268 void LayerTreeHostGtk::deviceOrPageScaleFactorChanged()
269 {
270     if (m_surface && m_surface->resize(m_webPage.size()))
271         m_layerTreeContext.contextID = m_surface->surfaceID();
272
273     // Other layers learn of the scale factor change via WebPage::setDeviceScaleFactor.
274     m_nonCompositedContentLayer->deviceOrPageScaleFactorChanged();
275
276     m_scaleMatrix.makeIdentity();
277     m_scaleMatrix.scale(m_webPage.deviceScaleFactor() * m_webPage.pageScaleFactor());
278     downcast<GraphicsLayerTextureMapper>(*m_rootLayer).layer().setTransform(m_scaleMatrix);
279 }
280
281 void LayerTreeHostGtk::forceRepaint()
282 {
283     scheduleLayerFlush();
284 }
285
286 void LayerTreeHostGtk::paintContents(const GraphicsLayer* graphicsLayer, GraphicsContext& graphicsContext, GraphicsLayerPaintingPhase, const FloatRect& clipRect)
287 {
288     if (graphicsLayer == m_nonCompositedContentLayer.get())
289         m_webPage.drawRect(graphicsContext, enclosingIntRect(clipRect));
290 }
291
292 float LayerTreeHostGtk::deviceScaleFactor() const
293 {
294     return m_webPage.deviceScaleFactor();
295 }
296
297 float LayerTreeHostGtk::pageScaleFactor() const
298 {
299     return m_webPage.pageScaleFactor();
300 }
301
302 bool LayerTreeHostGtk::renderFrame()
303 {
304     flushAndRenderLayers();
305     return downcast<GraphicsLayerTextureMapper>(*m_rootLayer).layer().descendantsOrSelfHaveRunningAnimations();
306 }
307
308 bool LayerTreeHostGtk::flushPendingLayerChanges()
309 {
310     m_rootLayer->flushCompositingStateForThisLayerOnly();
311     m_nonCompositedContentLayer->flushCompositingStateForThisLayerOnly();
312
313     if (!m_webPage.corePage()->mainFrame().view()->flushCompositingStateIncludingSubframes())
314         return false;
315
316     if (m_viewOverlayRootLayer)
317         m_viewOverlayRootLayer->flushCompositingState(FloatRect(FloatPoint(), m_rootLayer->size()));
318
319     downcast<GraphicsLayerTextureMapper>(*m_rootLayer).updateBackingStoreIncludingSubLayers();
320     return true;
321 }
322
323 void LayerTreeHostGtk::compositeLayersToContext(CompositePurpose purpose)
324 {
325     if (!makeContextCurrent())
326         return;
327
328     // The window size may be out of sync with the page size at this point, and getting
329     // the viewport parameters incorrect, means that the content will be misplaced. Thus
330     // we set the viewport parameters directly from the window size.
331     IntSize contextSize = m_context->defaultFrameBufferSize();
332     glViewport(0, 0, contextSize.width(), contextSize.height());
333     if (purpose == ForResize || !m_webPage.drawsBackground()) {
334         glClearColor(0, 0, 0, 0);
335         glClear(GL_COLOR_BUFFER_BIT);
336     }
337
338     ASSERT(m_textureMapper);
339
340     TextureMapper::PaintFlags paintFlags = 0;
341
342     if (m_surface && m_surface->shouldPaintMirrored())
343         paintFlags |= TextureMapper::PaintingMirrored;
344
345     m_textureMapper->beginPainting(paintFlags);
346     downcast<GraphicsLayerTextureMapper>(*m_rootLayer).layer().paint();
347     m_textureMapper->endPainting();
348
349     m_context->swapBuffers();
350 }
351
352 void LayerTreeHostGtk::flushAndRenderLayers()
353 {
354     {
355         RefPtr<LayerTreeHostGtk> protect(this);
356         m_webPage.layoutIfNeeded();
357
358         if (!m_isValid)
359             return;
360     }
361
362     if (!makeContextCurrent())
363         return;
364
365     if (!flushPendingLayerChanges())
366         return;
367
368     // Our model is very simple. We always composite and render the tree immediately after updating it.
369     compositeLayersToContext();
370
371     if (m_notifyAfterScheduledLayerFlush) {
372         // Let the drawing area know that we've done a flush of the layer changes.
373         m_webPage.drawingArea()->layerHostDidFlushLayers();
374         m_notifyAfterScheduledLayerFlush = false;
375     }
376 }
377
378 void LayerTreeHostGtk::scheduleLayerFlush()
379 {
380     if (!m_layerFlushSchedulingEnabled || !m_textureMapper)
381         return;
382
383     m_renderFrameScheduler.start();
384 }
385
386 void LayerTreeHostGtk::pageBackgroundTransparencyChanged()
387 {
388     m_nonCompositedContentLayer->setContentsOpaque(m_webPage.drawsBackground());
389 }
390
391 void LayerTreeHostGtk::cancelPendingLayerFlush()
392 {
393     m_renderFrameScheduler.stop();
394 }
395
396 void LayerTreeHostGtk::setViewOverlayRootLayer(GraphicsLayer* viewOverlayRootLayer)
397 {
398     LayerTreeHost::setViewOverlayRootLayer(viewOverlayRootLayer);
399     if (m_viewOverlayRootLayer)
400         m_rootLayer->addChild(m_viewOverlayRootLayer);
401 }
402
403 void LayerTreeHostGtk::createTextureMapper()
404 {
405     // The creation of the TextureMapper needs an active OpenGL context.
406     if (!makeContextCurrent())
407         return;
408
409     ASSERT(m_isValid);
410     ASSERT(!m_textureMapper);
411     m_textureMapper = TextureMapper::create();
412     static_cast<TextureMapperGL*>(m_textureMapper.get())->setEnableEdgeDistanceAntialiasing(true);
413     downcast<GraphicsLayerTextureMapper>(*m_rootLayer).layer().setTextureMapper(m_textureMapper.get());
414 }
415
416 #if PLATFORM(X11) && !USE(REDIRECTED_XCOMPOSITE_WINDOW)
417 void LayerTreeHostGtk::setNativeSurfaceHandleForCompositing(uint64_t handle)
418 {
419     cancelPendingLayerFlush();
420     m_layerTreeContext.contextID = handle;
421
422     createTextureMapper();
423     scheduleLayerFlush();
424 }
425 #endif
426
427 } // namespace WebKit
428
429 #endif