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
60using namespace WebCore;
61
62namespace WebKit {
63
64PassRefPtr<LayerTreeHostGtk> LayerTreeHostGtk::create(WebPage* webPage)
65{
66    RefPtr<LayerTreeHostGtk> host = adoptRef(new LayerTreeHostGtk(webPage));
67    host->initialize();
68    return host.release();
69}
70
71LayerTreeHostGtk::LayerTreeHostGtk(WebPage* webPage)
72    : LayerTreeHost(webPage)
73    , m_isValid(true)
74    , m_notifyAfterScheduledLayerFlush(false)
75    , m_lastFlushTime(0)
76    , m_layerFlushSchedulingEnabled(true)
77{
78}
79
80GLContext* LayerTreeHostGtk::glContext()
81{
82    if (m_context)
83        return m_context.get();
84
85    uint64_t windowHandle = m_webPage->nativeWindowHandle();
86    if (!windowHandle)
87        return 0;
88
89    m_context = GLContext::createContextForWindow(windowHandle, GLContext::sharingContext());
90    return m_context.get();
91}
92
93void LayerTreeHostGtk::initialize()
94{
95    m_rootLayer = GraphicsLayer::create(graphicsLayerFactory(), *this);
96    m_rootLayer->setDrawsContent(false);
97    m_rootLayer->setSize(m_webPage->size());
98
99    // The non-composited contents are a child of the root layer.
100    m_nonCompositedContentLayer = GraphicsLayer::create(graphicsLayerFactory(), *this);
101    m_nonCompositedContentLayer->setDrawsContent(true);
102    m_nonCompositedContentLayer->setContentsOpaque(m_webPage->drawsBackground() && !m_webPage->drawsTransparentBackground());
103    m_nonCompositedContentLayer->setSize(m_webPage->size());
104    if (m_webPage->corePage()->settings().acceleratedDrawingEnabled())
105        m_nonCompositedContentLayer->setAcceleratesDrawing(true);
106
107#ifndef NDEBUG
108    m_rootLayer->setName("LayerTreeHost root layer");
109    m_nonCompositedContentLayer->setName("LayerTreeHost non-composited content");
110#endif
111
112    m_rootLayer->addChild(m_nonCompositedContentLayer.get());
113    m_nonCompositedContentLayer->setNeedsDisplay();
114
115    m_layerTreeContext.contextID = m_webPage->nativeWindowHandle();
116
117    GLContext* context = glContext();
118    if (!context)
119        return;
120
121    // The creation of the TextureMapper needs an active OpenGL context.
122    context->makeContextCurrent();
123
124    m_textureMapper = TextureMapper::create(TextureMapper::OpenGLMode);
125    static_cast<TextureMapperGL*>(m_textureMapper.get())->setEnableEdgeDistanceAntialiasing(true);
126    toTextureMapperLayer(m_rootLayer.get())->setTextureMapper(m_textureMapper.get());
127
128    // FIXME: Cretae page olverlay layers. https://bugs.webkit.org/show_bug.cgi?id=131433.
129
130    scheduleLayerFlush();
131}
132
133LayerTreeHostGtk::~LayerTreeHostGtk()
134{
135    ASSERT(!m_isValid);
136    ASSERT(!m_rootLayer);
137    cancelPendingLayerFlush();
138}
139
140const LayerTreeContext& LayerTreeHostGtk::layerTreeContext()
141{
142    return m_layerTreeContext;
143}
144
145void LayerTreeHostGtk::setShouldNotifyAfterNextScheduledLayerFlush(bool notifyAfterScheduledLayerFlush)
146{
147    m_notifyAfterScheduledLayerFlush = notifyAfterScheduledLayerFlush;
148}
149
150void LayerTreeHostGtk::setRootCompositingLayer(GraphicsLayer* graphicsLayer)
151{
152    m_nonCompositedContentLayer->removeAllChildren();
153
154    // Add the accelerated layer tree hierarchy.
155    if (graphicsLayer)
156        m_nonCompositedContentLayer->addChild(graphicsLayer);
157
158    scheduleLayerFlush();
159}
160
161void LayerTreeHostGtk::invalidate()
162{
163    ASSERT(m_isValid);
164
165    // This can trigger destruction of GL objects so let's make sure that
166    // we have the right active context
167    if (m_context)
168        m_context->makeContextCurrent();
169
170    cancelPendingLayerFlush();
171    m_rootLayer = nullptr;
172    m_nonCompositedContentLayer = nullptr;
173    m_textureMapper = nullptr;
174
175    m_context = nullptr;
176    m_isValid = false;
177}
178
179void LayerTreeHostGtk::setNonCompositedContentsNeedDisplay()
180{
181    m_nonCompositedContentLayer->setNeedsDisplay();
182
183    PageOverlayLayerMap::iterator end = m_pageOverlayLayers.end();
184    for (PageOverlayLayerMap::iterator it = m_pageOverlayLayers.begin(); it != end; ++it)
185        it->value->setNeedsDisplay();
186
187    scheduleLayerFlush();
188}
189
190void LayerTreeHostGtk::setNonCompositedContentsNeedDisplayInRect(const IntRect& rect)
191{
192    m_nonCompositedContentLayer->setNeedsDisplayInRect(rect);
193
194    PageOverlayLayerMap::iterator end = m_pageOverlayLayers.end();
195    for (PageOverlayLayerMap::iterator it = m_pageOverlayLayers.begin(); it != end; ++it)
196        it->value->setNeedsDisplayInRect(rect);
197
198    scheduleLayerFlush();
199}
200
201void LayerTreeHostGtk::scrollNonCompositedContents(const IntRect& scrollRect)
202{
203    setNonCompositedContentsNeedDisplayInRect(scrollRect);
204}
205
206void LayerTreeHostGtk::sizeDidChange(const IntSize& newSize)
207{
208    if (m_rootLayer->size() == newSize)
209        return;
210    m_rootLayer->setSize(newSize);
211
212    // If the newSize exposes new areas of the non-composited content a setNeedsDisplay is needed
213    // for those newly exposed areas.
214    FloatSize oldSize = m_nonCompositedContentLayer->size();
215    m_nonCompositedContentLayer->setSize(newSize);
216
217    if (newSize.width() > oldSize.width()) {
218        float height = std::min(static_cast<float>(newSize.height()), oldSize.height());
219        m_nonCompositedContentLayer->setNeedsDisplayInRect(FloatRect(oldSize.width(), 0, newSize.width() - oldSize.width(), height));
220    }
221
222    if (newSize.height() > oldSize.height())
223        m_nonCompositedContentLayer->setNeedsDisplayInRect(FloatRect(0, oldSize.height(), newSize.width(), newSize.height() - oldSize.height()));
224    m_nonCompositedContentLayer->setNeedsDisplay();
225
226    PageOverlayLayerMap::iterator end = m_pageOverlayLayers.end();
227    for (PageOverlayLayerMap::iterator it = m_pageOverlayLayers.begin(); it != end; ++it)
228        it->value->setSize(newSize);
229
230    compositeLayersToContext(ForResize);
231}
232
233void LayerTreeHostGtk::deviceOrPageScaleFactorChanged()
234{
235    // Other layers learn of the scale factor change via WebPage::setDeviceScaleFactor.
236    m_nonCompositedContentLayer->deviceOrPageScaleFactorChanged();
237}
238
239void LayerTreeHostGtk::forceRepaint()
240{
241    scheduleLayerFlush();
242}
243
244void LayerTreeHostGtk::didInstallPageOverlay(PageOverlay* pageOverlay)
245{
246    createPageOverlayLayer(pageOverlay);
247    scheduleLayerFlush();
248}
249
250void LayerTreeHostGtk::didUninstallPageOverlay(PageOverlay* pageOverlay)
251{
252    destroyPageOverlayLayer(pageOverlay);
253    scheduleLayerFlush();
254}
255
256void LayerTreeHostGtk::setPageOverlayNeedsDisplay(PageOverlay* pageOverlay, const IntRect& rect)
257{
258    GraphicsLayer* layer = m_pageOverlayLayers.get(pageOverlay);
259    if (!layer)
260        return;
261
262    layer->setNeedsDisplayInRect(rect);
263    scheduleLayerFlush();
264}
265
266void LayerTreeHostGtk::notifyAnimationStarted(const WebCore::GraphicsLayer*, double /* time */)
267{
268}
269
270void LayerTreeHostGtk::notifyFlushRequired(const WebCore::GraphicsLayer*)
271{
272}
273
274void LayerTreeHostGtk::paintContents(const GraphicsLayer* graphicsLayer, GraphicsContext& graphicsContext, GraphicsLayerPaintingPhase, const FloatRect& clipRect)
275{
276    if (graphicsLayer == m_nonCompositedContentLayer.get()) {
277        m_webPage->drawRect(graphicsContext, enclosingIntRect(clipRect));
278        return;
279    }
280
281    // FIXME: Draw page overlays. https://bugs.webkit.org/show_bug.cgi?id=131433.
282}
283
284void LayerTreeHostGtk::layerFlushTimerFired()
285{
286    flushAndRenderLayers();
287
288    if (!m_layerFlushTimerCallback.isScheduled() && toTextureMapperLayer(m_rootLayer.get())->descendantsOrSelfHaveRunningAnimations()) {
289        const double targetFPS = 60;
290        double nextFlush = std::max((1 / targetFPS) - (currentTime() - m_lastFlushTime), 0.0);
291        m_layerFlushTimerCallback.scheduleAfterDelay("[WebKit] layerFlushTimer", std::bind(&LayerTreeHostGtk::layerFlushTimerFired, this),
292            std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::duration<double>(nextFlush)), GDK_PRIORITY_EVENTS);
293    }
294}
295
296bool LayerTreeHostGtk::flushPendingLayerChanges()
297{
298    m_rootLayer->flushCompositingStateForThisLayerOnly();
299    m_nonCompositedContentLayer->flushCompositingStateForThisLayerOnly();
300
301    PageOverlayLayerMap::iterator end = m_pageOverlayLayers.end();
302    for (PageOverlayLayerMap::iterator it = m_pageOverlayLayers.begin(); it != end; ++it)
303        it->value->flushCompositingStateForThisLayerOnly();
304
305    return m_webPage->corePage()->mainFrame().view()->flushCompositingStateIncludingSubframes();
306}
307
308void LayerTreeHostGtk::compositeLayersToContext(CompositePurpose purpose)
309{
310    GLContext* context = glContext();
311    if (!context || !context->makeContextCurrent())
312        return;
313
314    // The window size may be out of sync with the page size at this point, and getting
315    // the viewport parameters incorrect, means that the content will be misplaced. Thus
316    // we set the viewport parameters directly from the window size.
317    IntSize contextSize = m_context->defaultFrameBufferSize();
318    glViewport(0, 0, contextSize.width(), contextSize.height());
319
320    if (purpose == ForResize) {
321        glClearColor(1, 1, 1, 0);
322        glClear(GL_COLOR_BUFFER_BIT);
323    }
324
325    m_textureMapper->beginPainting();
326    toTextureMapperLayer(m_rootLayer.get())->paint();
327    m_textureMapper->endPainting();
328
329    context->swapBuffers();
330}
331
332void LayerTreeHostGtk::flushAndRenderLayers()
333{
334    {
335        RefPtr<LayerTreeHostGtk> protect(this);
336        m_webPage->layoutIfNeeded();
337
338        if (!m_isValid)
339            return;
340    }
341
342    GLContext* context = glContext();
343    if (!context || !context->makeContextCurrent())
344        return;
345
346    m_lastFlushTime = currentTime();
347    if (!flushPendingLayerChanges())
348        return;
349
350    // Our model is very simple. We always composite and render the tree immediately after updating it.
351    compositeLayersToContext();
352
353    if (m_notifyAfterScheduledLayerFlush) {
354        // Let the drawing area know that we've done a flush of the layer changes.
355        static_cast<DrawingAreaImpl*>(m_webPage->drawingArea())->layerHostDidFlushLayers();
356        m_notifyAfterScheduledLayerFlush = false;
357    }
358}
359
360void LayerTreeHostGtk::createPageOverlayLayer(PageOverlay* pageOverlay)
361{
362    std::unique_ptr<GraphicsLayer> layer = GraphicsLayer::create(graphicsLayerFactory(), *this);
363#ifndef NDEBUG
364    layer->setName("LayerTreeHost page overlay content");
365#endif
366
367    layer->setAcceleratesDrawing(m_webPage->corePage()->settings().acceleratedDrawingEnabled());
368    layer->setDrawsContent(true);
369    layer->setSize(m_webPage->size());
370    layer->setShowDebugBorder(m_webPage->corePage()->settings().showDebugBorders());
371    layer->setShowRepaintCounter(m_webPage->corePage()->settings().showRepaintCounter());
372
373    m_rootLayer->addChild(layer.get());
374    m_pageOverlayLayers.add(pageOverlay, WTF::move(layer));
375}
376
377void LayerTreeHostGtk::destroyPageOverlayLayer(PageOverlay* pageOverlay)
378{
379    std::unique_ptr<GraphicsLayer> layer = m_pageOverlayLayers.take(pageOverlay);
380    ASSERT(layer);
381
382    layer->removeFromParent();
383}
384
385void LayerTreeHostGtk::scheduleLayerFlush()
386{
387    if (!m_layerFlushSchedulingEnabled)
388        return;
389
390    // We use a GLib timer because otherwise GTK+ event handling during dragging can starve WebCore timers, which have a lower priority.
391    if (!m_layerFlushTimerCallback.isScheduled())
392        m_layerFlushTimerCallback.schedule("[WebKit] layerFlushTimer", std::bind(&LayerTreeHostGtk::layerFlushTimerFired, this), GDK_PRIORITY_EVENTS);
393}
394
395void LayerTreeHostGtk::setLayerFlushSchedulingEnabled(bool layerFlushingEnabled)
396{
397    if (m_layerFlushSchedulingEnabled == layerFlushingEnabled)
398        return;
399
400    m_layerFlushSchedulingEnabled = layerFlushingEnabled;
401
402    if (m_layerFlushSchedulingEnabled) {
403        scheduleLayerFlush();
404        return;
405    }
406
407    cancelPendingLayerFlush();
408}
409
410void LayerTreeHostGtk::pageBackgroundTransparencyChanged()
411{
412    m_nonCompositedContentLayer->setContentsOpaque(m_webPage->drawsBackground() && !m_webPage->drawsTransparentBackground());
413}
414
415void LayerTreeHostGtk::cancelPendingLayerFlush()
416{
417    m_layerFlushTimerCallback.cancel();
418}
419
420} // namespace WebKit
421
422#endif
423