93a8ef30906024f4006e63a712661ec1a97a55c4
[WebKit-https.git] / Source / WebCore / platform / graphics / texmap / coordinated / CompositingCoordinator.cpp
1 /*
2  * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
3  * Copyright (C) 2013 Company 100, Inc. All rights reserved.
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
29 #if USE(COORDINATED_GRAPHICS)
30
31 #include "CompositingCoordinator.h"
32
33 #include "DOMWindow.h"
34 #include "Document.h"
35 #include "FrameView.h"
36 #include "GraphicsContext.h"
37 #include "InspectorController.h"
38 #include "MainFrame.h"
39 #include "Page.h"
40 #include "Settings.h"
41 #include <wtf/CurrentTime.h>
42 #include <wtf/TemporaryChange.h>
43
44 // FIXME: Having this in the platform directory is a layering violation. This does not belong here.
45
46 namespace WebCore {
47
48 CompositingCoordinator::CompositingCoordinator(Page* page, CompositingCoordinator::Client* client)
49     : m_page(page)
50     , m_client(client)
51     , m_rootCompositingLayer(nullptr)
52     , m_overlayCompositingLayer(nullptr)
53     , m_isDestructing(false)
54     , m_isPurging(false)
55     , m_isFlushingLayerChanges(false)
56     , m_shouldSyncFrame(false)
57     , m_didInitializeRootCompositingLayer(false)
58     , m_releaseInactiveAtlasesTimer(*this, &CompositingCoordinator::releaseInactiveAtlasesTimerFired)
59 #if ENABLE(REQUEST_ANIMATION_FRAME)
60     , m_lastAnimationServiceTime(0)
61 #endif
62 {
63 }
64
65 CompositingCoordinator::~CompositingCoordinator()
66 {
67     m_isDestructing = true;
68
69     purgeBackingStores();
70
71     for (auto& registeredLayer : m_registeredLayers.values())
72         registeredLayer->setCoordinator(nullptr);
73 }
74
75 void CompositingCoordinator::setRootCompositingLayer(GraphicsLayer* graphicsLayer)
76 {
77     if (m_rootCompositingLayer == graphicsLayer)
78         return;
79
80     if (m_rootCompositingLayer)
81         m_rootCompositingLayer->removeFromParent();
82
83     m_rootCompositingLayer = graphicsLayer;
84     if (m_rootCompositingLayer)
85         m_rootLayer->addChildAtIndex(m_rootCompositingLayer, 0);
86 }
87
88 void CompositingCoordinator::setViewOverlayRootLayer(GraphicsLayer* graphicsLayer)
89 {
90     if (m_overlayCompositingLayer == graphicsLayer)
91         return;
92
93     if (m_overlayCompositingLayer)
94         m_overlayCompositingLayer->removeFromParent();
95
96     m_overlayCompositingLayer = graphicsLayer;
97     if (m_overlayCompositingLayer)
98         m_rootLayer->addChild(m_overlayCompositingLayer);
99 }
100
101 void CompositingCoordinator::sizeDidChange(const IntSize& newSize)
102 {
103     m_rootLayer->setSize(newSize);
104     notifyFlushRequired(m_rootLayer.get());
105 }
106
107 bool CompositingCoordinator::flushPendingLayerChanges()
108 {
109     TemporaryChange<bool> protector(m_isFlushingLayerChanges, true);
110
111     initializeRootCompositingLayerIfNeeded();
112
113     bool viewportIsStable = m_page->mainFrame().view()->viewportIsStable();
114     m_rootLayer->flushCompositingStateForThisLayerOnly(viewportIsStable);
115     m_client->didFlushRootLayer(m_visibleContentsRect);
116
117     if (m_overlayCompositingLayer)
118         m_overlayCompositingLayer->flushCompositingState(FloatRect(FloatPoint(), m_rootLayer->size()), viewportIsStable);
119
120     bool didSync = m_page->mainFrame().view()->flushCompositingStateIncludingSubframes();
121
122     toCoordinatedGraphicsLayer(m_rootLayer.get())->updateContentBuffersIncludingSubLayers();
123     toCoordinatedGraphicsLayer(m_rootLayer.get())->syncPendingStateChangesIncludingSubLayers();
124
125     flushPendingImageBackingChanges();
126
127     if (m_shouldSyncFrame) {
128         didSync = true;
129
130         if (m_rootCompositingLayer) {
131             m_state.contentsSize = roundedIntSize(m_rootCompositingLayer->size());
132             if (CoordinatedGraphicsLayer* contentsLayer = mainContentsLayer())
133                 m_state.coveredRect = contentsLayer->coverRect();
134         }
135         m_state.scrollPosition = m_visibleContentsRect.location();
136
137         m_client->commitSceneState(m_state);
138
139         clearPendingStateChanges();
140         m_shouldSyncFrame = false;
141     }
142
143     return didSync;
144 }
145
146 double CompositingCoordinator::timestamp() const
147 {
148     auto* document = m_page->mainFrame().document();
149     if (!document)
150         return 0;
151     return document->domWindow() ? document->domWindow()->nowTimestamp() : document->monotonicTimestamp();
152 }
153
154 void CompositingCoordinator::syncDisplayState()
155 {
156 #if ENABLE(REQUEST_ANIMATION_FRAME) && !USE(REQUEST_ANIMATION_FRAME_TIMER) && !USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
157     // Make sure that any previously registered animation callbacks are being executed before we flush the layers.
158     m_lastAnimationServiceTime = timestamp();
159     m_page->mainFrame().view()->serviceScriptedAnimations();
160 #endif
161     m_page->mainFrame().view()->updateLayoutAndStyleIfNeededRecursive();
162 }
163
164 #if ENABLE(REQUEST_ANIMATION_FRAME)
165 double CompositingCoordinator::nextAnimationServiceTime() const
166 {
167     // According to the requestAnimationFrame spec, rAF callbacks should not be faster than 60FPS.
168     static const double MinimalTimeoutForAnimations = 1. / 60.;
169     return std::max<double>(0., MinimalTimeoutForAnimations - timestamp() + m_lastAnimationServiceTime);
170 }
171 #endif
172
173 void CompositingCoordinator::clearPendingStateChanges()
174 {
175     m_state.layersToCreate.clear();
176     m_state.layersToUpdate.clear();
177     m_state.layersToRemove.clear();
178
179     m_state.imagesToCreate.clear();
180     m_state.imagesToRemove.clear();
181     m_state.imagesToUpdate.clear();
182     m_state.imagesToClear.clear();
183
184     m_state.updateAtlasesToCreate.clear();
185     m_state.updateAtlasesToRemove.clear();
186 }
187
188 void CompositingCoordinator::initializeRootCompositingLayerIfNeeded()
189 {
190     if (m_didInitializeRootCompositingLayer)
191         return;
192
193     m_state.rootCompositingLayer = toCoordinatedGraphicsLayer(m_rootLayer.get())->id();
194     m_didInitializeRootCompositingLayer = true;
195     m_shouldSyncFrame = true;
196 }
197
198 void CompositingCoordinator::createRootLayer(const IntSize& size)
199 {
200     ASSERT(!m_rootLayer);
201     // Create a root layer.
202     m_rootLayer = GraphicsLayer::create(this, *this);
203 #ifndef NDEBUG
204     m_rootLayer->setName("CompositingCoordinator root layer");
205 #endif
206     m_rootLayer->setDrawsContent(false);
207     m_rootLayer->setSize(size);
208 }
209
210 void CompositingCoordinator::syncLayerState(CoordinatedLayerID id, CoordinatedGraphicsLayerState& state)
211 {
212     m_shouldSyncFrame = true;
213     m_state.layersToUpdate.append(std::make_pair(id, state));
214 }
215
216 PassRefPtr<CoordinatedImageBacking> CompositingCoordinator::createImageBackingIfNeeded(Image* image)
217 {
218     CoordinatedImageBackingID imageID = CoordinatedImageBacking::getCoordinatedImageBackingID(image);
219     ImageBackingMap::iterator it = m_imageBackings.find(imageID);
220     RefPtr<CoordinatedImageBacking> imageBacking;
221     if (it == m_imageBackings.end()) {
222         imageBacking = CoordinatedImageBacking::create(this, image);
223         m_imageBackings.add(imageID, imageBacking);
224     } else
225         imageBacking = it->value;
226
227     return imageBacking;
228 }
229
230 void CompositingCoordinator::createImageBacking(CoordinatedImageBackingID imageID)
231 {
232     m_state.imagesToCreate.append(imageID);
233 }
234
235 void CompositingCoordinator::updateImageBacking(CoordinatedImageBackingID imageID, PassRefPtr<CoordinatedSurface> coordinatedSurface)
236 {
237     m_shouldSyncFrame = true;
238     m_state.imagesToUpdate.append(std::make_pair(imageID, coordinatedSurface));
239 }
240
241 void CompositingCoordinator::clearImageBackingContents(CoordinatedImageBackingID imageID)
242 {
243     m_shouldSyncFrame = true;
244     m_state.imagesToClear.append(imageID);
245 }
246
247 void CompositingCoordinator::removeImageBacking(CoordinatedImageBackingID imageID)
248 {
249     if (m_isPurging)
250         return;
251
252     ASSERT(m_imageBackings.contains(imageID));
253     m_imageBackings.remove(imageID);
254
255     m_state.imagesToRemove.append(imageID);
256
257     size_t imageIDPosition = m_state.imagesToClear.find(imageID);
258     if (imageIDPosition != notFound)
259         m_state.imagesToClear.remove(imageIDPosition);
260 }
261
262 void CompositingCoordinator::flushPendingImageBackingChanges()
263 {
264     for (auto& imageBacking : m_imageBackings.values())
265         imageBacking->update();
266 }
267
268 void CompositingCoordinator::notifyAnimationStarted(const GraphicsLayer*, const String&, double /* time */)
269 {
270 }
271
272 void CompositingCoordinator::notifyFlushRequired(const GraphicsLayer*)
273 {
274     if (!m_isDestructing && !isFlushingLayerChanges())
275         m_client->notifyFlushRequired();
276 }
277
278 void CompositingCoordinator::paintContents(const GraphicsLayer* graphicsLayer, GraphicsContext& graphicsContext, GraphicsLayerPaintingPhase, const FloatRect& clipRect)
279 {
280     m_client->paintLayerContents(graphicsLayer, graphicsContext, enclosingIntRect(clipRect));
281 }
282
283 std::unique_ptr<GraphicsLayer> CompositingCoordinator::createGraphicsLayer(GraphicsLayer::Type layerType, GraphicsLayerClient& client)
284 {
285     CoordinatedGraphicsLayer* layer = new CoordinatedGraphicsLayer(layerType, client);
286     layer->setCoordinator(this);
287     m_registeredLayers.add(layer->id(), layer);
288     m_state.layersToCreate.append(layer->id());
289     layer->setNeedsVisibleRectAdjustment();
290     notifyFlushRequired(layer);
291     return std::unique_ptr<GraphicsLayer>(layer);
292 }
293
294 float CompositingCoordinator::deviceScaleFactor() const
295 {
296     return m_page->deviceScaleFactor();
297 }
298
299 float CompositingCoordinator::pageScaleFactor() const
300 {
301     return m_page->pageScaleFactor();
302 }
303
304 void CompositingCoordinator::createUpdateAtlas(uint32_t atlasID, PassRefPtr<CoordinatedSurface> coordinatedSurface)
305 {
306     m_state.updateAtlasesToCreate.append(std::make_pair(atlasID, coordinatedSurface));
307 }
308
309 void CompositingCoordinator::removeUpdateAtlas(uint32_t atlasID)
310 {
311     if (m_isPurging)
312         return;
313     m_state.updateAtlasesToRemove.append(atlasID);
314 }
315
316 FloatRect CompositingCoordinator::visibleContentsRect() const
317 {
318     return m_visibleContentsRect;
319 }
320
321 CoordinatedGraphicsLayer* CompositingCoordinator::mainContentsLayer()
322 {
323     if (!m_rootCompositingLayer)
324         return 0;
325
326     return toCoordinatedGraphicsLayer(m_rootCompositingLayer)->findFirstDescendantWithContentsRecursively();
327 }
328
329 void CompositingCoordinator::setVisibleContentsRect(const FloatRect& rect, const FloatPoint& trajectoryVector)
330 {
331     // A zero trajectoryVector indicates that tiles all around the viewport are requested.
332     if (CoordinatedGraphicsLayer* contentsLayer = mainContentsLayer())
333         contentsLayer->setVisibleContentRectTrajectoryVector(trajectoryVector);
334
335     bool contentsRectDidChange = rect != m_visibleContentsRect;
336     if (contentsRectDidChange) {
337         m_visibleContentsRect = rect;
338
339         for (auto& registeredLayer : m_registeredLayers.values())
340             registeredLayer->setNeedsVisibleRectAdjustment();
341     }
342
343     FrameView* view = m_page->mainFrame().view();
344     if (view->useFixedLayout() && contentsRectDidChange) {
345         // Round the rect instead of enclosing it to make sure that its size stays
346         // the same while panning. This can have nasty effects on layout.
347         view->setFixedVisibleContentRect(roundedIntRect(rect));
348     }
349 }
350
351 void CompositingCoordinator::deviceOrPageScaleFactorChanged()
352 {
353     m_rootLayer->deviceOrPageScaleFactorChanged();
354 }
355
356 void CompositingCoordinator::detachLayer(CoordinatedGraphicsLayer* layer)
357 {
358     if (m_isPurging)
359         return;
360
361     m_registeredLayers.remove(layer->id());
362
363     size_t index = m_state.layersToCreate.find(layer->id());
364     if (index != notFound) {
365         m_state.layersToCreate.remove(index);
366         return;
367     }
368
369     m_state.layersToRemove.append(layer->id());
370     notifyFlushRequired(layer);
371 }
372
373 void CompositingCoordinator::commitScrollOffset(uint32_t layerID, const WebCore::IntSize& offset)
374 {
375     LayerMap::iterator i = m_registeredLayers.find(layerID);
376     if (i == m_registeredLayers.end())
377         return;
378
379     i->value->commitScrollOffset(offset);
380 }
381
382 void CompositingCoordinator::renderNextFrame()
383 {
384     for (auto& atlas : m_updateAtlases)
385         atlas->didSwapBuffers();
386 }
387
388 void CompositingCoordinator::purgeBackingStores()
389 {
390     TemporaryChange<bool> purgingToggle(m_isPurging, true);
391
392     for (auto& registeredLayer : m_registeredLayers.values())
393         registeredLayer->purgeBackingStores();
394
395     m_imageBackings.clear();
396     m_updateAtlases.clear();
397 }
398
399 bool CompositingCoordinator::paintToSurface(const IntSize& size, CoordinatedSurface::Flags flags, uint32_t& atlasID, IntPoint& offset, CoordinatedSurface::Client* client)
400 {
401     for (auto& updateAtlas : m_updateAtlases) {
402         UpdateAtlas* atlas = updateAtlas.get();
403         if (atlas->supportsAlpha() == (flags & CoordinatedSurface::SupportsAlpha)) {
404             // This will be false if there is no available buffer space.
405             if (atlas->paintOnAvailableBuffer(size, atlasID, offset, client))
406                 return true;
407         }
408     }
409
410     static const int ScratchBufferDimension = 1024; // Should be a power of two.
411     m_updateAtlases.append(std::make_unique<UpdateAtlas>(this, ScratchBufferDimension, flags));
412     scheduleReleaseInactiveAtlases();
413     return m_updateAtlases.last()->paintOnAvailableBuffer(size, atlasID, offset, client);
414 }
415
416 const double ReleaseInactiveAtlasesTimerInterval = 0.5;
417
418 void CompositingCoordinator::scheduleReleaseInactiveAtlases()
419 {
420     if (!m_releaseInactiveAtlasesTimer.isActive())
421         m_releaseInactiveAtlasesTimer.startRepeating(ReleaseInactiveAtlasesTimerInterval);
422 }
423
424 void CompositingCoordinator::releaseInactiveAtlasesTimerFired()
425 {
426     // We always want to keep one atlas for root contents layer.
427     std::unique_ptr<UpdateAtlas> atlasToKeepAnyway;
428     bool foundActiveAtlasForRootContentsLayer = false;
429     for (int i = m_updateAtlases.size() - 1;  i >= 0; --i) {
430         UpdateAtlas* atlas = m_updateAtlases[i].get();
431         if (!atlas->isInUse())
432             atlas->addTimeInactive(ReleaseInactiveAtlasesTimerInterval);
433         bool usableForRootContentsLayer = !atlas->supportsAlpha();
434         if (atlas->isInactive()) {
435             if (!foundActiveAtlasForRootContentsLayer && !atlasToKeepAnyway && usableForRootContentsLayer)
436                 atlasToKeepAnyway = WTFMove(m_updateAtlases[i]);
437             m_updateAtlases.remove(i);
438         } else if (usableForRootContentsLayer)
439             foundActiveAtlasForRootContentsLayer = true;
440     }
441
442     if (!foundActiveAtlasForRootContentsLayer && atlasToKeepAnyway)
443         m_updateAtlases.append(atlasToKeepAnyway.release());
444
445     m_updateAtlases.shrinkToFit();
446
447     if (m_updateAtlases.size() <= 1)
448         m_releaseInactiveAtlasesTimer.stop();
449 }
450
451 } // namespace WebCore
452
453 #endif // USE(COORDINATED_GRAPHICS)