1/*
2 Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies)
3 Copyright (C) 2012 Company 100, Inc.
4 Copyright (C) 2017 Sony Interactive Entertainment Inc.
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public
8 License as published by the Free Software Foundation; either
9 version 2 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
15
16 You should have received a copy of the GNU Library General Public License
17 along with this library; see the file COPYING.LIB. If not, write to
18 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 Boston, MA 02110-1301, USA.
20*/
21
22#include "config.h"
23#include "CoordinatedGraphicsScene.h"
24
25#if USE(COORDINATED_GRAPHICS)
26
27#include <WebCore/CoordinatedBackingStore.h>
28#include <WebCore/NicosiaBackingStoreTextureMapperImpl.h>
29#include <WebCore/NicosiaBuffer.h>
30#include <WebCore/NicosiaCompositionLayerTextureMapperImpl.h>
31#include <WebCore/NicosiaContentLayerTextureMapperImpl.h>
32#include <WebCore/NicosiaImageBackingTextureMapperImpl.h>
33#include <WebCore/NicosiaScene.h>
34#include <WebCore/TextureMapper.h>
35#include <WebCore/TextureMapperBackingStore.h>
36#include <WebCore/TextureMapperGL.h>
37#include <WebCore/TextureMapperLayer.h>
38#include <wtf/Atomics.h>
39
40namespace WebKit {
41using namespace WebCore;
42
43static bool layerShouldHaveBackingStore(TextureMapperLayer* layer)
44{
45 return layer->drawsContent() && layer->contentsAreVisible() && !layer->size().isEmpty();
46}
47
48CoordinatedGraphicsScene::CoordinatedGraphicsScene(CoordinatedGraphicsSceneClient* client)
49 : m_client(client)
50{
51}
52
53CoordinatedGraphicsScene::~CoordinatedGraphicsScene() = default;
54
55void CoordinatedGraphicsScene::applyStateChanges(const Vector<CoordinatedGraphicsState>& states)
56{
57 if (!m_textureMapper) {
58 m_textureMapper = TextureMapper::create();
59 static_cast<TextureMapperGL*>(m_textureMapper.get())->setEnableEdgeDistanceAntialiasing(true);
60 }
61
62 ensureRootLayer();
63
64 for (auto& state : states)
65 commitSceneState(state.nicosia);
66}
67
68void CoordinatedGraphicsScene::paintToCurrentGLContext(const TransformationMatrix& matrix, const FloatRect& clipRect, TextureMapper::PaintFlags PaintFlags)
69{
70 updateSceneState();
71
72 TextureMapperLayer* currentRootLayer = rootLayer();
73 if (!currentRootLayer)
74 return;
75
76 currentRootLayer->setTextureMapper(m_textureMapper.get());
77 bool sceneHasRunningAnimations = currentRootLayer->applyAnimationsRecursively(MonotonicTime::now());
78 m_textureMapper->beginPainting(PaintFlags);
79 m_textureMapper->beginClip(TransformationMatrix(), clipRect);
80
81 if (currentRootLayer->transform() != matrix)
82 currentRootLayer->setTransform(matrix);
83
84 currentRootLayer->paint();
85 m_fpsCounter.updateFPSAndDisplay(*m_textureMapper, clipRect.location(), matrix);
86 m_textureMapper->endClip();
87 m_textureMapper->endPainting();
88
89 if (sceneHasRunningAnimations)
90 updateViewport();
91}
92
93void CoordinatedGraphicsScene::updateViewport()
94{
95 if (m_client)
96 m_client->updateViewport();
97}
98
99void CoordinatedGraphicsScene::onNewBufferAvailable()
100{
101 updateViewport();
102}
103
104Nicosia::CompositionLayerTextureMapperImpl& compositionLayerImpl(Nicosia::CompositionLayer& compositionLayer)
105{
106 return downcast<Nicosia::CompositionLayerTextureMapperImpl>(compositionLayer.impl());
107}
108
109Nicosia::ContentLayerTextureMapperImpl& contentLayerImpl(Nicosia::ContentLayer& contentLayer)
110{
111 return downcast<Nicosia::ContentLayerTextureMapperImpl>(contentLayer.impl());
112}
113
114Nicosia::BackingStoreTextureMapperImpl& backingStoreImpl(Nicosia::BackingStore& backingStore)
115{
116 return downcast<Nicosia::BackingStoreTextureMapperImpl>(backingStore.impl());
117}
118
119Nicosia::ImageBackingTextureMapperImpl& imageBackingImpl(Nicosia::ImageBacking& imageBacking)
120{
121 return downcast<Nicosia::ImageBackingTextureMapperImpl>(imageBacking.impl());
122}
123
124TextureMapperLayer& texmapLayer(Nicosia::CompositionLayer& compositionLayer)
125{
126 auto& compositionState = compositionLayerImpl(compositionLayer).compositionState();
127 if (!compositionState.layer) {
128 compositionState.layer = std::make_unique<TextureMapperLayer>();
129 compositionState.layer->setID(compositionLayer.id());
130 }
131 return *compositionState.layer;
132}
133
134void updateBackingStore(TextureMapperLayer& layer,
135 Nicosia::BackingStoreTextureMapperImpl::CompositionState& compositionState,
136 const Nicosia::BackingStoreTextureMapperImpl::TileUpdate& update)
137{
138 if (!layerShouldHaveBackingStore(&layer)) {
139 layer.setBackingStore(nullptr);
140 compositionState.backingStore = nullptr;
141 return;
142 }
143
144 if (!compositionState.backingStore)
145 compositionState.backingStore = CoordinatedBackingStore::create();
146 auto& backingStore = *compositionState.backingStore;
147
148 layer.setBackingStore(&backingStore);
149 backingStore.setSize(layer.size());
150
151 for (auto& tile : update.tilesToCreate)
152 backingStore.createTile(tile.tileID, tile.scale);
153 for (auto& tile : update.tilesToRemove)
154 backingStore.removeTile(tile.tileID);
155 for (auto& tile : update.tilesToUpdate) {
156 backingStore.updateTile(tile.tileID, tile.updateInfo.updateRect,
157 tile.tileRect, tile.updateInfo.buffer.copyRef(), { 0, 0 });
158 }
159}
160
161void updateImageBacking(TextureMapperLayer& layer,
162 Nicosia::ImageBackingTextureMapperImpl::CompositionState& compositionState,
163 Nicosia::ImageBackingTextureMapperImpl::Update& update)
164{
165 if (!update.isVisible) {
166 layer.setBackingStore(nullptr);
167 return;
168 }
169
170 if (!compositionState.backingStore)
171 compositionState.backingStore = CoordinatedBackingStore::create();
172 auto& backingStore = *compositionState.backingStore;
173 layer.setContentsLayer(&backingStore);
174
175 if (!update.buffer)
176 return;
177
178 backingStore.createTile(1, 1.0);
179 WebCore::IntRect rect { { }, update.buffer->size() };
180 ASSERT(2000 >= std::max(rect.width(), rect.height()));
181 backingStore.setSize(rect.size());
182 backingStore.updateTile(1, rect, rect, WTFMove(update.buffer), rect.location());
183}
184
185void removeLayer(Nicosia::CompositionLayer& layer)
186{
187 layer.accessCommitted(
188 [](const Nicosia::CompositionLayer::LayerState& committed)
189 {
190 if (committed.backingStore) {
191 auto& compositionState = backingStoreImpl(*committed.backingStore).compositionState();
192 compositionState.backingStore = nullptr;
193 }
194
195 if (committed.contentLayer)
196 contentLayerImpl(*committed.contentLayer).proxy().invalidate();
197
198 if (committed.imageBacking) {
199 auto& compositionState = imageBackingImpl(*committed.imageBacking).compositionState();
200 compositionState.backingStore = nullptr;
201 }
202 });
203
204 auto& compositionState = compositionLayerImpl(layer).compositionState();
205 compositionState.layer = nullptr;
206}
207
208void CoordinatedGraphicsScene::commitSceneState(const CoordinatedGraphicsState::NicosiaState& state)
209{
210 if (!m_client)
211 return;
212
213 m_nicosia.scene = state.scene;
214}
215
216void CoordinatedGraphicsScene::updateSceneState()
217{
218 if (!m_nicosia.scene)
219 return;
220
221 // Store layer and impl references along with the corresponding update
222 // for each type of possible layer backing.
223 struct {
224 struct BackingStore {
225 std::reference_wrapper<TextureMapperLayer> layer;
226 std::reference_wrapper<Nicosia::BackingStoreTextureMapperImpl> backingStore;
227 Nicosia::BackingStoreTextureMapperImpl::TileUpdate update;
228 };
229 Vector<BackingStore> backingStore;
230
231 struct ContentLayer {
232 std::reference_wrapper<TextureMapperLayer> layer;
233 std::reference_wrapper<TextureMapperPlatformLayerProxy> proxy;
234 bool needsActivation { false };
235 };
236 Vector<ContentLayer> contentLayer;
237
238 struct ImageBacking {
239 std::reference_wrapper<TextureMapperLayer> layer;
240 std::reference_wrapper<Nicosia::ImageBackingTextureMapperImpl> imageBacking;
241 Nicosia::ImageBackingTextureMapperImpl::Update update;
242 };
243 Vector<ImageBacking> imageBacking;
244 } layersByBacking;
245
246 // Access the scene state and perform state update for each layer.
247 m_nicosia.scene->accessState(
248 [this, &layersByBacking](Nicosia::Scene::State& state)
249 {
250 // FIXME: try to minimize the amount of work in case the Scene::State object
251 // didn't change (i.e. no layer flush was done), but don't forget to properly
252 // gather and update proxy objects for content layers.
253
254 // Handle the root layer, adding it to the TextureMapperLayer tree
255 // on the first update. No such change is expected later.
256 {
257 auto& rootLayer = texmapLayer(*state.rootLayer);
258 if (rootLayer.id() != m_rootLayerID) {
259 m_rootLayerID = rootLayer.id();
260 RELEASE_ASSERT(m_rootLayer->children().isEmpty());
261 m_rootLayer->addChild(&rootLayer);
262 }
263 }
264
265 // Gather all the to-be-removed layers so that composition-side state
266 // can be properly purged after the current state's set of layers is adopted.
267 HashSet<RefPtr<Nicosia::CompositionLayer>> removedLayers;
268 for (auto& layer : m_nicosia.state.layers) {
269 if (!state.layers.contains(layer))
270 removedLayers.add(layer);
271 }
272
273 m_nicosia.state = state;
274
275 for (auto& layer : removedLayers)
276 removeLayer(*layer);
277 removedLayers = { };
278
279 // Iterate the current state's set of layers, updating state values according to
280 // the incoming state changes. Layer backings are stored so that the updates
281 // (possibly time-consuming) can be done outside of this scene update.
282 for (auto& compositionLayer : m_nicosia.state.layers) {
283 auto& layer = texmapLayer(*compositionLayer);
284 compositionLayer->commitState(
285 [this, &layer, &compositionLayer, &layersByBacking]
286 (const Nicosia::CompositionLayer::LayerState& layerState)
287 {
288 if (layerState.delta.positionChanged)
289 layer.setPosition(layerState.position);
290 if (layerState.delta.anchorPointChanged)
291 layer.setAnchorPoint(layerState.anchorPoint);
292 if (layerState.delta.sizeChanged)
293 layer.setSize(layerState.size);
294
295 if (layerState.delta.transformChanged)
296 layer.setTransform(layerState.transform);
297 if (layerState.delta.childrenTransformChanged)
298 layer.setChildrenTransform(layerState.childrenTransform);
299
300 if (layerState.delta.contentsRectChanged)
301 layer.setContentsRect(layerState.contentsRect);
302 if (layerState.delta.contentsTilingChanged) {
303 layer.setContentsTilePhase(layerState.contentsTilePhase);
304 layer.setContentsTileSize(layerState.contentsTileSize);
305 }
306
307 if (layerState.delta.opacityChanged)
308 layer.setOpacity(layerState.opacity);
309 if (layerState.delta.solidColorChanged)
310 layer.setSolidColor(layerState.solidColor);
311
312 if (layerState.delta.filtersChanged)
313 layer.setFilters(layerState.filters);
314 if (layerState.delta.animationsChanged)
315 layer.setAnimations(layerState.animations);
316
317 if (layerState.delta.childrenChanged) {
318 layer.setChildren(WTF::map(layerState.children,
319 [](auto& child) { return &texmapLayer(*child); }));
320 }
321
322 if (layerState.delta.maskChanged)
323 layer.setMaskLayer(layerState.mask ? &texmapLayer(*layerState.mask) : nullptr);
324 if (layerState.delta.replicaChanged)
325 layer.setReplicaLayer(layerState.replica ? &texmapLayer(*layerState.replica) : nullptr);
326
327 if (layerState.delta.flagsChanged) {
328 layer.setContentsOpaque(layerState.flags.contentsOpaque);
329 layer.setDrawsContent(layerState.flags.drawsContent);
330 layer.setContentsVisible(layerState.flags.contentsVisible);
331 layer.setBackfaceVisibility(layerState.flags.backfaceVisible);
332 layer.setMasksToBounds(layerState.flags.masksToBounds);
333 layer.setPreserves3D(layerState.flags.preserves3D);
334 }
335
336 if (layerState.delta.repaintCounterChanged)
337 layer.setRepaintCounter(layerState.repaintCounter.visible, layerState.repaintCounter.count);
338
339 if (layerState.delta.debugBorderChanged)
340 layer.setDebugVisuals(layerState.debugBorder.visible, layerState.debugBorder.color, layerState.debugBorder.width);
341
342 if (layerState.backingStore) {
343 auto& impl = backingStoreImpl(*layerState.backingStore);
344 layersByBacking.backingStore.append(
345 { std::ref(layer), std::ref(impl), impl.takeUpdate() });
346 } else
347 layer.setBackingStore(nullptr);
348
349 if (layerState.contentLayer) {
350 auto& impl = contentLayerImpl(*layerState.contentLayer);
351 layersByBacking.contentLayer.append(
352 { std::ref(layer), std::ref(impl.proxy()), layerState.delta.contentLayerChanged });
353 } else if (layerState.imageBacking) {
354 auto& impl = imageBackingImpl(*layerState.imageBacking);
355 layersByBacking.imageBacking.append(
356 { std::ref(layer), std::ref(impl), impl.takeUpdate() });
357 } else
358 layer.setContentsLayer(nullptr);
359 });
360 }
361 });
362
363 // Iterate through each backing type of layers and gather backing store
364 // or proxy objects that need an update.
365 // FIXME: HashSet<std::reference_wrapper<>> would be ideal, but doesn't work (yet).
366 HashSet<Ref<WebCore::CoordinatedBackingStore>> backingStoresWithPendingBuffers;
367 HashSet<Ref<WebCore::TextureMapperPlatformLayerProxy>> proxiesForSwapping;
368
369 {
370 for (auto& entry : layersByBacking.backingStore) {
371 auto& compositionState = entry.backingStore.get().compositionState();
372 updateBackingStore(entry.layer.get(), compositionState, entry.update);
373
374 if (compositionState.backingStore)
375 backingStoresWithPendingBuffers.add(makeRef(*compositionState.backingStore));
376 }
377
378 layersByBacking.backingStore = { };
379 }
380
381 {
382 for (auto& entry : layersByBacking.contentLayer) {
383 auto& proxy = entry.proxy.get();
384 if (entry.needsActivation)
385 proxy.activateOnCompositingThread(this, &entry.layer.get());
386 proxiesForSwapping.add(makeRef(proxy));
387 }
388
389 layersByBacking.contentLayer = { };
390 }
391
392 {
393 for (auto& entry : layersByBacking.imageBacking) {
394 auto& compositionState = entry.imageBacking.get().compositionState();
395 updateImageBacking(entry.layer.get(), compositionState, entry.update);
396
397 if (compositionState.backingStore)
398 backingStoresWithPendingBuffers.add(makeRef(*compositionState.backingStore));
399 }
400
401 layersByBacking.imageBacking = { };
402 }
403
404 for (auto& backingStore : backingStoresWithPendingBuffers)
405 backingStore->commitTileOperations(*m_textureMapper);
406
407 for (auto& proxy : proxiesForSwapping)
408 proxy->swapBuffer();
409}
410
411void CoordinatedGraphicsScene::ensureRootLayer()
412{
413 if (m_rootLayer)
414 return;
415
416 m_rootLayer = std::make_unique<TextureMapperLayer>();
417 m_rootLayer->setMasksToBounds(false);
418 m_rootLayer->setDrawsContent(false);
419 m_rootLayer->setAnchorPoint(FloatPoint3D(0, 0, 0));
420
421 // The root layer should not have zero size, or it would be optimized out.
422 m_rootLayer->setSize(FloatSize(1.0, 1.0));
423
424 ASSERT(m_textureMapper);
425 m_rootLayer->setTextureMapper(m_textureMapper.get());
426}
427
428void CoordinatedGraphicsScene::purgeGLResources()
429{
430 ASSERT(!m_client);
431
432 if (m_nicosia.scene) {
433 m_nicosia.scene->accessState(
434 [](Nicosia::Scene::State& state)
435 {
436 for (auto& layer : state.layers)
437 removeLayer(*layer);
438 state.layers = { };
439 state.rootLayer = nullptr;
440 });
441 m_nicosia.scene = nullptr;
442 }
443
444 m_rootLayer = nullptr;
445 m_rootLayerID = 0;
446 m_textureMapper = nullptr;
447}
448
449void CoordinatedGraphicsScene::detach()
450{
451 ASSERT(RunLoop::isMain());
452 m_isActive = false;
453 m_client = nullptr;
454}
455
456} // namespace WebKit
457
458#endif // USE(COORDINATED_GRAPHICS)
459