[GTK] Using a native window for the WebView breaks GtkOverlay
[WebKit-https.git] / Source / WebKit / gtk / WebCoreSupport / AcceleratedCompositingContextGL.cpp
1 /*
2  * Copyright (C) 2012 Igalia, S.L.
3  *
4  *  This library is free software; you can redistribute it and/or
5  *  modify it under the terms of the GNU Lesser General Public
6  *  License as published by the Free Software Foundation; either
7  *  version 2 of the License, or (at your option) any later version.
8  *
9  *  This library is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  *  Lesser General Public License for more details.
13  *
14  *  You should have received a copy of the GNU Lesser General Public
15  *  License along with this library; if not, write to the Free Software
16  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17  */
18
19 #include "config.h"
20 #include "AcceleratedCompositingContext.h"
21
22 #if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER_GL)
23
24 #include "CairoUtilities.h"
25 #include "Chrome.h"
26 #include "ChromeClientGtk.h"
27 #include "Frame.h"
28 #include "FrameView.h"
29 #include "PlatformContextCairo.h"
30 #include "Settings.h"
31 #include "TextureMapperGL.h"
32 #include "TextureMapperLayer.h"
33 #include "webkitwebviewprivate.h"
34 #include <GL/gl.h>
35 #include <cairo.h>
36 #include <gdk/gdk.h>
37 #include <gtk/gtk.h>
38
39 const double gFramesPerSecond = 60;
40
41 // There seems to be a delicate balance between the main loop being flooded
42 // with motion events (that force flushes) and starving the main loop of events
43 // with flush callbacks. This delay is entirely empirical.
44 const double gScheduleDelay = (1.0 / (gFramesPerSecond / 3.0));
45
46 using namespace WebCore;
47
48 namespace WebKit {
49
50 AcceleratedCompositingContext::AcceleratedCompositingContext(WebKitWebView* webView)
51     : m_webView(webView)
52     , m_layerFlushTimerCallbackId(0)
53     , m_redirectedWindow(RedirectedXCompositeWindow::create(IntSize(1, 1)))
54     , m_lastFlushTime(0)
55     , m_redrawPendingTime(0)
56     , m_needsExtraFlush(false)
57 {
58 }
59
60 static IntSize getWebViewSize(WebKitWebView* webView)
61 {
62     GtkAllocation allocation;
63     gtk_widget_get_allocation(GTK_WIDGET(webView), &allocation);
64     return IntSize(allocation.width, allocation.height);
65 }
66
67 void AcceleratedCompositingContext::initialize()
68 {
69     if (m_rootLayer)
70         return;
71
72     m_rootLayer = GraphicsLayer::create(this);
73     m_rootLayer->setDrawsContent(false);
74
75     IntSize pageSize = getWebViewSize(m_webView);
76     m_rootLayer->setSize(pageSize);
77
78     // The non-composited contents are a child of the root layer.
79     m_nonCompositedContentLayer = GraphicsLayer::create(this);
80     m_nonCompositedContentLayer->setDrawsContent(true);
81     m_nonCompositedContentLayer->setContentsOpaque(!m_webView->priv->transparent);
82     m_nonCompositedContentLayer->setSize(pageSize);
83     if (core(m_webView)->settings()->acceleratedDrawingEnabled())
84         m_nonCompositedContentLayer->setAcceleratesDrawing(true);
85
86 #ifndef NDEBUG
87     m_rootLayer->setName("Root layer");
88     m_nonCompositedContentLayer->setName("Non-composited content");
89 #endif
90
91     m_rootLayer->addChild(m_nonCompositedContentLayer.get());
92     m_nonCompositedContentLayer->setNeedsDisplay();
93
94     // The creation of the TextureMapper needs an active OpenGL context.
95     GLContext* context = m_redirectedWindow->context();
96     context->makeContextCurrent();
97     m_textureMapper = TextureMapperGL::create();
98
99     toTextureMapperLayer(m_rootLayer.get())->setTextureMapper(m_textureMapper.get());
100
101     scheduleLayerFlush();
102 }
103
104 AcceleratedCompositingContext::~AcceleratedCompositingContext()
105 {
106     stopAnyPendingLayerFlush();
107 }
108
109 void AcceleratedCompositingContext::stopAnyPendingLayerFlush()
110 {
111     if (!m_layerFlushTimerCallbackId)
112         return;
113     g_source_remove(m_layerFlushTimerCallbackId);
114     m_layerFlushTimerCallbackId = 0;
115 }
116
117 bool AcceleratedCompositingContext::enabled()
118 {
119     return m_rootLayer && m_textureMapper;
120 }
121
122 bool AcceleratedCompositingContext::renderLayersToWindow(cairo_t* cr, const IntRect& clipRect)
123 {
124     m_redrawPendingTime = 0;
125
126     if (!enabled())
127         return false;
128
129     // It's important to paint a white background (if the WebView isn't transparent), because when growing
130     // the redirected window, the usable size of the window may be smaller than the allocation of our widget.
131     // We don't want to show artifacts in that case.
132     IntSize usableWindowSize = m_redirectedWindow->usableSize();
133     if (usableWindowSize != m_redirectedWindow->size()) {
134         if (!m_webView->priv->transparent) {
135             cairo_set_source_rgb(cr, 1, 1, 1);
136             cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
137         } else
138             cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
139         cairo_paint(cr);
140     }
141
142     cairo_surface_t* windowSurface = m_redirectedWindow->cairoSurfaceForWidget(GTK_WIDGET(m_webView));
143     if (!windowSurface)
144         return true;
145
146     cairo_rectangle(cr, clipRect.x(), clipRect.y(), clipRect.width(), clipRect.height());
147     cairo_set_source_surface(cr, windowSurface, 0, 0);
148     cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
149     cairo_fill(cr);
150
151     if (!m_layerFlushTimerCallbackId && toTextureMapperLayer(m_rootLayer.get())->descendantsOrSelfHaveRunningAnimations() || m_needsExtraFlush) {
152         m_needsExtraFlush = false;
153         double nextFlush = max((1 / gFramesPerSecond) - (currentTime() - m_lastFlushTime), 0.0);
154         m_layerFlushTimerCallbackId = g_timeout_add_full(GDK_PRIORITY_EVENTS, 1000 * nextFlush, reinterpret_cast<GSourceFunc>(layerFlushTimerFiredCallback), this, 0);
155     }
156
157     return true;
158 }
159
160 GLContext* AcceleratedCompositingContext::prepareForRendering()
161 {
162     if (!enabled())
163         return 0;
164
165     GLContext* context = m_redirectedWindow->context();
166     if (!context)
167         return 0;
168
169     if (!context->makeContextCurrent())
170         return 0;
171
172     return context;
173 }
174
175 void AcceleratedCompositingContext::compositeLayersToContext()
176 {
177     GLContext* context = prepareForRendering();
178     if (!context)
179         return;
180
181     const IntSize& windowSize = m_redirectedWindow->size();
182     glViewport(0, 0, windowSize.width(), windowSize.height());
183
184     m_textureMapper->beginPainting();
185     toTextureMapperLayer(m_rootLayer.get())->paint();
186     m_textureMapper->endPainting();
187
188     context->swapBuffers();
189
190     // FIXME: It seems that when using double-buffering (and on some drivers single-buffering)
191     // and XComposite window redirection, two swap buffers are required to force the pixmap
192     // to update. This isn't a problem during animations, because swapBuffer is continuously
193     // called. For non-animation situations we use this terrible hack until we can get to the
194     // bottom of the issue.
195     if (!toTextureMapperLayer(m_rootLayer.get())->descendantsOrSelfHaveRunningAnimations()) {
196         context->swapBuffers();
197         context->swapBuffers();
198     }
199 }
200
201 void AcceleratedCompositingContext::clearEverywhere()
202 {
203     GLContext* context = prepareForRendering();
204     if (!context)
205         return;
206
207     const IntSize& windowSize = m_redirectedWindow->size();
208     glViewport(0, 0, windowSize.width(), windowSize.height());
209     glClearColor(1, 1, 1, 1);
210     glClear(GL_COLOR_BUFFER_BIT);
211
212     context->swapBuffers();
213
214     // FIXME: It seems that when using double-buffering (and on some drivers single-buffering)
215     // and XComposite window redirection, two swap buffers are required to force the pixmap
216     // to update. This isn't a problem during animations, because swapBuffer is continuously
217     // called. For non-animation situations we use this terrible hack until we can get to the
218     // bottom of the issue.
219     if (!toTextureMapperLayer(m_rootLayer.get())->descendantsOrSelfHaveRunningAnimations()) {
220         context->swapBuffers();
221         context->swapBuffers();
222     }
223 }
224
225 void AcceleratedCompositingContext::setRootCompositingLayer(GraphicsLayer* graphicsLayer)
226 {
227     // Clearing everywhere when turning on or off the layer tree prevents us from flashing
228     // old content before the first flush.
229     clearEverywhere();
230
231     if (!graphicsLayer) {
232         stopAnyPendingLayerFlush();
233
234         // Shrink the offscreen window to save memory while accelerated compositing is turned off.
235         m_redirectedWindow->resize(IntSize(1, 1));
236         m_rootLayer = nullptr;
237         m_nonCompositedContentLayer = nullptr;
238         m_textureMapper = nullptr;
239         return;
240     }
241
242     if (graphicsLayer && !enabled())
243         m_redirectedWindow->resize(getWebViewSize(m_webView));
244
245     // Add the accelerated layer tree hierarchy.
246     initialize();
247     m_nonCompositedContentLayer->removeAllChildren();
248     m_nonCompositedContentLayer->addChild(graphicsLayer);
249
250     stopAnyPendingLayerFlush();
251
252     // FIXME: Two flushes seem necessary to get the proper rendering in some cases. It's unclear
253     // if this is a bug with the RedirectedXComposite window or with this class.
254     m_needsExtraFlush = true;
255     scheduleLayerFlush();
256
257     m_layerFlushTimerCallbackId = g_timeout_add_full(GDK_PRIORITY_EVENTS, 500, reinterpret_cast<GSourceFunc>(layerFlushTimerFiredCallback), this, 0);
258 }
259
260 void AcceleratedCompositingContext::setNonCompositedContentsNeedDisplay(const IntRect& rect)
261 {
262     if (!m_rootLayer)
263         return;
264     if (rect.isEmpty()) {
265         m_rootLayer->setNeedsDisplay();
266         return;
267     }
268     m_nonCompositedContentLayer->setNeedsDisplayInRect(rect);
269     scheduleLayerFlush();
270 }
271
272 void AcceleratedCompositingContext::resizeRootLayer(const IntSize& newSize)
273 {
274     if (!enabled())
275         return;
276
277     if (m_rootLayer->size() == newSize)
278         return;
279
280     m_redirectedWindow->resize(newSize);
281     m_rootLayer->setSize(newSize);
282
283     // If the newSize exposes new areas of the non-composited content a setNeedsDisplay is needed
284     // for those newly exposed areas.
285     FloatSize oldSize = m_nonCompositedContentLayer->size();
286     m_nonCompositedContentLayer->setSize(newSize);
287
288     if (newSize.width() > oldSize.width()) {
289         float height = std::min(static_cast<float>(newSize.height()), oldSize.height());
290         m_nonCompositedContentLayer->setNeedsDisplayInRect(FloatRect(oldSize.width(), 0, newSize.width() - oldSize.width(), height));
291     }
292
293     if (newSize.height() > oldSize.height())
294         m_nonCompositedContentLayer->setNeedsDisplayInRect(FloatRect(0, oldSize.height(), newSize.width(), newSize.height() - oldSize.height()));
295
296     m_nonCompositedContentLayer->setNeedsDisplayInRect(IntRect(IntPoint(), newSize));
297     flushAndRenderLayers();
298 }
299
300 void AcceleratedCompositingContext::scrollNonCompositedContents(const IntRect& scrollRect, const IntSize& scrollOffset)
301 {
302     m_nonCompositedContentLayer->setNeedsDisplayInRect(scrollRect);
303     scheduleLayerFlush();
304 }
305
306 gboolean AcceleratedCompositingContext::layerFlushTimerFiredCallback(AcceleratedCompositingContext* context)
307 {
308     context->layerFlushTimerFired();
309     return FALSE;
310 }
311
312 void AcceleratedCompositingContext::scheduleLayerFlush()
313 {
314     if (!enabled())
315         return;
316
317     if (m_layerFlushTimerCallbackId)
318         return;
319
320     // We use a GLib timer because otherwise GTK+ event handling during dragging can
321     // starve WebCore timers, which have a lower priority.
322     double nextFlush = max(gScheduleDelay - (currentTime() - m_lastFlushTime), 0.0);
323     m_layerFlushTimerCallbackId = g_timeout_add_full(GDK_PRIORITY_EVENTS, nextFlush * 1000, reinterpret_cast<GSourceFunc>(layerFlushTimerFiredCallback), this, 0);
324 }
325
326 bool AcceleratedCompositingContext::flushPendingLayerChanges()
327 {
328     m_rootLayer->syncCompositingStateForThisLayerOnly();
329     m_nonCompositedContentLayer->syncCompositingStateForThisLayerOnly();
330     return core(m_webView)->mainFrame()->view()->syncCompositingStateIncludingSubframes();
331 }
332
333 void AcceleratedCompositingContext::flushAndRenderLayers()
334 {
335     if (!enabled())
336         return;
337
338     Frame* frame = core(m_webView)->mainFrame();
339     if (!frame || !frame->contentRenderer() || !frame->view())
340         return;
341     frame->view()->updateLayoutAndStyleIfNeededRecursive();
342
343     GLContext* context = m_redirectedWindow->context();
344     if (context && !context->makeContextCurrent())
345         return;
346
347     if (!flushPendingLayerChanges())
348         return;
349
350     m_lastFlushTime = currentTime();
351     compositeLayersToContext();
352
353     gtk_widget_queue_draw(GTK_WIDGET(m_webView));
354
355     // If it's been a long time since we've actually painted, which means that events might
356     // be starving the main loop, we should force a draw now. This seems to prevent display
357     // lag on http://2012.beercamp.com.
358     if (m_redrawPendingTime && currentTime() - m_redrawPendingTime > gScheduleDelay)
359         gdk_window_process_updates(gtk_widget_get_window(GTK_WIDGET(m_webView)), FALSE);
360     else if (!m_redrawPendingTime)
361         m_redrawPendingTime = currentTime();
362 }
363
364 void AcceleratedCompositingContext::layerFlushTimerFired()
365 {
366     m_layerFlushTimerCallbackId = 0;
367     flushAndRenderLayers();
368 }
369
370 void AcceleratedCompositingContext::notifyAnimationStarted(const GraphicsLayer*, double time)
371 {
372
373 }
374 void AcceleratedCompositingContext::notifySyncRequired(const GraphicsLayer*)
375 {
376
377 }
378
379 void AcceleratedCompositingContext::paintContents(const GraphicsLayer*, GraphicsContext& context, GraphicsLayerPaintingPhase, const IntRect& rectToPaint)
380 {
381     context.save();
382     context.clip(rectToPaint);
383     core(m_webView)->mainFrame()->view()->paint(&context, rectToPaint);
384     context.restore();
385 }
386
387 bool AcceleratedCompositingContext::showDebugBorders(const GraphicsLayer*) const
388 {
389     return false;
390 }
391
392 bool AcceleratedCompositingContext::showRepaintCounter(const GraphicsLayer*) const
393 {
394     return false;
395 }
396
397 } // namespace WebKit
398
399 #endif // USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER_GL)