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