[GTK] LayerTreeHostGtk is creating a software scene graph
[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 = TextureMapper::create(TextureMapper::OpenGLMode);
126     static_cast<TextureMapperGL*>(m_textureMapper.get())->setEnableEdgeDistanceAntialiasing(true);
127     toTextureMapperLayer(m_rootLayer.get())->setTextureMapper(m_textureMapper.get());
128
129     // FIXME: Cretae page olverlay layers. https://bugs.webkit.org/show_bug.cgi?id=131433.
130
131     scheduleLayerFlush();
132 }
133
134 LayerTreeHostGtk::~LayerTreeHostGtk()
135 {
136     ASSERT(!m_isValid);
137     ASSERT(!m_rootLayer);
138     cancelPendingLayerFlush();
139 }
140
141 const LayerTreeContext& LayerTreeHostGtk::layerTreeContext()
142 {
143     return m_layerTreeContext;
144 }
145
146 void LayerTreeHostGtk::setShouldNotifyAfterNextScheduledLayerFlush(bool notifyAfterScheduledLayerFlush)
147 {
148     m_notifyAfterScheduledLayerFlush = notifyAfterScheduledLayerFlush;
149 }
150
151 void LayerTreeHostGtk::setRootCompositingLayer(GraphicsLayer* graphicsLayer)
152 {
153     m_nonCompositedContentLayer->removeAllChildren();
154
155     // Add the accelerated layer tree hierarchy.
156     if (graphicsLayer)
157         m_nonCompositedContentLayer->addChild(graphicsLayer);
158
159     scheduleLayerFlush();
160 }
161
162 void LayerTreeHostGtk::invalidate()
163 {
164     ASSERT(m_isValid);
165
166     // This can trigger destruction of GL objects so let's make sure that
167     // we have the right active context
168     if (m_context)
169         m_context->makeContextCurrent();
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 FloatRect& clipRect)
276 {
277     if (graphicsLayer == m_nonCompositedContentLayer.get()) {
278         m_webPage->drawRect(graphicsContext, enclosingIntRect(clipRect));
279         return;
280     }
281
282     // FIXME: Draw page overlays. https://bugs.webkit.org/show_bug.cgi?id=131433.
283 }
284
285 gboolean LayerTreeHostGtk::layerFlushTimerFiredCallback(LayerTreeHostGtk* layerTreeHost)
286 {
287     layerTreeHost->layerFlushTimerFired();
288     return FALSE;
289 }
290
291 void LayerTreeHostGtk::layerFlushTimerFired()
292 {
293     ASSERT(m_layerFlushTimerCallbackId);
294     m_layerFlushTimerCallbackId = 0;
295
296     flushAndRenderLayers();
297
298     if (toTextureMapperLayer(m_rootLayer.get())->descendantsOrSelfHaveRunningAnimations() && !m_layerFlushTimerCallbackId) {
299         const double targetFPS = 60;
300         double nextFlush = std::max((1 / targetFPS) - (currentTime() - m_lastFlushTime), 0.0);
301         m_layerFlushTimerCallbackId = g_timeout_add_full(GDK_PRIORITY_EVENTS, nextFlush * 1000.0, reinterpret_cast<GSourceFunc>(layerFlushTimerFiredCallback), this, 0);
302         g_source_set_name_by_id(m_layerFlushTimerCallbackId, "[WebKit] layerFlushTimerFiredCallback");
303     }
304 }
305
306 bool LayerTreeHostGtk::flushPendingLayerChanges()
307 {
308     m_rootLayer->flushCompositingStateForThisLayerOnly();
309     m_nonCompositedContentLayer->flushCompositingStateForThisLayerOnly();
310
311     PageOverlayLayerMap::iterator end = m_pageOverlayLayers.end();
312     for (PageOverlayLayerMap::iterator it = m_pageOverlayLayers.begin(); it != end; ++it)
313         it->value->flushCompositingStateForThisLayerOnly();
314
315     return m_webPage->corePage()->mainFrame().view()->flushCompositingStateIncludingSubframes();
316 }
317
318 void LayerTreeHostGtk::compositeLayersToContext(CompositePurpose purpose)
319 {
320     GLContext* context = glContext();
321     if (!context || !context->makeContextCurrent())
322         return;
323
324     // The window size may be out of sync with the page size at this point, and getting
325     // the viewport parameters incorrect, means that the content will be misplaced. Thus
326     // we set the viewport parameters directly from the window size.
327     IntSize contextSize = m_context->defaultFrameBufferSize();
328     glViewport(0, 0, contextSize.width(), contextSize.height());
329
330     if (purpose == ForResize) {
331         glClearColor(1, 1, 1, 0);
332         glClear(GL_COLOR_BUFFER_BIT);
333     }
334
335     m_textureMapper->beginPainting();
336     toTextureMapperLayer(m_rootLayer.get())->paint();
337     m_textureMapper->endPainting();
338
339     context->swapBuffers();
340 }
341
342 void LayerTreeHostGtk::flushAndRenderLayers()
343 {
344     {
345         RefPtr<LayerTreeHostGtk> protect(this);
346         m_webPage->layoutIfNeeded();
347
348         if (!m_isValid)
349             return;
350     }
351
352     GLContext* context = glContext();
353     if (!context || !context->makeContextCurrent())
354         return;
355
356     m_lastFlushTime = currentTime();
357     if (!flushPendingLayerChanges())
358         return;
359
360     // Our model is very simple. We always composite and render the tree immediately after updating it.
361     compositeLayersToContext();
362
363     if (m_notifyAfterScheduledLayerFlush) {
364         // Let the drawing area know that we've done a flush of the layer changes.
365         static_cast<DrawingAreaImpl*>(m_webPage->drawingArea())->layerHostDidFlushLayers();
366         m_notifyAfterScheduledLayerFlush = false;
367     }
368 }
369
370 void LayerTreeHostGtk::createPageOverlayLayer(PageOverlay* pageOverlay)
371 {
372     std::unique_ptr<GraphicsLayer> layer = GraphicsLayer::create(graphicsLayerFactory(), this);
373 #ifndef NDEBUG
374     layer->setName("LayerTreeHost page overlay content");
375 #endif
376
377     layer->setAcceleratesDrawing(m_webPage->corePage()->settings().acceleratedDrawingEnabled());
378     layer->setDrawsContent(true);
379     layer->setSize(m_webPage->size());
380     layer->setShowDebugBorder(m_webPage->corePage()->settings().showDebugBorders());
381     layer->setShowRepaintCounter(m_webPage->corePage()->settings().showRepaintCounter());
382
383     m_rootLayer->addChild(layer.get());
384     m_pageOverlayLayers.add(pageOverlay, std::move(layer));
385 }
386
387 void LayerTreeHostGtk::destroyPageOverlayLayer(PageOverlay* pageOverlay)
388 {
389     std::unique_ptr<GraphicsLayer> layer = m_pageOverlayLayers.take(pageOverlay);
390     ASSERT(layer);
391
392     layer->removeFromParent();
393 }
394
395 void LayerTreeHostGtk::scheduleLayerFlush()
396 {
397     if (!m_layerFlushSchedulingEnabled)
398         return;
399
400     // We use a GLib timer because otherwise GTK+ event handling during dragging can starve WebCore timers, which have a lower priority.
401     if (!m_layerFlushTimerCallbackId) {
402         m_layerFlushTimerCallbackId = g_timeout_add_full(GDK_PRIORITY_EVENTS, 0, reinterpret_cast<GSourceFunc>(layerFlushTimerFiredCallback), this, 0);
403         g_source_set_name_by_id(m_layerFlushTimerCallbackId, "[WebKit] layerFlushTimerFiredCallback");
404     }
405 }
406
407 void LayerTreeHostGtk::setLayerFlushSchedulingEnabled(bool layerFlushingEnabled)
408 {
409     if (m_layerFlushSchedulingEnabled == layerFlushingEnabled)
410         return;
411
412     m_layerFlushSchedulingEnabled = layerFlushingEnabled;
413
414     if (m_layerFlushSchedulingEnabled) {
415         scheduleLayerFlush();
416         return;
417     }
418
419     cancelPendingLayerFlush();
420 }
421
422 void LayerTreeHostGtk::pageBackgroundTransparencyChanged()
423 {
424     m_nonCompositedContentLayer->setContentsOpaque(m_webPage->drawsBackground() && !m_webPage->drawsTransparentBackground());
425 }
426
427 void LayerTreeHostGtk::cancelPendingLayerFlush()
428 {
429     if (!m_layerFlushTimerCallbackId)
430         return;
431
432     g_source_remove(m_layerFlushTimerCallbackId);
433     m_layerFlushTimerCallbackId = 0;
434 }
435
436 } // namespace WebKit
437
438 #endif