33228bee035187be2c28b56d8bf7da08b55907d1
[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 "DrawingAreaImpl.h"
33 #include "TextureMapperGL.h"
34 #include "WebPage.h"
35 #include "WebProcess.h"
36
37 #if USE(OPENGL_ES_2)
38 #include <GLES2/gl2.h>
39 #else
40 #include <GL/gl.h>
41 #endif
42
43 #include <WebCore/FrameView.h>
44 #include <WebCore/GLContext.h>
45 #include <WebCore/GraphicsLayerTextureMapper.h>
46 #include <WebCore/MainFrame.h>
47 #include <WebCore/Page.h>
48 #include <WebCore/Settings.h>
49 #include <wtf/CurrentTime.h>
50
51 #include <gdk/gdk.h>
52 #if defined(GDK_WINDOWING_X11)
53 #define Region XRegion
54 #define Font XFont
55 #define Cursor XCursor
56 #define Screen XScreen
57 #include <gdk/gdkx.h>
58 #endif
59
60 using namespace WebCore;
61
62 namespace WebKit {
63
64 PassRefPtr<LayerTreeHostGtk> LayerTreeHostGtk::create(WebPage* webPage)
65 {
66     RefPtr<LayerTreeHostGtk> host = adoptRef(new LayerTreeHostGtk(webPage));
67     host->initialize();
68     return host.release();
69 }
70
71 LayerTreeHostGtk::LayerTreeHostGtk(WebPage* webPage)
72     : LayerTreeHost(webPage)
73     , m_isValid(true)
74     , m_notifyAfterScheduledLayerFlush(false)
75     , m_lastFlushTime(0)
76     , m_layerFlushSchedulingEnabled(true)
77     , m_layerFlushTimerCallbackId(0)
78 {
79 }
80
81 GLContext* LayerTreeHostGtk::glContext()
82 {
83     if (m_context)
84         return m_context.get();
85
86     uint64_t windowHandle = m_webPage->nativeWindowHandle();
87     if (!windowHandle)
88         return 0;
89
90     m_context = GLContext::createContextForWindow(windowHandle, GLContext::sharingContext());
91     return m_context.get();
92 }
93
94 void LayerTreeHostGtk::initialize()
95 {
96     m_rootLayer = GraphicsLayer::create(graphicsLayerFactory(), this);
97     m_rootLayer->setDrawsContent(false);
98     m_rootLayer->setSize(m_webPage->size());
99
100     // The non-composited contents are a child of the root layer.
101     m_nonCompositedContentLayer = GraphicsLayer::create(graphicsLayerFactory(), this);
102     m_nonCompositedContentLayer->setDrawsContent(true);
103     m_nonCompositedContentLayer->setContentsOpaque(m_webPage->drawsBackground() && !m_webPage->drawsTransparentBackground());
104     m_nonCompositedContentLayer->setSize(m_webPage->size());
105     if (m_webPage->corePage()->settings().acceleratedDrawingEnabled())
106         m_nonCompositedContentLayer->setAcceleratesDrawing(true);
107
108 #ifndef NDEBUG
109     m_rootLayer->setName("LayerTreeHost root layer");
110     m_nonCompositedContentLayer->setName("LayerTreeHost non-composited content");
111 #endif
112
113     m_rootLayer->addChild(m_nonCompositedContentLayer.get());
114     m_nonCompositedContentLayer->setNeedsDisplay();
115
116     m_layerTreeContext.windowHandle = m_webPage->nativeWindowHandle();
117
118     GLContext* context = glContext();
119     if (!context)
120         return;
121
122     // The creation of the TextureMapper needs an active OpenGL context.
123     context->makeContextCurrent();
124
125     m_textureMapper = TextureMapperGL::create();
126     static_cast<TextureMapperGL*>(m_textureMapper.get())->setEnableEdgeDistanceAntialiasing(true);
127     toTextureMapperLayer(m_rootLayer.get())->setTextureMapper(m_textureMapper.get());
128
129     if (m_webPage->hasPageOverlay()) {
130         PageOverlayList& pageOverlays = m_webPage->pageOverlays();
131         PageOverlayList::iterator end = pageOverlays.end();
132         for (PageOverlayList::iterator it = pageOverlays.begin(); it != end; ++it)
133             createPageOverlayLayer(it->get());
134     }
135
136     scheduleLayerFlush();
137 }
138
139 LayerTreeHostGtk::~LayerTreeHostGtk()
140 {
141     ASSERT(!m_isValid);
142     ASSERT(!m_rootLayer);
143     cancelPendingLayerFlush();
144 }
145
146 const LayerTreeContext& LayerTreeHostGtk::layerTreeContext()
147 {
148     return m_layerTreeContext;
149 }
150
151 void LayerTreeHostGtk::setShouldNotifyAfterNextScheduledLayerFlush(bool notifyAfterScheduledLayerFlush)
152 {
153     m_notifyAfterScheduledLayerFlush = notifyAfterScheduledLayerFlush;
154 }
155
156 void LayerTreeHostGtk::setRootCompositingLayer(GraphicsLayer* graphicsLayer)
157 {
158     m_nonCompositedContentLayer->removeAllChildren();
159
160     // Add the accelerated layer tree hierarchy.
161     if (graphicsLayer)
162         m_nonCompositedContentLayer->addChild(graphicsLayer);
163
164     scheduleLayerFlush();
165 }
166
167 void LayerTreeHostGtk::invalidate()
168 {
169     ASSERT(m_isValid);
170
171     cancelPendingLayerFlush();
172     m_rootLayer = nullptr;
173     m_nonCompositedContentLayer = nullptr;
174     m_textureMapper = nullptr;
175
176     m_context = nullptr;
177     m_isValid = false;
178 }
179
180 void LayerTreeHostGtk::setNonCompositedContentsNeedDisplay()
181 {
182     m_nonCompositedContentLayer->setNeedsDisplay();
183
184     PageOverlayLayerMap::iterator end = m_pageOverlayLayers.end();
185     for (PageOverlayLayerMap::iterator it = m_pageOverlayLayers.begin(); it != end; ++it)
186         it->value->setNeedsDisplay();
187
188     scheduleLayerFlush();
189 }
190
191 void LayerTreeHostGtk::setNonCompositedContentsNeedDisplayInRect(const IntRect& rect)
192 {
193     m_nonCompositedContentLayer->setNeedsDisplayInRect(rect);
194
195     PageOverlayLayerMap::iterator end = m_pageOverlayLayers.end();
196     for (PageOverlayLayerMap::iterator it = m_pageOverlayLayers.begin(); it != end; ++it)
197         it->value->setNeedsDisplayInRect(rect);
198
199     scheduleLayerFlush();
200 }
201
202 void LayerTreeHostGtk::scrollNonCompositedContents(const IntRect& scrollRect)
203 {
204     setNonCompositedContentsNeedDisplayInRect(scrollRect);
205 }
206
207 void LayerTreeHostGtk::sizeDidChange(const IntSize& newSize)
208 {
209     if (m_rootLayer->size() == newSize)
210         return;
211     m_rootLayer->setSize(newSize);
212
213     // If the newSize exposes new areas of the non-composited content a setNeedsDisplay is needed
214     // for those newly exposed areas.
215     FloatSize oldSize = m_nonCompositedContentLayer->size();
216     m_nonCompositedContentLayer->setSize(newSize);
217
218     if (newSize.width() > oldSize.width()) {
219         float height = std::min(static_cast<float>(newSize.height()), oldSize.height());
220         m_nonCompositedContentLayer->setNeedsDisplayInRect(FloatRect(oldSize.width(), 0, newSize.width() - oldSize.width(), height));
221     }
222
223     if (newSize.height() > oldSize.height())
224         m_nonCompositedContentLayer->setNeedsDisplayInRect(FloatRect(0, oldSize.height(), newSize.width(), newSize.height() - oldSize.height()));
225     m_nonCompositedContentLayer->setNeedsDisplay();
226
227     PageOverlayLayerMap::iterator end = m_pageOverlayLayers.end();
228     for (PageOverlayLayerMap::iterator it = m_pageOverlayLayers.begin(); it != end; ++it)
229         it->value->setSize(newSize);
230
231     compositeLayersToContext(ForResize);
232 }
233
234 void LayerTreeHostGtk::deviceOrPageScaleFactorChanged()
235 {
236     // Other layers learn of the scale factor change via WebPage::setDeviceScaleFactor.
237     m_nonCompositedContentLayer->deviceOrPageScaleFactorChanged();
238 }
239
240 void LayerTreeHostGtk::forceRepaint()
241 {
242     scheduleLayerFlush();
243 }
244
245 void LayerTreeHostGtk::didInstallPageOverlay(PageOverlay* pageOverlay)
246 {
247     createPageOverlayLayer(pageOverlay);
248     scheduleLayerFlush();
249 }
250
251 void LayerTreeHostGtk::didUninstallPageOverlay(PageOverlay* pageOverlay)
252 {
253     destroyPageOverlayLayer(pageOverlay);
254     scheduleLayerFlush();
255 }
256
257 void LayerTreeHostGtk::setPageOverlayNeedsDisplay(PageOverlay* pageOverlay, const IntRect& rect)
258 {
259     GraphicsLayer* layer = m_pageOverlayLayers.get(pageOverlay);
260     if (!layer)
261         return;
262
263     layer->setNeedsDisplayInRect(rect);
264     scheduleLayerFlush();
265 }
266
267 void LayerTreeHostGtk::notifyAnimationStarted(const WebCore::GraphicsLayer*, double time)
268 {
269 }
270
271 void LayerTreeHostGtk::notifyFlushRequired(const WebCore::GraphicsLayer*)
272 {
273 }
274
275 void LayerTreeHostGtk::paintContents(const GraphicsLayer* graphicsLayer, GraphicsContext& graphicsContext, GraphicsLayerPaintingPhase, const IntRect& clipRect)
276 {
277     if (graphicsLayer == m_nonCompositedContentLayer.get()) {
278         m_webPage->drawRect(graphicsContext, clipRect);
279         return;
280     }
281
282     for (auto& pageOverlayLayer : m_pageOverlayLayers) {
283         if (pageOverlayLayer.value.get() == graphicsLayer) {
284             m_webPage->drawPageOverlay(pageOverlayLayer.key, graphicsContext, clipRect);
285             break;
286         }
287     }
288 }
289
290 gboolean LayerTreeHostGtk::layerFlushTimerFiredCallback(LayerTreeHostGtk* layerTreeHost)
291 {
292     layerTreeHost->layerFlushTimerFired();
293     return FALSE;
294 }
295
296 void LayerTreeHostGtk::layerFlushTimerFired()
297 {
298     ASSERT(m_layerFlushTimerCallbackId);
299     m_layerFlushTimerCallbackId = 0;
300
301     flushAndRenderLayers();
302
303     if (toTextureMapperLayer(m_rootLayer.get())->descendantsOrSelfHaveRunningAnimations() && !m_layerFlushTimerCallbackId) {
304         const double targetFPS = 60;
305         double nextFlush = std::max((1 / targetFPS) - (currentTime() - m_lastFlushTime), 0.0);
306         m_layerFlushTimerCallbackId = g_timeout_add_full(GDK_PRIORITY_EVENTS, nextFlush * 1000.0, reinterpret_cast<GSourceFunc>(layerFlushTimerFiredCallback), this, 0);
307     }
308 }
309
310 bool LayerTreeHostGtk::flushPendingLayerChanges()
311 {
312     m_rootLayer->flushCompositingStateForThisLayerOnly();
313     m_nonCompositedContentLayer->flushCompositingStateForThisLayerOnly();
314
315     PageOverlayLayerMap::iterator end = m_pageOverlayLayers.end();
316     for (PageOverlayLayerMap::iterator it = m_pageOverlayLayers.begin(); it != end; ++it)
317         it->value->flushCompositingStateForThisLayerOnly();
318
319     return m_webPage->corePage()->mainFrame().view()->flushCompositingStateIncludingSubframes();
320 }
321
322 void LayerTreeHostGtk::compositeLayersToContext(CompositePurpose purpose)
323 {
324     GLContext* context = glContext();
325     if (!context || !context->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
334     if (purpose == ForResize) {
335         glClearColor(1, 1, 1, 0);
336         glClear(GL_COLOR_BUFFER_BIT);
337     }
338
339     m_textureMapper->beginPainting();
340     toTextureMapperLayer(m_rootLayer.get())->paint();
341     m_textureMapper->endPainting();
342
343     context->swapBuffers();
344 }
345
346 void LayerTreeHostGtk::flushAndRenderLayers()
347 {
348     {
349         RefPtr<LayerTreeHostGtk> protect(this);
350         m_webPage->layoutIfNeeded();
351
352         if (!m_isValid)
353             return;
354     }
355
356     GLContext* context = glContext();
357     if (!context || !context->makeContextCurrent())
358         return;
359
360     m_lastFlushTime = currentTime();
361     if (!flushPendingLayerChanges())
362         return;
363
364     // Our model is very simple. We always composite and render the tree immediately after updating it.
365     compositeLayersToContext();
366
367     if (m_notifyAfterScheduledLayerFlush) {
368         // Let the drawing area know that we've done a flush of the layer changes.
369         static_cast<DrawingAreaImpl*>(m_webPage->drawingArea())->layerHostDidFlushLayers();
370         m_notifyAfterScheduledLayerFlush = false;
371     }
372 }
373
374 void LayerTreeHostGtk::createPageOverlayLayer(PageOverlay* pageOverlay)
375 {
376     std::unique_ptr<GraphicsLayer> layer = GraphicsLayer::create(graphicsLayerFactory(), this);
377 #ifndef NDEBUG
378     layer->setName("LayerTreeHost page overlay content");
379 #endif
380
381     layer->setAcceleratesDrawing(m_webPage->corePage()->settings().acceleratedDrawingEnabled());
382     layer->setDrawsContent(true);
383     layer->setSize(m_webPage->size());
384     layer->setShowDebugBorder(m_webPage->corePage()->settings().showDebugBorders());
385     layer->setShowRepaintCounter(m_webPage->corePage()->settings().showRepaintCounter());
386
387     m_rootLayer->addChild(layer.get());
388     m_pageOverlayLayers.add(pageOverlay, std::move(layer));
389 }
390
391 void LayerTreeHostGtk::destroyPageOverlayLayer(PageOverlay* pageOverlay)
392 {
393     std::unique_ptr<GraphicsLayer> layer = m_pageOverlayLayers.take(pageOverlay);
394     ASSERT(layer);
395
396     layer->removeFromParent();
397 }
398
399 void LayerTreeHostGtk::scheduleLayerFlush()
400 {
401     if (!m_layerFlushSchedulingEnabled)
402         return;
403
404     // We use a GLib timer because otherwise GTK+ event handling during dragging can starve WebCore timers, which have a lower priority.
405     if (!m_layerFlushTimerCallbackId)
406         m_layerFlushTimerCallbackId = g_timeout_add_full(GDK_PRIORITY_EVENTS, 0, reinterpret_cast<GSourceFunc>(layerFlushTimerFiredCallback), this, 0);
407 }
408
409 void LayerTreeHostGtk::setLayerFlushSchedulingEnabled(bool layerFlushingEnabled)
410 {
411     if (m_layerFlushSchedulingEnabled == layerFlushingEnabled)
412         return;
413
414     m_layerFlushSchedulingEnabled = layerFlushingEnabled;
415
416     if (m_layerFlushSchedulingEnabled) {
417         scheduleLayerFlush();
418         return;
419     }
420
421     cancelPendingLayerFlush();
422 }
423
424 void LayerTreeHostGtk::pageBackgroundTransparencyChanged()
425 {
426     m_nonCompositedContentLayer->setContentsOpaque(m_webPage->drawsBackground() && !m_webPage->drawsTransparentBackground());
427 }
428
429 void LayerTreeHostGtk::cancelPendingLayerFlush()
430 {
431     if (!m_layerFlushTimerCallbackId)
432         return;
433
434     g_source_remove(m_layerFlushTimerCallbackId);
435     m_layerFlushTimerCallbackId = 0;
436 }
437
438 } // namespace WebKit
439
440 #endif