OLD | NEW |
| (Empty) |
1 // Copyright 2011 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "config.h" | |
6 | |
7 #if USE(ACCELERATED_COMPOSITING) | |
8 | |
9 #include "base/basictypes.h" | |
10 #include "TiledLayerChromium.h" | |
11 | |
12 #include "CCLayerImpl.h" | |
13 #include "CCLayerTreeHost.h" | |
14 #include "CCOverdrawMetrics.h" | |
15 #include "CCTextureUpdateQueue.h" | |
16 #include "CCTiledLayerImpl.h" | |
17 #include "GraphicsContext3D.h" | |
18 #include "Region.h" | |
19 #include <wtf/CurrentTime.h> | |
20 #include <wtf/MathExtras.h> | |
21 | |
22 using namespace std; | |
23 using WebKit::WebTransformationMatrix; | |
24 | |
25 namespace cc { | |
26 | |
27 class UpdatableTile : public CCLayerTilingData::Tile { | |
28 public: | |
29 static PassOwnPtr<UpdatableTile> create(PassOwnPtr<LayerTextureUpdater::Text
ure> texture) | |
30 { | |
31 return adoptPtr(new UpdatableTile(texture)); | |
32 } | |
33 | |
34 LayerTextureUpdater::Texture* texture() { return m_texture.get(); } | |
35 CCPrioritizedTexture* managedTexture() { return m_texture->texture(); } | |
36 | |
37 bool isDirty() const { return !dirtyRect.isEmpty(); } | |
38 | |
39 // Reset update state for the current frame. This should occur before painti
ng | |
40 // for all layers. Since painting one layer can invalidate another layer | |
41 // after it has already painted, mark all non-dirty tiles as valid before pa
inting | |
42 // such that invalidations during painting won't prevent them from being pus
hed. | |
43 void resetUpdateState() | |
44 { | |
45 updateRect = IntRect(); | |
46 occluded = false; | |
47 partialUpdate = false; | |
48 validForFrame = !isDirty(); | |
49 } | |
50 | |
51 // This promises to update the tile and therefore also guarantees the tile | |
52 // will be valid for this frame. dirtyRect is copied into updateRect so | |
53 // we can continue to track re-entrant invalidations that occur during paint
ing. | |
54 void markForUpdate() | |
55 { | |
56 validForFrame = true; | |
57 updateRect = dirtyRect; | |
58 dirtyRect = IntRect(); | |
59 } | |
60 | |
61 IntRect dirtyRect; | |
62 IntRect updateRect; | |
63 bool partialUpdate; | |
64 bool validForFrame; | |
65 bool occluded; | |
66 bool isInUseOnImpl; | |
67 private: | |
68 explicit UpdatableTile(PassOwnPtr<LayerTextureUpdater::Texture> texture) | |
69 : partialUpdate(false) | |
70 , validForFrame(false) | |
71 , occluded(false) | |
72 , isInUseOnImpl(false) | |
73 , m_texture(texture) | |
74 { | |
75 } | |
76 | |
77 OwnPtr<LayerTextureUpdater::Texture> m_texture; | |
78 | |
79 DISALLOW_COPY_AND_ASSIGN(UpdatableTile); | |
80 }; | |
81 | |
82 TiledLayerChromium::TiledLayerChromium() | |
83 : LayerChromium() | |
84 , m_textureFormat(GraphicsContext3D::INVALID_ENUM) | |
85 , m_skipsDraw(false) | |
86 , m_failedUpdate(false) | |
87 , m_sampledTexelFormat(LayerTextureUpdater::SampledTexelFormatInvalid) | |
88 , m_tilingOption(AutoTile) | |
89 { | |
90 m_tiler = CCLayerTilingData::create(IntSize(), CCLayerTilingData::HasBorderT
exels); | |
91 } | |
92 | |
93 TiledLayerChromium::~TiledLayerChromium() | |
94 { | |
95 } | |
96 | |
97 scoped_ptr<CCLayerImpl> TiledLayerChromium::createCCLayerImpl() | |
98 { | |
99 return CCTiledLayerImpl::create(id()).PassAs<CCLayerImpl>(); | |
100 } | |
101 | |
102 void TiledLayerChromium::updateTileSizeAndTilingOption() | |
103 { | |
104 ASSERT(layerTreeHost()); | |
105 | |
106 const IntSize& defaultTileSize = layerTreeHost()->settings().defaultTileSize
; | |
107 const IntSize& maxUntiledLayerSize = layerTreeHost()->settings().maxUntiledL
ayerSize; | |
108 int layerWidth = contentBounds().width(); | |
109 int layerHeight = contentBounds().height(); | |
110 | |
111 const IntSize tileSize(min(defaultTileSize.width(), layerWidth), min(default
TileSize.height(), layerHeight)); | |
112 | |
113 // Tile if both dimensions large, or any one dimension large and the other | |
114 // extends into a second tile but the total layer area isn't larger than tha
t | |
115 // of the largest possible untiled layer. This heuristic allows for long ski
nny layers | |
116 // (e.g. scrollbars) that are Nx1 tiles to minimize wasted texture space but
still avoids | |
117 // creating very large tiles. | |
118 const bool anyDimensionLarge = layerWidth > maxUntiledLayerSize.width() || l
ayerHeight > maxUntiledLayerSize.height(); | |
119 const bool anyDimensionOneTile = (layerWidth <= defaultTileSize.width() || l
ayerHeight <= defaultTileSize.height()) | |
120 && (layerWidth * layerHeight) <= (maxUntil
edLayerSize.width() * maxUntiledLayerSize.height()); | |
121 const bool autoTiled = anyDimensionLarge && !anyDimensionOneTile; | |
122 | |
123 bool isTiled; | |
124 if (m_tilingOption == AlwaysTile) | |
125 isTiled = true; | |
126 else if (m_tilingOption == NeverTile) | |
127 isTiled = false; | |
128 else | |
129 isTiled = autoTiled; | |
130 | |
131 IntSize requestedSize = isTiled ? tileSize : contentBounds(); | |
132 const int maxSize = layerTreeHost()->rendererCapabilities().maxTextureSize; | |
133 IntSize clampedSize = requestedSize.shrunkTo(IntSize(maxSize, maxSize)); | |
134 setTileSize(clampedSize); | |
135 } | |
136 | |
137 void TiledLayerChromium::updateBounds() | |
138 { | |
139 IntSize oldBounds = m_tiler->bounds(); | |
140 IntSize newBounds = contentBounds(); | |
141 if (oldBounds == newBounds) | |
142 return; | |
143 m_tiler->setBounds(newBounds); | |
144 | |
145 // Invalidate any areas that the new bounds exposes. | |
146 Region oldRegion(IntRect(IntPoint(), oldBounds)); | |
147 Region newRegion(IntRect(IntPoint(), newBounds)); | |
148 newRegion.subtract(oldRegion); | |
149 Vector<WebCore::IntRect> rects = newRegion.rects(); | |
150 for (size_t i = 0; i < rects.size(); ++i) | |
151 invalidateContentRect(rects[i]); | |
152 } | |
153 | |
154 void TiledLayerChromium::setTileSize(const IntSize& size) | |
155 { | |
156 m_tiler->setTileSize(size); | |
157 } | |
158 | |
159 void TiledLayerChromium::setBorderTexelOption(CCLayerTilingData::BorderTexelOpti
on borderTexelOption) | |
160 { | |
161 m_tiler->setBorderTexelOption(borderTexelOption); | |
162 } | |
163 | |
164 bool TiledLayerChromium::drawsContent() const | |
165 { | |
166 if (!LayerChromium::drawsContent()) | |
167 return false; | |
168 | |
169 bool hasMoreThanOneTile = m_tiler->numTilesX() > 1 || m_tiler->numTilesY() >
1; | |
170 if (m_tilingOption == NeverTile && hasMoreThanOneTile) | |
171 return false; | |
172 | |
173 return true; | |
174 } | |
175 | |
176 bool TiledLayerChromium::needsContentsScale() const | |
177 { | |
178 return true; | |
179 } | |
180 | |
181 IntSize TiledLayerChromium::contentBounds() const | |
182 { | |
183 return IntSize(lroundf(bounds().width() * contentsScale()), lroundf(bounds()
.height() * contentsScale())); | |
184 } | |
185 | |
186 void TiledLayerChromium::setTilingOption(TilingOption tilingOption) | |
187 { | |
188 m_tilingOption = tilingOption; | |
189 } | |
190 | |
191 void TiledLayerChromium::setIsMask(bool isMask) | |
192 { | |
193 setTilingOption(isMask ? NeverTile : AutoTile); | |
194 } | |
195 | |
196 void TiledLayerChromium::pushPropertiesTo(CCLayerImpl* layer) | |
197 { | |
198 LayerChromium::pushPropertiesTo(layer); | |
199 | |
200 CCTiledLayerImpl* tiledLayer = static_cast<CCTiledLayerImpl*>(layer); | |
201 | |
202 tiledLayer->setSkipsDraw(m_skipsDraw); | |
203 tiledLayer->setContentsSwizzled(m_sampledTexelFormat != LayerTextureUpdater:
:SampledTexelFormatRGBA); | |
204 tiledLayer->setTilingData(*m_tiler); | |
205 Vector<UpdatableTile*> invalidTiles; | |
206 | |
207 for (CCLayerTilingData::TileMap::const_iterator iter = m_tiler->tiles().begi
n(); iter != m_tiler->tiles().end(); ++iter) { | |
208 #if WTF_NEW_HASHMAP_ITERATORS_INTERFACE | |
209 int i = iter->key.first; | |
210 int j = iter->key.second; | |
211 UpdatableTile* tile = static_cast<UpdatableTile*>(iter->value.get()); | |
212 #else | |
213 int i = iter->first.first; | |
214 int j = iter->first.second; | |
215 UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second.get()); | |
216 #endif | |
217 // FIXME: This should not ever be null. | |
218 if (!tile) | |
219 continue; | |
220 | |
221 tile->isInUseOnImpl = false; | |
222 | |
223 if (!tile->managedTexture()->haveBackingTexture()) { | |
224 // Evicted tiles get deleted from both layers | |
225 invalidTiles.append(tile); | |
226 continue; | |
227 } | |
228 | |
229 if (!tile->validForFrame) { | |
230 // Invalidated tiles are set so they can get different debug colors. | |
231 tiledLayer->pushInvalidTile(i, j); | |
232 continue; | |
233 } | |
234 | |
235 tiledLayer->pushTileProperties(i, j, tile->managedTexture()->resourceId(
), tile->opaqueRect()); | |
236 tile->isInUseOnImpl = true; | |
237 } | |
238 for (Vector<UpdatableTile*>::const_iterator iter = invalidTiles.begin(); ite
r != invalidTiles.end(); ++iter) | |
239 m_tiler->takeTile((*iter)->i(), (*iter)->j()); | |
240 } | |
241 | |
242 CCPrioritizedTextureManager* TiledLayerChromium::textureManager() const | |
243 { | |
244 if (!layerTreeHost()) | |
245 return 0; | |
246 return layerTreeHost()->contentsTextureManager(); | |
247 } | |
248 | |
249 void TiledLayerChromium::setLayerTreeHost(CCLayerTreeHost* host) | |
250 { | |
251 if (host && host != layerTreeHost()) { | |
252 for (CCLayerTilingData::TileMap::const_iterator iter = m_tiler->tiles().
begin(); iter != m_tiler->tiles().end(); ++iter) { | |
253 #if WTF_NEW_HASHMAP_ITERATORS_INTERFACE | |
254 UpdatableTile* tile = static_cast<UpdatableTile*>(iter->value.get())
; | |
255 #else | |
256 UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second.get()
); | |
257 #endif | |
258 // FIXME: This should not ever be null. | |
259 if (!tile) | |
260 continue; | |
261 tile->managedTexture()->setTextureManager(host->contentsTextureManag
er()); | |
262 } | |
263 } | |
264 LayerChromium::setLayerTreeHost(host); | |
265 } | |
266 | |
267 UpdatableTile* TiledLayerChromium::tileAt(int i, int j) const | |
268 { | |
269 return static_cast<UpdatableTile*>(m_tiler->tileAt(i, j)); | |
270 } | |
271 | |
272 UpdatableTile* TiledLayerChromium::createTile(int i, int j) | |
273 { | |
274 createTextureUpdaterIfNeeded(); | |
275 | |
276 OwnPtr<UpdatableTile> tile(UpdatableTile::create(textureUpdater()->createTex
ture(textureManager()))); | |
277 tile->managedTexture()->setDimensions(m_tiler->tileSize(), m_textureFormat); | |
278 | |
279 UpdatableTile* addedTile = tile.get(); | |
280 m_tiler->addTile(tile.release(), i, j); | |
281 | |
282 addedTile->dirtyRect = m_tiler->tileRect(addedTile); | |
283 | |
284 // Temporary diagnostic crash. | |
285 if (!addedTile) | |
286 CRASH(); | |
287 if (!tileAt(i, j)) | |
288 CRASH(); | |
289 | |
290 return addedTile; | |
291 } | |
292 | |
293 void TiledLayerChromium::setNeedsDisplayRect(const FloatRect& dirtyRect) | |
294 { | |
295 float contentsWidthScale = static_cast<float>(contentBounds().width()) / bou
nds().width(); | |
296 float contentsHeightScale = static_cast<float>(contentBounds().height()) / b
ounds().height(); | |
297 FloatRect scaledDirtyRect(dirtyRect); | |
298 scaledDirtyRect.scale(contentsWidthScale, contentsHeightScale); | |
299 IntRect dirty = enclosingIntRect(scaledDirtyRect); | |
300 invalidateContentRect(dirty); | |
301 LayerChromium::setNeedsDisplayRect(dirtyRect); | |
302 } | |
303 | |
304 void TiledLayerChromium::setUseLCDText(bool useLCDText) | |
305 { | |
306 LayerChromium::setUseLCDText(useLCDText); | |
307 | |
308 CCLayerTilingData::BorderTexelOption borderTexelOption; | |
309 #if OS(ANDROID) | |
310 // Always want border texels and GL_LINEAR due to pinch zoom. | |
311 borderTexelOption = CCLayerTilingData::HasBorderTexels; | |
312 #else | |
313 borderTexelOption = useLCDText ? CCLayerTilingData::NoBorderTexels : CCLayer
TilingData::HasBorderTexels; | |
314 #endif | |
315 setBorderTexelOption(borderTexelOption); | |
316 } | |
317 | |
318 void TiledLayerChromium::invalidateContentRect(const IntRect& contentRect) | |
319 { | |
320 updateBounds(); | |
321 if (m_tiler->isEmpty() || contentRect.isEmpty() || m_skipsDraw) | |
322 return; | |
323 | |
324 for (CCLayerTilingData::TileMap::const_iterator iter = m_tiler->tiles().begi
n(); iter != m_tiler->tiles().end(); ++iter) { | |
325 #if WTF_NEW_HASHMAP_ITERATORS_INTERFACE | |
326 UpdatableTile* tile = static_cast<UpdatableTile*>(iter->value.get()); | |
327 #else | |
328 UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second.get()); | |
329 #endif | |
330 ASSERT(tile); | |
331 // FIXME: This should not ever be null. | |
332 if (!tile) | |
333 continue; | |
334 IntRect bound = m_tiler->tileRect(tile); | |
335 bound.intersect(contentRect); | |
336 tile->dirtyRect.unite(bound); | |
337 } | |
338 } | |
339 | |
340 // Returns true if tile is dirty and only part of it needs to be updated. | |
341 bool TiledLayerChromium::tileOnlyNeedsPartialUpdate(UpdatableTile* tile) | |
342 { | |
343 return !tile->dirtyRect.contains(m_tiler->tileRect(tile)); | |
344 } | |
345 | |
346 // Dirty tiles with valid textures needs buffered update to guarantee that | |
347 // we don't modify textures currently used for drawing by the impl thread. | |
348 bool TiledLayerChromium::tileNeedsBufferedUpdate(UpdatableTile* tile) | |
349 { | |
350 if (!tile->managedTexture()->haveBackingTexture()) | |
351 return false; | |
352 | |
353 if (!tile->isDirty()) | |
354 return false; | |
355 | |
356 if (!tile->isInUseOnImpl) | |
357 return false; | |
358 | |
359 return true; | |
360 } | |
361 | |
362 | |
363 bool TiledLayerChromium::updateTiles(int left, int top, int right, int bottom, C
CTextureUpdateQueue& queue, const CCOcclusionTracker* occlusion, CCRenderingStat
s& stats, bool& didPaint) | |
364 { | |
365 didPaint = false; | |
366 createTextureUpdaterIfNeeded(); | |
367 | |
368 bool ignoreOcclusions = !occlusion; | |
369 if (!haveTexturesForTiles(left, top, right, bottom, ignoreOcclusions)) { | |
370 m_failedUpdate = true; | |
371 return false; | |
372 } | |
373 | |
374 IntRect paintRect = markTilesForUpdate(left, top, right, bottom, ignoreOcclu
sions); | |
375 | |
376 if (occlusion) | |
377 occlusion->overdrawMetrics().didPaint(paintRect); | |
378 | |
379 if (paintRect.isEmpty()) | |
380 return true; | |
381 | |
382 didPaint = true; | |
383 updateTileTextures(paintRect, left, top, right, bottom, queue, occlusion, st
ats); | |
384 return true; | |
385 } | |
386 | |
387 void TiledLayerChromium::markOcclusionsAndRequestTextures(int left, int top, int
right, int bottom, const CCOcclusionTracker* occlusion) | |
388 { | |
389 // There is some difficult dependancies between occlusions, recording occlus
ion metrics | |
390 // and requesting memory so those are encapsulated in this function: | |
391 // - We only want to call requestLate on unoccluded textures (to preserve | |
392 // memory for other layers when near OOM). | |
393 // - We only want to record occlusion metrics if all memory requests succeed
. | |
394 | |
395 int occludedTileCount = 0; | |
396 bool succeeded = true; | |
397 for (int j = top; j <= bottom; ++j) { | |
398 for (int i = left; i <= right; ++i) { | |
399 UpdatableTile* tile = tileAt(i, j); | |
400 ASSERT(tile); // Did setTexturePriorities get skipped? | |
401 // FIXME: This should not ever be null. | |
402 if (!tile) | |
403 continue; | |
404 ASSERT(!tile->occluded); // Did resetUpdateState get skipped? Are we
doing more than one occlusion pass? | |
405 IntRect visibleTileRect = intersection(m_tiler->tileBounds(i, j), vi
sibleContentRect()); | |
406 if (occlusion && occlusion->occluded(this, visibleTileRect)) { | |
407 tile->occluded = true; | |
408 occludedTileCount++; | |
409 } else { | |
410 succeeded &= tile->managedTexture()->requestLate(); | |
411 } | |
412 } | |
413 } | |
414 | |
415 if (!succeeded) | |
416 return; | |
417 | |
418 // FIXME: Remove the loop and just pass the count! | |
419 for (int i = 0; i < occludedTileCount; i++) | |
420 occlusion->overdrawMetrics().didCullTileForUpload(); | |
421 } | |
422 | |
423 bool TiledLayerChromium::haveTexturesForTiles(int left, int top, int right, int
bottom, bool ignoreOcclusions) | |
424 { | |
425 for (int j = top; j <= bottom; ++j) { | |
426 for (int i = left; i <= right; ++i) { | |
427 UpdatableTile* tile = tileAt(i, j); | |
428 ASSERT(tile); // Did setTexturePriorites get skipped? | |
429 // FIXME: This should not ever be null. | |
430 if (!tile) | |
431 continue; | |
432 | |
433 // Ensure the entire tile is dirty if we don't have the texture. | |
434 if (!tile->managedTexture()->haveBackingTexture()) | |
435 tile->dirtyRect = m_tiler->tileRect(tile); | |
436 | |
437 // If using occlusion and the visible region of the tile is occluded
, | |
438 // don't reserve a texture or update the tile. | |
439 if (tile->occluded && !ignoreOcclusions) | |
440 continue; | |
441 | |
442 if (!tile->managedTexture()->canAcquireBackingTexture()) | |
443 return false; | |
444 } | |
445 } | |
446 return true; | |
447 } | |
448 | |
449 IntRect TiledLayerChromium::markTilesForUpdate(int left, int top, int right, int
bottom, bool ignoreOcclusions) | |
450 { | |
451 IntRect paintRect; | |
452 for (int j = top; j <= bottom; ++j) { | |
453 for (int i = left; i <= right; ++i) { | |
454 UpdatableTile* tile = tileAt(i, j); | |
455 ASSERT(tile); // Did setTexturePriorites get skipped? | |
456 // FIXME: This should not ever be null. | |
457 if (!tile) | |
458 continue; | |
459 if (tile->occluded && !ignoreOcclusions) | |
460 continue; | |
461 paintRect.unite(tile->dirtyRect); | |
462 tile->markForUpdate(); | |
463 } | |
464 } | |
465 return paintRect; | |
466 } | |
467 | |
468 void TiledLayerChromium::updateTileTextures(const IntRect& paintRect, int left,
int top, int right, int bottom, CCTextureUpdateQueue& queue, const CCOcclusionTr
acker* occlusion, CCRenderingStats& stats) | |
469 { | |
470 // The updateRect should be in layer space. So we have to convert the paintR
ect from content space to layer space. | |
471 m_updateRect = FloatRect(paintRect); | |
472 float widthScale = bounds().width() / static_cast<float>(contentBounds().wid
th()); | |
473 float heightScale = bounds().height() / static_cast<float>(contentBounds().h
eight()); | |
474 m_updateRect.scale(widthScale, heightScale); | |
475 | |
476 // Calling prepareToUpdate() calls into WebKit to paint, which may have the
side | |
477 // effect of disabling compositing, which causes our reference to the textur
e updater to be deleted. | |
478 // However, we can't free the memory backing the SkCanvas until the paint fi
nishes, | |
479 // so we grab a local reference here to hold the updater alive until the pai
nt completes. | |
480 RefPtr<LayerTextureUpdater> protector(textureUpdater()); | |
481 IntRect paintedOpaqueRect; | |
482 textureUpdater()->prepareToUpdate(paintRect, m_tiler->tileSize(), 1 / widthS
cale, 1 / heightScale, paintedOpaqueRect, stats); | |
483 | |
484 for (int j = top; j <= bottom; ++j) { | |
485 for (int i = left; i <= right; ++i) { | |
486 UpdatableTile* tile = tileAt(i, j); | |
487 ASSERT(tile); // Did setTexturePriorites get skipped? | |
488 // FIXME: This should not ever be null. | |
489 if (!tile) | |
490 continue; | |
491 | |
492 IntRect tileRect = m_tiler->tileBounds(i, j); | |
493 | |
494 // Use updateRect as the above loop copied the dirty rect for this f
rame to updateRect. | |
495 const IntRect& dirtyRect = tile->updateRect; | |
496 if (dirtyRect.isEmpty()) | |
497 continue; | |
498 | |
499 // Save what was painted opaque in the tile. Keep the old area if th
e paint didn't touch it, and didn't paint some | |
500 // other part of the tile opaque. | |
501 IntRect tilePaintedRect = intersection(tileRect, paintRect); | |
502 IntRect tilePaintedOpaqueRect = intersection(tileRect, paintedOpaque
Rect); | |
503 if (!tilePaintedRect.isEmpty()) { | |
504 IntRect paintInsideTileOpaqueRect = intersection(tile->opaqueRec
t(), tilePaintedRect); | |
505 bool paintInsideTileOpaqueRectIsNonOpaque = !tilePaintedOpaqueRe
ct.contains(paintInsideTileOpaqueRect); | |
506 bool opaquePaintNotInsideTileOpaqueRect = !tilePaintedOpaqueRect
.isEmpty() && !tile->opaqueRect().contains(tilePaintedOpaqueRect); | |
507 | |
508 if (paintInsideTileOpaqueRectIsNonOpaque || opaquePaintNotInside
TileOpaqueRect) | |
509 tile->setOpaqueRect(tilePaintedOpaqueRect); | |
510 } | |
511 | |
512 // sourceRect starts as a full-sized tile with border texels include
d. | |
513 IntRect sourceRect = m_tiler->tileRect(tile); | |
514 sourceRect.intersect(dirtyRect); | |
515 // Paint rect not guaranteed to line up on tile boundaries, so | |
516 // make sure that sourceRect doesn't extend outside of it. | |
517 sourceRect.intersect(paintRect); | |
518 | |
519 tile->updateRect = sourceRect; | |
520 | |
521 if (sourceRect.isEmpty()) | |
522 continue; | |
523 | |
524 tile->texture()->prepareRect(sourceRect, stats); | |
525 if (occlusion) | |
526 occlusion->overdrawMetrics().didUpload(WebTransformationMatrix()
, sourceRect, tile->opaqueRect()); | |
527 | |
528 const IntPoint anchor = m_tiler->tileRect(tile).location(); | |
529 | |
530 // Calculate tile-space rectangle to upload into. | |
531 IntSize destOffset(sourceRect.x() - anchor.x(), sourceRect.y() - anc
hor.y()); | |
532 if (destOffset.width() < 0) | |
533 CRASH(); | |
534 if (destOffset.height() < 0) | |
535 CRASH(); | |
536 | |
537 // Offset from paint rectangle to this tile's dirty rectangle. | |
538 IntPoint paintOffset(sourceRect.x() - paintRect.x(), sourceRect.y()
- paintRect.y()); | |
539 if (paintOffset.x() < 0) | |
540 CRASH(); | |
541 if (paintOffset.y() < 0) | |
542 CRASH(); | |
543 if (paintOffset.x() + sourceRect.width() > paintRect.width()) | |
544 CRASH(); | |
545 if (paintOffset.y() + sourceRect.height() > paintRect.height()) | |
546 CRASH(); | |
547 | |
548 TextureUploader::Parameters upload = { tile->texture(), sourceRect,
destOffset }; | |
549 if (tile->partialUpdate) | |
550 queue.appendPartialUpload(upload); | |
551 else | |
552 queue.appendFullUpload(upload); | |
553 } | |
554 } | |
555 } | |
556 | |
557 namespace { | |
558 // This picks a small animated layer to be anything less than one viewport. This | |
559 // is specifically for page transitions which are viewport-sized layers. The ext
ra | |
560 // 64 pixels is due to these layers being slightly larger than the viewport in s
ome cases. | |
561 bool isSmallAnimatedLayer(TiledLayerChromium* layer) | |
562 { | |
563 if (!layer->drawTransformIsAnimating() && !layer->screenSpaceTransformIsAnim
ating()) | |
564 return false; | |
565 IntSize viewportSize = layer->layerTreeHost() ? layer->layerTreeHost()->devi
ceViewportSize() : IntSize(); | |
566 IntRect contentRect(IntPoint::zero(), layer->contentBounds()); | |
567 return contentRect.width() <= viewportSize.width() + 64 | |
568 && contentRect.height() <= viewportSize.height() + 64; | |
569 } | |
570 | |
571 // FIXME: Remove this and make this based on distance once distance can be calcu
lated | |
572 // for offscreen layers. For now, prioritize all small animated layers after 512 | |
573 // pixels of pre-painting. | |
574 void setPriorityForTexture(const IntRect& visibleRect, | |
575 const IntRect& tileRect, | |
576 bool drawsToRoot, | |
577 bool isSmallAnimatedLayer, | |
578 CCPrioritizedTexture* texture) | |
579 { | |
580 int priority = CCPriorityCalculator::lowestPriority(); | |
581 if (!visibleRect.isEmpty()) | |
582 priority = CCPriorityCalculator::priorityFromDistance(visibleRect, tileR
ect, drawsToRoot); | |
583 if (isSmallAnimatedLayer) | |
584 priority = CCPriorityCalculator::maxPriority(priority, CCPriorityCalcula
tor::smallAnimatedLayerMinPriority()); | |
585 if (priority != CCPriorityCalculator::lowestPriority()) | |
586 texture->setRequestPriority(priority); | |
587 } | |
588 } | |
589 | |
590 void TiledLayerChromium::setTexturePriorities(const CCPriorityCalculator& priori
tyCalc) | |
591 { | |
592 updateBounds(); | |
593 resetUpdateState(); | |
594 | |
595 if (m_tiler->hasEmptyBounds()) | |
596 return; | |
597 | |
598 bool drawsToRoot = !renderTarget()->parent(); | |
599 bool smallAnimatedLayer = isSmallAnimatedLayer(this); | |
600 | |
601 // Minimally create the tiles in the desired pre-paint rect. | |
602 IntRect createTilesRect = idlePaintRect(); | |
603 if (!createTilesRect.isEmpty()) { | |
604 int left, top, right, bottom; | |
605 m_tiler->contentRectToTileIndices(createTilesRect, left, top, right, bot
tom); | |
606 for (int j = top; j <= bottom; ++j) { | |
607 for (int i = left; i <= right; ++i) { | |
608 if (!tileAt(i, j)) | |
609 createTile(i, j); | |
610 } | |
611 } | |
612 } | |
613 | |
614 // Also, minimally create all tiles for small animated layers and also | |
615 // double-buffer them since we have limited their size to be reasonable. | |
616 IntRect doubleBufferedRect = visibleContentRect(); | |
617 if (smallAnimatedLayer) | |
618 doubleBufferedRect = IntRect(IntPoint::zero(), contentBounds()); | |
619 | |
620 // Create additional textures for double-buffered updates when needed. | |
621 // These textures must stay alive while the updated textures are incremental
ly | |
622 // uploaded, swapped atomically via pushProperties, and finally deleted | |
623 // after the commit is complete, after which they can be recycled. | |
624 if (!doubleBufferedRect.isEmpty()) { | |
625 int left, top, right, bottom; | |
626 m_tiler->contentRectToTileIndices(doubleBufferedRect, left, top, right,
bottom); | |
627 for (int j = top; j <= bottom; ++j) { | |
628 for (int i = left; i <= right; ++i) { | |
629 UpdatableTile* tile = tileAt(i, j); | |
630 if (!tile) | |
631 tile = createTile(i, j); | |
632 // We need an additional texture if the tile needs a buffered-up
date and it's not a partial update. | |
633 // FIXME: Decide if partial update should be allowed based on co
st | |
634 // of update. https://bugs.webkit.org/show_bug.cgi?id=77376 | |
635 if (!layerTreeHost() || !layerTreeHost()->bufferedUpdates() || !
tileNeedsBufferedUpdate(tile)) | |
636 continue; | |
637 if (tileOnlyNeedsPartialUpdate(tile) && layerTreeHost()->request
PartialTextureUpdate()) { | |
638 tile->partialUpdate = true; | |
639 continue; | |
640 } | |
641 | |
642 IntRect tileRect = m_tiler->tileRect(tile); | |
643 tile->dirtyRect = tileRect; | |
644 LayerTextureUpdater::Texture* backBuffer = tile->texture(); | |
645 setPriorityForTexture(visibleContentRect(), tile->dirtyRect, dra
wsToRoot, smallAnimatedLayer, backBuffer->texture()); | |
646 scoped_ptr<CCPrioritizedTexture> frontBuffer = CCPrioritizedText
ure::create(backBuffer->texture()->textureManager(), | |
647
backBuffer->texture()->size(), | |
648
backBuffer->texture()->format()); | |
649 // Swap backBuffer into frontBuffer and add it to delete after c
ommit queue. | |
650 backBuffer->swapTextureWith(frontBuffer); | |
651 layerTreeHost()->deleteTextureAfterCommit(frontBuffer.Pass()); | |
652 } | |
653 } | |
654 } | |
655 | |
656 // Now update priorities on all tiles we have in the layer, no matter where
they are. | |
657 for (CCLayerTilingData::TileMap::const_iterator iter = m_tiler->tiles().begi
n(); iter != m_tiler->tiles().end(); ++iter) { | |
658 #if WTF_NEW_HASHMAP_ITERATORS_INTERFACE | |
659 UpdatableTile* tile = static_cast<UpdatableTile*>(iter->value.get()); | |
660 #else | |
661 UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second.get()); | |
662 #endif | |
663 // FIXME: This should not ever be null. | |
664 if (!tile) | |
665 continue; | |
666 IntRect tileRect = m_tiler->tileRect(tile); | |
667 setPriorityForTexture(visibleContentRect(), tileRect, drawsToRoot, small
AnimatedLayer, tile->managedTexture()); | |
668 } | |
669 } | |
670 | |
671 Region TiledLayerChromium::visibleContentOpaqueRegion() const | |
672 { | |
673 if (m_skipsDraw) | |
674 return Region(); | |
675 if (contentsOpaque()) | |
676 return visibleContentRect(); | |
677 return m_tiler->opaqueRegionInContentRect(visibleContentRect()); | |
678 } | |
679 | |
680 void TiledLayerChromium::resetUpdateState() | |
681 { | |
682 m_skipsDraw = false; | |
683 m_failedUpdate = false; | |
684 | |
685 CCLayerTilingData::TileMap::const_iterator end = m_tiler->tiles().end(); | |
686 for (CCLayerTilingData::TileMap::const_iterator iter = m_tiler->tiles().begi
n(); iter != end; ++iter) { | |
687 #if WTF_NEW_HASHMAP_ITERATORS_INTERFACE | |
688 UpdatableTile* tile = static_cast<UpdatableTile*>(iter->value.get()); | |
689 #else | |
690 UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second.get()); | |
691 #endif | |
692 // FIXME: This should not ever be null. | |
693 if (!tile) | |
694 continue; | |
695 tile->resetUpdateState(); | |
696 } | |
697 } | |
698 | |
699 void TiledLayerChromium::update(CCTextureUpdateQueue& queue, const CCOcclusionTr
acker* occlusion, CCRenderingStats& stats) | |
700 { | |
701 ASSERT(!m_skipsDraw && !m_failedUpdate); // Did resetUpdateState get skipped
? | |
702 updateBounds(); | |
703 if (m_tiler->hasEmptyBounds() || !drawsContent()) | |
704 return; | |
705 | |
706 bool didPaint = false; | |
707 | |
708 // Animation pre-paint. If the layer is small, try to paint it all | |
709 // immediately whether or not it is occluded, to avoid paint/upload | |
710 // hiccups while it is animating. | |
711 if (isSmallAnimatedLayer(this)) { | |
712 int left, top, right, bottom; | |
713 m_tiler->contentRectToTileIndices(IntRect(IntPoint::zero(), contentBound
s()), left, top, right, bottom); | |
714 updateTiles(left, top, right, bottom, queue, 0, stats, didPaint); | |
715 if (didPaint) | |
716 return; | |
717 // This was an attempt to paint the entire layer so if we fail it's okay
, | |
718 // just fallback on painting visible etc. below. | |
719 m_failedUpdate = false; | |
720 } | |
721 | |
722 if (visibleContentRect().isEmpty()) | |
723 return; | |
724 | |
725 // Visible painting. First occlude visible tiles and paint the non-occluded
tiles. | |
726 int left, top, right, bottom; | |
727 m_tiler->contentRectToTileIndices(visibleContentRect(), left, top, right, bo
ttom); | |
728 markOcclusionsAndRequestTextures(left, top, right, bottom, occlusion); | |
729 m_skipsDraw = !updateTiles(left, top, right, bottom, queue, occlusion, stats
, didPaint); | |
730 if (m_skipsDraw) | |
731 m_tiler->reset(); | |
732 if (m_skipsDraw || didPaint) | |
733 return; | |
734 | |
735 // If we have already painting everything visible. Do some pre-painting whil
e idle. | |
736 IntRect idlePaintContentRect = idlePaintRect(); | |
737 if (idlePaintContentRect.isEmpty()) | |
738 return; | |
739 | |
740 // Prepaint anything that was occluded but inside the layer's visible region
. | |
741 if (!updateTiles(left, top, right, bottom, queue, 0, stats, didPaint) || did
Paint) | |
742 return; | |
743 | |
744 int prepaintLeft, prepaintTop, prepaintRight, prepaintBottom; | |
745 m_tiler->contentRectToTileIndices(idlePaintContentRect, prepaintLeft, prepai
ntTop, prepaintRight, prepaintBottom); | |
746 | |
747 // Then expand outwards from the visible area until we find a dirty row or c
olumn to update. | |
748 while (left > prepaintLeft || top > prepaintTop || right < prepaintRight ||
bottom < prepaintBottom) { | |
749 if (bottom < prepaintBottom) { | |
750 ++bottom; | |
751 if (!updateTiles(left, bottom, right, bottom, queue, 0, stats, didPa
int) || didPaint) | |
752 return; | |
753 } | |
754 if (top > prepaintTop) { | |
755 --top; | |
756 if (!updateTiles(left, top, right, top, queue, 0, stats, didPaint) |
| didPaint) | |
757 return; | |
758 } | |
759 if (left > prepaintLeft) { | |
760 --left; | |
761 if (!updateTiles(left, top, left, bottom, queue, 0, stats, didPaint)
|| didPaint) | |
762 return; | |
763 } | |
764 if (right < prepaintRight) { | |
765 ++right; | |
766 if (!updateTiles(right, top, right, bottom, queue, 0, stats, didPain
t) || didPaint) | |
767 return; | |
768 } | |
769 } | |
770 } | |
771 | |
772 bool TiledLayerChromium::needsIdlePaint() | |
773 { | |
774 // Don't trigger more paints if we failed (as we'll just fail again). | |
775 if (m_failedUpdate || visibleContentRect().isEmpty() || m_tiler->hasEmptyBou
nds() || !drawsContent()) | |
776 return false; | |
777 | |
778 IntRect idlePaintContentRect = idlePaintRect(); | |
779 if (idlePaintContentRect.isEmpty()) | |
780 return false; | |
781 | |
782 int left, top, right, bottom; | |
783 m_tiler->contentRectToTileIndices(idlePaintContentRect, left, top, right, bo
ttom); | |
784 | |
785 for (int j = top; j <= bottom; ++j) { | |
786 for (int i = left; i <= right; ++i) { | |
787 UpdatableTile* tile = tileAt(i, j); | |
788 ASSERT(tile); // Did setTexturePriorities get skipped? | |
789 if (!tile) | |
790 continue; | |
791 | |
792 bool updated = !tile->updateRect.isEmpty(); | |
793 bool canAcquire = tile->managedTexture()->canAcquireBackingTexture()
; | |
794 bool dirty = tile->isDirty() || !tile->managedTexture()->haveBacking
Texture(); | |
795 if (!updated && canAcquire && dirty) | |
796 return true; | |
797 } | |
798 } | |
799 return false; | |
800 } | |
801 | |
802 IntRect TiledLayerChromium::idlePaintRect() | |
803 { | |
804 // Don't inflate an empty rect. | |
805 if (visibleContentRect().isEmpty()) | |
806 return IntRect(); | |
807 | |
808 // FIXME: This can be made a lot larger now! We should increase | |
809 // this slowly while insuring it doesn't cause any perf issues. | |
810 IntRect prepaintRect = visibleContentRect(); | |
811 prepaintRect.inflateX(m_tiler->tileSize().width()); | |
812 prepaintRect.inflateY(m_tiler->tileSize().height() * 2); | |
813 IntRect contentRect(IntPoint::zero(), contentBounds()); | |
814 prepaintRect.intersect(contentRect); | |
815 | |
816 return prepaintRect; | |
817 } | |
818 | |
819 } | |
820 #endif // USE(ACCELERATED_COMPOSITING) | |
OLD | NEW |