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 | |
40 | namespace WebKit { |
41 | using namespace WebCore; |
42 | |
43 | static bool layerShouldHaveBackingStore(TextureMapperLayer* layer) |
44 | { |
45 | return layer->drawsContent() && layer->contentsAreVisible() && !layer->size().isEmpty(); |
46 | } |
47 | |
48 | CoordinatedGraphicsScene::CoordinatedGraphicsScene(CoordinatedGraphicsSceneClient* client) |
49 | : m_client(client) |
50 | { |
51 | } |
52 | |
53 | CoordinatedGraphicsScene::~CoordinatedGraphicsScene() = default; |
54 | |
55 | void 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 | |
68 | void 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 | |
93 | void CoordinatedGraphicsScene::updateViewport() |
94 | { |
95 | if (m_client) |
96 | m_client->updateViewport(); |
97 | } |
98 | |
99 | void CoordinatedGraphicsScene::onNewBufferAvailable() |
100 | { |
101 | updateViewport(); |
102 | } |
103 | |
104 | Nicosia::CompositionLayerTextureMapperImpl& compositionLayerImpl(Nicosia::CompositionLayer& compositionLayer) |
105 | { |
106 | return downcast<Nicosia::CompositionLayerTextureMapperImpl>(compositionLayer.impl()); |
107 | } |
108 | |
109 | Nicosia::ContentLayerTextureMapperImpl& contentLayerImpl(Nicosia::ContentLayer& contentLayer) |
110 | { |
111 | return downcast<Nicosia::ContentLayerTextureMapperImpl>(contentLayer.impl()); |
112 | } |
113 | |
114 | Nicosia::BackingStoreTextureMapperImpl& backingStoreImpl(Nicosia::BackingStore& backingStore) |
115 | { |
116 | return downcast<Nicosia::BackingStoreTextureMapperImpl>(backingStore.impl()); |
117 | } |
118 | |
119 | Nicosia::ImageBackingTextureMapperImpl& imageBackingImpl(Nicosia::ImageBacking& imageBacking) |
120 | { |
121 | return downcast<Nicosia::ImageBackingTextureMapperImpl>(imageBacking.impl()); |
122 | } |
123 | |
124 | TextureMapperLayer& 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 | |
134 | void 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 | |
161 | void 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 | |
185 | void 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 | |
208 | void CoordinatedGraphicsScene::commitSceneState(const CoordinatedGraphicsState::NicosiaState& state) |
209 | { |
210 | if (!m_client) |
211 | return; |
212 | |
213 | m_nicosia.scene = state.scene; |
214 | } |
215 | |
216 | void 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 | |
411 | void 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 | |
428 | void 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 | |
449 | void 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 | |