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