788662e969f00c0d11f2875d4b8df2e320fddeb8
[WebKit-https.git] / Source / WebKit / WebProcess / WebPage / CoordinatedGraphics / 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 #include "CompositingCoordinator.h"
29
30 #if USE(COORDINATED_GRAPHICS)
31
32 #include <WebCore/DOMWindow.h>
33 #include <WebCore/Document.h>
34 #include <WebCore/FrameView.h>
35 #include <WebCore/GraphicsContext.h>
36 #include <WebCore/InspectorController.h>
37 #include <WebCore/MainFrame.h>
38 #include <WebCore/NicosiaPaintingEngine.h>
39 #include <WebCore/Page.h>
40 #include <wtf/MemoryPressureHandler.h>
41 #include <wtf/SetForScope.h>
42
43 #if USE(GLIB_EVENT_LOOP)
44 #include <wtf/glib/RunLoopSourcePriority.h>
45 #endif
46
47 namespace WebKit {
48 using namespace WebCore;
49
50 CompositingCoordinator::CompositingCoordinator(Page* page, CompositingCoordinator::Client& client)
51     : m_page(page)
52     , m_client(client)
53     , m_paintingEngine(Nicosia::PaintingEngine::create())
54     , m_releaseInactiveAtlasesTimer(RunLoop::main(), this, &CompositingCoordinator::releaseInactiveAtlasesTimerFired)
55 {
56 #if USE(GLIB_EVENT_LOOP)
57     m_releaseInactiveAtlasesTimer.setPriority(RunLoopSourcePriority::ReleaseUnusedResourcesTimer);
58 #endif
59 }
60
61 CompositingCoordinator::~CompositingCoordinator()
62 {
63     m_isDestructing = true;
64
65     purgeBackingStores();
66
67     for (auto& registeredLayer : m_registeredLayers.values())
68         registeredLayer->setCoordinator(nullptr);
69 }
70
71 void CompositingCoordinator::invalidate()
72 {
73     m_rootLayer = nullptr;
74     purgeBackingStores();
75 }
76
77 void CompositingCoordinator::setRootCompositingLayer(GraphicsLayer* graphicsLayer)
78 {
79     if (m_rootCompositingLayer == graphicsLayer)
80         return;
81
82     if (m_rootCompositingLayer)
83         m_rootCompositingLayer->removeFromParent();
84
85     m_rootCompositingLayer = graphicsLayer;
86     if (m_rootCompositingLayer)
87         m_rootLayer->addChildAtIndex(m_rootCompositingLayer, 0);
88 }
89
90 void CompositingCoordinator::setViewOverlayRootLayer(GraphicsLayer* graphicsLayer)
91 {
92     if (m_overlayCompositingLayer == graphicsLayer)
93         return;
94
95     if (m_overlayCompositingLayer)
96         m_overlayCompositingLayer->removeFromParent();
97
98     m_overlayCompositingLayer = graphicsLayer;
99     if (m_overlayCompositingLayer)
100         m_rootLayer->addChild(m_overlayCompositingLayer);
101 }
102
103 void CompositingCoordinator::sizeDidChange(const IntSize& newSize)
104 {
105     m_rootLayer->setSize(newSize);
106     notifyFlushRequired(m_rootLayer.get());
107 }
108
109 bool CompositingCoordinator::flushPendingLayerChanges()
110 {
111     SetForScope<bool> protector(m_isFlushingLayerChanges, true);
112
113     initializeRootCompositingLayerIfNeeded();
114
115     m_rootLayer->flushCompositingStateForThisLayerOnly();
116     m_client.didFlushRootLayer(m_visibleContentsRect);
117
118     if (m_overlayCompositingLayer)
119         m_overlayCompositingLayer->flushCompositingState(FloatRect(FloatPoint(), m_rootLayer->size()));
120
121     bool didSync = m_page->mainFrame().view()->flushCompositingStateIncludingSubframes();
122
123     auto& coordinatedLayer = downcast<CoordinatedGraphicsLayer>(*m_rootLayer);
124     coordinatedLayer.updateContentBuffersIncludingSubLayers();
125     coordinatedLayer.syncPendingStateChangesIncludingSubLayers();
126
127     flushPendingImageBackingChanges();
128
129     if (m_shouldSyncFrame) {
130         didSync = true;
131
132         if (m_rootCompositingLayer) {
133             m_state.contentsSize = roundedIntSize(m_rootCompositingLayer->size());
134             if (CoordinatedGraphicsLayer* contentsLayer = mainContentsLayer())
135                 m_state.coveredRect = contentsLayer->coverRect();
136         }
137         m_state.scrollPosition = m_visibleContentsRect.location();
138
139         m_client.commitSceneState(m_state);
140
141         if (!m_atlasesToRemove.isEmpty())
142             m_client.releaseUpdateAtlases(m_atlasesToRemove);
143         m_atlasesToRemove.clear();
144
145         clearPendingStateChanges();
146         m_shouldSyncFrame = false;
147     }
148
149     return didSync;
150 }
151
152 double CompositingCoordinator::timestamp() const
153 {
154     auto* document = m_page->mainFrame().document();
155     if (!document)
156         return 0;
157     return document->domWindow() ? document->domWindow()->nowTimestamp() : document->monotonicTimestamp();
158 }
159
160 void CompositingCoordinator::syncDisplayState()
161 {
162     m_page->mainFrame().view()->updateLayoutAndStyleIfNeededRecursive();
163 }
164
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
172 void CompositingCoordinator::clearPendingStateChanges()
173 {
174     m_state.layersToCreate.clear();
175     m_state.layersToUpdate.clear();
176     m_state.layersToRemove.clear();
177
178     m_state.imagesToCreate.clear();
179     m_state.imagesToRemove.clear();
180     m_state.imagesToUpdate.clear();
181     m_state.imagesToClear.clear();
182
183     m_state.updateAtlasesToCreate.clear();
184 }
185
186 void CompositingCoordinator::initializeRootCompositingLayerIfNeeded()
187 {
188     if (m_didInitializeRootCompositingLayer)
189         return;
190
191     m_state.rootCompositingLayer = downcast<CoordinatedGraphicsLayer>(*m_rootLayer).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 Ref<CoordinatedImageBacking> CompositingCoordinator::createImageBackingIfNeeded(Image& image)
215 {
216     CoordinatedImageBackingID imageID = CoordinatedImageBacking::getCoordinatedImageBackingID(image);
217     auto addResult = m_imageBackings.ensure(imageID, [this, &image] {
218         return CoordinatedImageBacking::create(*this, image);
219     });
220     return *addResult.iterator->value;
221 }
222
223 void CompositingCoordinator::createImageBacking(CoordinatedImageBackingID imageID)
224 {
225     m_state.imagesToCreate.append(imageID);
226 }
227
228 void CompositingCoordinator::updateImageBacking(CoordinatedImageBackingID imageID, RefPtr<Nicosia::Buffer>&& buffer)
229 {
230     m_shouldSyncFrame = true;
231     m_state.imagesToUpdate.append(std::make_pair(imageID, WTFMove(buffer)));
232 }
233
234 void CompositingCoordinator::clearImageBackingContents(CoordinatedImageBackingID imageID)
235 {
236     m_shouldSyncFrame = true;
237     m_state.imagesToClear.append(imageID);
238 }
239
240 void CompositingCoordinator::removeImageBacking(CoordinatedImageBackingID imageID)
241 {
242     if (m_isPurging)
243         return;
244
245     ASSERT(m_imageBackings.contains(imageID));
246     m_imageBackings.remove(imageID);
247
248     m_state.imagesToRemove.append(imageID);
249
250     size_t imageIDPosition = m_state.imagesToClear.find(imageID);
251     if (imageIDPosition != notFound)
252         m_state.imagesToClear.remove(imageIDPosition);
253 }
254
255 void CompositingCoordinator::flushPendingImageBackingChanges()
256 {
257     for (auto& imageBacking : m_imageBackings.values())
258         imageBacking->update();
259 }
260
261 void CompositingCoordinator::notifyFlushRequired(const GraphicsLayer*)
262 {
263     if (!m_isDestructing && !isFlushingLayerChanges())
264         m_client.notifyFlushRequired();
265 }
266
267 float CompositingCoordinator::deviceScaleFactor() const
268 {
269     return m_page->deviceScaleFactor();
270 }
271
272 float CompositingCoordinator::pageScaleFactor() const
273 {
274     return m_page->pageScaleFactor();
275 }
276
277 std::unique_ptr<GraphicsLayer> CompositingCoordinator::createGraphicsLayer(GraphicsLayer::Type layerType, GraphicsLayerClient& client)
278 {
279     CoordinatedGraphicsLayer* layer = new CoordinatedGraphicsLayer(layerType, client);
280     layer->setCoordinator(this);
281     m_registeredLayers.add(layer->id(), layer);
282     m_state.layersToCreate.append(layer->id());
283     layer->setNeedsVisibleRectAdjustment();
284     notifyFlushRequired(layer);
285     return std::unique_ptr<GraphicsLayer>(layer);
286 }
287
288 void CompositingCoordinator::createUpdateAtlas(UpdateAtlas::ID id, Ref<Nicosia::Buffer>&& buffer)
289 {
290     m_state.updateAtlasesToCreate.append(std::make_pair(id, WTFMove(buffer)));
291 }
292
293 void CompositingCoordinator::removeUpdateAtlas(UpdateAtlas::ID id)
294 {
295     if (m_isPurging)
296         return;
297     m_atlasesToRemove.append(id);
298 }
299
300 FloatRect CompositingCoordinator::visibleContentsRect() const
301 {
302     return m_visibleContentsRect;
303 }
304
305 CoordinatedGraphicsLayer* CompositingCoordinator::mainContentsLayer()
306 {
307     if (!is<CoordinatedGraphicsLayer>(m_rootCompositingLayer))
308         return nullptr;
309
310     return downcast<CoordinatedGraphicsLayer>(*m_rootCompositingLayer).findFirstDescendantWithContentsRecursively();
311 }
312
313 void CompositingCoordinator::setVisibleContentsRect(const FloatRect& rect, const FloatPoint& trajectoryVector)
314 {
315     // A zero trajectoryVector indicates that tiles all around the viewport are requested.
316     if (CoordinatedGraphicsLayer* contentsLayer = mainContentsLayer())
317         contentsLayer->setVisibleContentRectTrajectoryVector(trajectoryVector);
318
319     bool contentsRectDidChange = rect != m_visibleContentsRect;
320     if (contentsRectDidChange) {
321         m_visibleContentsRect = rect;
322
323         for (auto& registeredLayer : m_registeredLayers.values())
324             registeredLayer->setNeedsVisibleRectAdjustment();
325     }
326
327     FrameView* view = m_page->mainFrame().view();
328     if (view->useFixedLayout() && contentsRectDidChange) {
329         // Round the rect instead of enclosing it to make sure that its size stays
330         // the same while panning. This can have nasty effects on layout.
331         view->setFixedVisibleContentRect(roundedIntRect(rect));
332     }
333 }
334
335 void CompositingCoordinator::deviceOrPageScaleFactorChanged()
336 {
337     m_rootLayer->deviceOrPageScaleFactorChanged();
338 }
339
340 void CompositingCoordinator::detachLayer(CoordinatedGraphicsLayer* layer)
341 {
342     if (m_isPurging)
343         return;
344
345     m_registeredLayers.remove(layer->id());
346
347     size_t index = m_state.layersToCreate.find(layer->id());
348     if (index != notFound) {
349         m_state.layersToCreate.remove(index);
350         return;
351     }
352
353     m_state.layersToRemove.append(layer->id());
354     notifyFlushRequired(layer);
355 }
356
357 void CompositingCoordinator::renderNextFrame()
358 {
359     for (auto& atlas : m_updateAtlases)
360         atlas->didSwapBuffers();
361 }
362
363 void CompositingCoordinator::purgeBackingStores()
364 {
365     SetForScope<bool> purgingToggle(m_isPurging, true);
366
367     for (auto& registeredLayer : m_registeredLayers.values())
368         registeredLayer->purgeBackingStores();
369
370     m_imageBackings.clear();
371     m_updateAtlases.clear();
372 }
373
374 Ref<Nicosia::Buffer> CompositingCoordinator::getCoordinatedBuffer(const IntSize& size, Nicosia::Buffer::Flags flags, uint32_t& atlasID, IntRect& allocatedRect)
375 {
376     for (auto& atlas : m_updateAtlases) {
377         if (atlas->supportsAlpha() == (flags & Nicosia::Buffer::SupportsAlpha)) {
378             if (auto buffer = atlas->getCoordinatedBuffer(size, atlasID, allocatedRect))
379                 return *buffer;
380         }
381     }
382
383     static const IntSize s_atlasSize { 1024, 1024 }; // This should be a square.
384     m_updateAtlases.append(std::make_unique<UpdateAtlas>(*this, s_atlasSize, flags));
385     scheduleReleaseInactiveAtlases();
386
387     // Specified size should always fit into a newly-created UpdateAtlas and a non-null
388     // CoordinatedBuffer value should be returned from UpdateAtlas::getCoordinatedBuffer().
389     // We use a RELEASE_ASSERT() to stop any malfunctioning at the earliest point.
390     auto buffer = m_updateAtlases.last()->getCoordinatedBuffer(size, atlasID, allocatedRect);
391     RELEASE_ASSERT(buffer);
392     return *buffer;
393 }
394
395 Nicosia::PaintingEngine& CompositingCoordinator::paintingEngine()
396 {
397     return *m_paintingEngine;
398 }
399
400 const Seconds releaseInactiveAtlasesTimerInterval { 500_ms };
401
402 void CompositingCoordinator::scheduleReleaseInactiveAtlases()
403 {
404     if (!m_releaseInactiveAtlasesTimer.isActive())
405         m_releaseInactiveAtlasesTimer.startRepeating(releaseInactiveAtlasesTimerInterval);
406 }
407
408 void CompositingCoordinator::releaseInactiveAtlasesTimerFired()
409 {
410     releaseAtlases(MemoryPressureHandler::singleton().isUnderMemoryPressure() ? ReleaseUnused : ReleaseInactive);
411 }
412
413 void CompositingCoordinator::releaseAtlases(ReleaseAtlasPolicy policy)
414 {
415     // We always want to keep one atlas for root contents layer.
416     std::unique_ptr<UpdateAtlas> atlasToKeepAnyway;
417     bool foundActiveAtlasForRootContentsLayer = false;
418     for (int i = m_updateAtlases.size() - 1;  i >= 0; --i) {
419         UpdateAtlas* atlas = m_updateAtlases[i].get();
420         bool inUse = atlas->isInUse();
421         if (!inUse)
422             atlas->addTimeInactive(releaseInactiveAtlasesTimerInterval.value());
423         bool usableForRootContentsLayer = !atlas->supportsAlpha();
424         if (atlas->isInactive() || (!inUse && policy == ReleaseUnused)) {
425             if (!foundActiveAtlasForRootContentsLayer && !atlasToKeepAnyway && usableForRootContentsLayer)
426                 atlasToKeepAnyway = WTFMove(m_updateAtlases[i]);
427             m_updateAtlases.remove(i);
428         } else if (usableForRootContentsLayer)
429             foundActiveAtlasForRootContentsLayer = true;
430     }
431
432     if (!foundActiveAtlasForRootContentsLayer && atlasToKeepAnyway)
433         m_updateAtlases.append(atlasToKeepAnyway.release());
434
435     m_updateAtlases.shrinkToFit();
436
437     if (m_updateAtlases.size() <= 1)
438         m_releaseInactiveAtlasesTimer.stop();
439
440     if (!m_atlasesToRemove.isEmpty())
441         m_client.releaseUpdateAtlases(m_atlasesToRemove);
442     m_atlasesToRemove.clear();
443 }
444
445 void CompositingCoordinator::clearUpdateAtlases()
446 {
447     if (m_isPurging)
448         return;
449
450     m_releaseInactiveAtlasesTimer.stop();
451     m_updateAtlases.clear();
452
453     if (!m_atlasesToRemove.isEmpty())
454         m_client.releaseUpdateAtlases(m_atlasesToRemove);
455     m_atlasesToRemove.clear();
456 }
457
458 } // namespace WebKit
459
460 #endif // USE(COORDINATED_GRAPHICS)