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