9e3d3a08b9ccc47958c9b6e4b8ce960b1984a8fe
[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     }
173
174     return true;
175 }
176
177 GLContext* AcceleratedCompositingContext::prepareForRendering()
178 {
179     if (!enabled())
180         return 0;
181
182     GLContext* context = m_redirectedWindow->context();
183     if (!context)
184         return 0;
185
186     if (!context->makeContextCurrent())
187         return 0;
188
189     return context;
190 }
191
192 void AcceleratedCompositingContext::compositeLayersToContext(CompositePurpose purpose)
193 {
194     GLContext* context = prepareForRendering();
195     if (!context)
196         return;
197
198     const IntSize& windowSize = m_redirectedWindow->size();
199     glViewport(0, 0, windowSize.width(), windowSize.height());
200
201     if (purpose == ForResize) {
202         glClearColor(1, 1, 1, 0);
203         glClear(GL_COLOR_BUFFER_BIT);
204     }
205
206     m_textureMapper->beginPainting();
207     toTextureMapperLayer(m_rootLayer.get())->paint();
208     m_fpsCounter.updateFPSAndDisplay(m_textureMapper.get());
209     m_textureMapper->endPainting();
210
211     context->swapBuffers();
212 }
213
214 void AcceleratedCompositingContext::clearEverywhere()
215 {
216     GLContext* context = prepareForRendering();
217     if (!context)
218         return;
219
220     const IntSize& windowSize = m_redirectedWindow->size();
221     glViewport(0, 0, windowSize.width(), windowSize.height());
222     glClearColor(1, 1, 1, 1);
223     glClear(GL_COLOR_BUFFER_BIT);
224
225     context->swapBuffers();
226
227     // FIXME: It seems that when using double-buffering (and on some drivers single-buffering)
228     // and XComposite window redirection, two swap buffers are required to force the pixmap
229     // to update. This isn't a problem during animations, because swapBuffer is continuously
230     // called. For non-animation situations we use this terrible hack until we can get to the
231     // bottom of the issue.
232     if (!toTextureMapperLayer(m_rootLayer.get())->descendantsOrSelfHaveRunningAnimations()) {
233         context->swapBuffers();
234         context->swapBuffers();
235     }
236 }
237
238 void AcceleratedCompositingContext::setRootCompositingLayer(GraphicsLayer* graphicsLayer)
239 {
240     // Clearing everywhere when turning on or off the layer tree prevents us from flashing
241     // old content before the first flush.
242     clearEverywhere();
243
244     if (!graphicsLayer) {
245         stopAnyPendingLayerFlush();
246
247         // Shrink the offscreen window to save memory while accelerated compositing is turned off.
248         if (m_redirectedWindow)
249             m_redirectedWindow->resize(IntSize(1, 1));
250         m_rootLayer = nullptr;
251         m_nonCompositedContentLayer = nullptr;
252         m_textureMapper = nullptr;
253         return;
254     }
255
256     // Add the accelerated layer tree hierarchy.
257     initialize();
258     if (!m_redirectedWindow)
259         return;
260
261     m_nonCompositedContentLayer->removeAllChildren();
262     m_nonCompositedContentLayer->addChild(graphicsLayer);
263
264     stopAnyPendingLayerFlush();
265
266     // FIXME: Two flushes seem necessary to get the proper rendering in some cases. It's unclear
267     // if this is a bug with the RedirectedXComposite window or with this class.
268     m_needsExtraFlush = true;
269     scheduleLayerFlush();
270
271     m_layerFlushTimerCallbackId = g_timeout_add_full(GDK_PRIORITY_EVENTS, 500, reinterpret_cast<GSourceFunc>(layerFlushTimerFiredCallback), this, 0);
272 }
273
274 void AcceleratedCompositingContext::setNonCompositedContentsNeedDisplay(const IntRect& rect)
275 {
276     if (!m_rootLayer)
277         return;
278     if (rect.isEmpty()) {
279         m_rootLayer->setNeedsDisplay();
280         return;
281     }
282     m_nonCompositedContentLayer->setNeedsDisplayInRect(rect);
283     scheduleLayerFlush();
284 }
285
286 void AcceleratedCompositingContext::resizeRootLayer(const IntSize& newSize)
287 {
288     if (!enabled())
289         return;
290
291     if (m_rootLayer->size() == newSize)
292         return;
293
294     m_redirectedWindow->resize(newSize);
295     m_rootLayer->setSize(newSize);
296
297     // If the newSize exposes new areas of the non-composited content a setNeedsDisplay is needed
298     // for those newly exposed areas.
299     FloatSize oldSize = m_nonCompositedContentLayer->size();
300     m_nonCompositedContentLayer->setSize(newSize);
301
302     if (newSize.width() > oldSize.width()) {
303         float height = std::min(static_cast<float>(newSize.height()), oldSize.height());
304         m_nonCompositedContentLayer->setNeedsDisplayInRect(FloatRect(oldSize.width(), 0, newSize.width() - oldSize.width(), height));
305     }
306
307     if (newSize.height() > oldSize.height())
308         m_nonCompositedContentLayer->setNeedsDisplayInRect(FloatRect(0, oldSize.height(), newSize.width(), newSize.height() - oldSize.height()));
309
310     m_nonCompositedContentLayer->setNeedsDisplayInRect(IntRect(IntPoint(), newSize));
311     compositeLayersToContext(ForResize);
312     scheduleLayerFlush();
313 }
314
315 void AcceleratedCompositingContext::scrollNonCompositedContents(const IntRect& scrollRect, const IntSize& scrollOffset)
316 {
317     m_nonCompositedContentLayer->setNeedsDisplayInRect(scrollRect);
318     scheduleLayerFlush();
319 }
320
321 gboolean AcceleratedCompositingContext::layerFlushTimerFiredCallback(AcceleratedCompositingContext* context)
322 {
323     context->layerFlushTimerFired();
324     return FALSE;
325 }
326
327 void AcceleratedCompositingContext::scheduleLayerFlush()
328 {
329     if (!enabled())
330         return;
331
332     if (m_layerFlushTimerCallbackId)
333         return;
334
335     // We use a GLib timer because otherwise GTK+ event handling during dragging can
336     // starve WebCore timers, which have a lower priority.
337     double nextFlush = std::max(gScheduleDelay - (currentTime() - m_lastFlushTime), 0.0);
338     m_layerFlushTimerCallbackId = g_timeout_add_full(GDK_PRIORITY_EVENTS, nextFlush * 1000, reinterpret_cast<GSourceFunc>(layerFlushTimerFiredCallback), this, 0);
339 }
340
341 bool AcceleratedCompositingContext::flushPendingLayerChanges()
342 {
343     m_rootLayer->flushCompositingStateForThisLayerOnly();
344     m_nonCompositedContentLayer->flushCompositingStateForThisLayerOnly();
345     return core(m_webView)->mainFrame().view()->flushCompositingStateIncludingSubframes();
346 }
347
348 void AcceleratedCompositingContext::flushAndRenderLayers()
349 {
350     if (!enabled())
351         return;
352
353     Frame& frame = core(m_webView)->mainFrame();
354     if (!frame.contentRenderer() || !frame.view())
355         return;
356     frame.view()->updateLayoutAndStyleIfNeededRecursive();
357
358     if (!enabled())
359         return;
360
361     GLContext* context = m_redirectedWindow->context();
362     if (context && !context->makeContextCurrent())
363         return;
364
365     if (!flushPendingLayerChanges())
366         return;
367
368     m_lastFlushTime = currentTime();
369     compositeLayersToContext();
370
371     // If it's been a long time since we've actually painted, which means that events might
372     // be starving the main loop, we should force a draw now. This seems to prevent display
373     // lag on http://2012.beercamp.com.
374     if (m_redrawPendingTime && currentTime() - m_redrawPendingTime > gScheduleDelay) {
375         gtk_widget_queue_draw(GTK_WIDGET(m_webView));
376         gdk_window_process_updates(gtk_widget_get_window(GTK_WIDGET(m_webView)), FALSE);
377     } else if (!m_redrawPendingTime)
378         m_redrawPendingTime = currentTime();
379 }
380
381 void AcceleratedCompositingContext::layerFlushTimerFired()
382 {
383     m_layerFlushTimerCallbackId = 0;
384     flushAndRenderLayers();
385 }
386
387 void AcceleratedCompositingContext::notifyAnimationStarted(const GraphicsLayer*, double time)
388 {
389
390 }
391 void AcceleratedCompositingContext::notifyFlushRequired(const GraphicsLayer*)
392 {
393
394 }
395
396 void AcceleratedCompositingContext::paintContents(const GraphicsLayer*, GraphicsContext& context, GraphicsLayerPaintingPhase, const IntRect& rectToPaint)
397 {
398     context.save();
399     context.clip(rectToPaint);
400     core(m_webView)->mainFrame().view()->paint(&context, rectToPaint);
401     context.restore();
402 }
403
404 } // namespace WebKit
405
406 #endif // USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER_GL)