OLD | NEW |
| (Empty) |
1 // Copyright 2014 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 #include "platform/graphics/paint/DisplayItemList.h" | |
7 | |
8 #include "platform/NotImplemented.h" | |
9 #include "platform/TraceEvent.h" | |
10 #include "platform/graphics/GraphicsLayer.h" | |
11 #include "platform/graphics/paint/DrawingDisplayItem.h" | |
12 | |
13 #ifndef NDEBUG | |
14 #include "platform/graphics/LoggingCanvas.h" | |
15 #include "wtf/text/StringBuilder.h" | |
16 #include <stdio.h> | |
17 #endif | |
18 | |
19 namespace blink { | |
20 | |
21 const PaintArtifact& DisplayItemList::paintArtifact() const | |
22 { | |
23 ASSERT(m_newDisplayItems.isEmpty()); | |
24 ASSERT(m_newPaintChunks.isInInitialState()); | |
25 return m_currentPaintArtifact; | |
26 } | |
27 | |
28 bool DisplayItemList::lastDisplayItemIsNoopBegin() const | |
29 { | |
30 if (m_newDisplayItems.isEmpty()) | |
31 return false; | |
32 | |
33 const auto& lastDisplayItem = m_newDisplayItems.last(); | |
34 return lastDisplayItem.isBegin() && !lastDisplayItem.drawsContent(); | |
35 } | |
36 | |
37 void DisplayItemList::removeLastDisplayItem() | |
38 { | |
39 if (m_newDisplayItems.isEmpty()) | |
40 return; | |
41 | |
42 #if ENABLE(ASSERT) | |
43 // Also remove the index pointing to the removed display item. | |
44 DisplayItemIndicesByClientMap::iterator it = m_newDisplayItemIndicesByClient
.find(m_newDisplayItems.last().client()); | |
45 if (it != m_newDisplayItemIndicesByClient.end()) { | |
46 Vector<size_t>& indices = it->value; | |
47 if (!indices.isEmpty() && indices.last() == (m_newDisplayItems.size() -
1)) | |
48 indices.removeLast(); | |
49 } | |
50 #endif | |
51 m_newDisplayItems.removeLast(); | |
52 | |
53 if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) | |
54 m_newPaintChunks.decrementDisplayItemIndex(); | |
55 } | |
56 | |
57 void DisplayItemList::processNewItem(DisplayItem& displayItem) | |
58 { | |
59 ASSERT(!m_constructionDisabled); | |
60 ASSERT(!skippingCache() || !displayItem.isCached()); | |
61 | |
62 if (displayItem.isCached()) | |
63 ++m_numCachedItems; | |
64 | |
65 #if ENABLE(ASSERT) | |
66 // Verify noop begin/end pairs have been removed. | |
67 if (m_newDisplayItems.size() >= 2 && displayItem.isEnd()) { | |
68 const auto& beginDisplayItem = m_newDisplayItems[m_newDisplayItems.size(
) - 2]; | |
69 if (beginDisplayItem.isBegin() && !beginDisplayItem.isSubsequence() && !
beginDisplayItem.drawsContent()) | |
70 ASSERT(!displayItem.isEndAndPairedWith(beginDisplayItem.type())); | |
71 } | |
72 #endif | |
73 | |
74 if (!m_scopeStack.isEmpty()) | |
75 displayItem.setScope(m_scopeStack.last()); | |
76 | |
77 #if ENABLE(ASSERT) | |
78 size_t index = findMatchingItemFromIndex(displayItem.nonCachedId(), m_newDis
playItemIndicesByClient, m_newDisplayItems); | |
79 if (index != kNotFound) { | |
80 #ifndef NDEBUG | |
81 showDebugData(); | |
82 WTFLogAlways("DisplayItem %s has duplicated id with previous %s (index=%
d)\n", | |
83 displayItem.asDebugString().utf8().data(), m_newDisplayItems[index].
asDebugString().utf8().data(), static_cast<int>(index)); | |
84 #endif | |
85 ASSERT_NOT_REACHED(); | |
86 } | |
87 addItemToIndexIfNeeded(displayItem, m_newDisplayItems.size() - 1, m_newDispl
ayItemIndicesByClient); | |
88 #endif // ENABLE(ASSERT) | |
89 | |
90 if (skippingCache()) | |
91 displayItem.setSkippedCache(); | |
92 | |
93 if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) | |
94 m_newPaintChunks.incrementDisplayItemIndex(); | |
95 } | |
96 | |
97 void DisplayItemList::updateCurrentPaintChunkProperties(const PaintChunkProperti
es& newProperties) | |
98 { | |
99 m_newPaintChunks.updateCurrentPaintChunkProperties(newProperties); | |
100 } | |
101 | |
102 void DisplayItemList::beginScope() | |
103 { | |
104 ASSERT_WITH_SECURITY_IMPLICATION(m_nextScope < UINT_MAX); | |
105 m_scopeStack.append(m_nextScope++); | |
106 beginSkippingCache(); | |
107 } | |
108 | |
109 void DisplayItemList::endScope() | |
110 { | |
111 m_scopeStack.removeLast(); | |
112 endSkippingCache(); | |
113 } | |
114 | |
115 void DisplayItemList::invalidate(const DisplayItemClientWrapper& client, PaintIn
validationReason paintInvalidationReason, const IntRect& previousPaintInvalidati
onRect, const IntRect& newPaintInvalidationRect) | |
116 { | |
117 invalidateClient(client); | |
118 | |
119 if (RuntimeEnabledFeatures::slimmingPaintSynchronizedPaintingEnabled()) { | |
120 Invalidation invalidation = { previousPaintInvalidationRect, paintInvali
dationReason }; | |
121 if (!previousPaintInvalidationRect.isEmpty()) | |
122 m_invalidations.append(invalidation); | |
123 if (newPaintInvalidationRect != previousPaintInvalidationRect && !newPai
ntInvalidationRect.isEmpty()) { | |
124 invalidation.rect = newPaintInvalidationRect; | |
125 m_invalidations.append(invalidation); | |
126 } | |
127 } | |
128 } | |
129 | |
130 void DisplayItemList::invalidateClient(const DisplayItemClientWrapper& client) | |
131 { | |
132 invalidateUntracked(client.displayItemClient()); | |
133 if (RuntimeEnabledFeatures::slimmingPaintV2Enabled() && m_trackedPaintInvali
dationObjects) | |
134 m_trackedPaintInvalidationObjects->append(client.debugName()); | |
135 } | |
136 | |
137 void DisplayItemList::invalidateUntracked(DisplayItemClient client) | |
138 { | |
139 // This can be called during painting, but we can't invalidate already paint
ed clients. | |
140 ASSERT(!m_newDisplayItemIndicesByClient.contains(client)); | |
141 updateValidlyCachedClientsIfNeeded(); | |
142 m_validlyCachedClients.remove(client); | |
143 } | |
144 | |
145 void DisplayItemList::invalidateAll() | |
146 { | |
147 // Can only be called during layout/paintInvalidation, not during painting. | |
148 ASSERT(m_newDisplayItems.isEmpty()); | |
149 m_currentPaintArtifact.reset(); | |
150 m_validlyCachedClients.clear(); | |
151 m_validlyCachedClientsDirty = false; | |
152 | |
153 if (RuntimeEnabledFeatures::slimmingPaintV2Enabled() && m_trackedPaintInvali
dationObjects) | |
154 m_trackedPaintInvalidationObjects->append("##ALL##"); | |
155 } | |
156 | |
157 bool DisplayItemList::clientCacheIsValid(DisplayItemClient client) const | |
158 { | |
159 if (skippingCache()) | |
160 return false; | |
161 updateValidlyCachedClientsIfNeeded(); | |
162 return m_validlyCachedClients.contains(client); | |
163 } | |
164 | |
165 void DisplayItemList::invalidatePaintOffset(const DisplayItemClientWrapper& clie
nt) | |
166 { | |
167 ASSERT(RuntimeEnabledFeatures::slimmingPaintOffsetCachingEnabled()); | |
168 invalidateClient(client); | |
169 | |
170 #if ENABLE(ASSERT) | |
171 ASSERT(!paintOffsetWasInvalidated(client.displayItemClient())); | |
172 m_clientsWithPaintOffsetInvalidations.add(client.displayItemClient()); | |
173 #endif | |
174 } | |
175 | |
176 #if ENABLE(ASSERT) | |
177 bool DisplayItemList::paintOffsetWasInvalidated(DisplayItemClient client) const | |
178 { | |
179 ASSERT(RuntimeEnabledFeatures::slimmingPaintOffsetCachingEnabled()); | |
180 return m_clientsWithPaintOffsetInvalidations.contains(client); | |
181 } | |
182 #endif | |
183 | |
184 size_t DisplayItemList::findMatchingItemFromIndex(const DisplayItem::Id& id, con
st DisplayItemIndicesByClientMap& displayItemIndicesByClient, const DisplayItems
& list) | |
185 { | |
186 DisplayItemIndicesByClientMap::const_iterator it = displayItemIndicesByClien
t.find(id.client); | |
187 if (it == displayItemIndicesByClient.end()) | |
188 return kNotFound; | |
189 | |
190 const Vector<size_t>& indices = it->value; | |
191 for (size_t index : indices) { | |
192 const DisplayItem& existingItem = list[index]; | |
193 ASSERT(!existingItem.isValid() || existingItem.client() == id.client); | |
194 if (existingItem.isValid() && id.matches(existingItem)) | |
195 return index; | |
196 } | |
197 | |
198 return kNotFound; | |
199 } | |
200 | |
201 void DisplayItemList::addItemToIndexIfNeeded(const DisplayItem& displayItem, siz
e_t index, DisplayItemIndicesByClientMap& displayItemIndicesByClient) | |
202 { | |
203 if (!displayItem.isCacheable()) | |
204 return; | |
205 | |
206 DisplayItemIndicesByClientMap::iterator it = displayItemIndicesByClient.find
(displayItem.client()); | |
207 Vector<size_t>& indices = it == displayItemIndicesByClient.end() ? | |
208 displayItemIndicesByClient.add(displayItem.client(), Vector<size_t>()).s
toredValue->value : it->value; | |
209 indices.append(index); | |
210 } | |
211 | |
212 struct DisplayItemList::OutOfOrderIndexContext { | |
213 OutOfOrderIndexContext(DisplayItems::iterator begin) : nextItemToIndex(begin
) { } | |
214 | |
215 DisplayItems::iterator nextItemToIndex; | |
216 DisplayItemIndicesByClientMap displayItemIndicesByClient; | |
217 }; | |
218 | |
219 DisplayItems::iterator DisplayItemList::findOutOfOrderCachedItem(const DisplayIt
em::Id& id, OutOfOrderIndexContext& context) | |
220 { | |
221 ASSERT(clientCacheIsValid(id.client)); | |
222 | |
223 size_t foundIndex = findMatchingItemFromIndex(id, context.displayItemIndices
ByClient, m_currentPaintArtifact.displayItems()); | |
224 if (foundIndex != kNotFound) | |
225 return m_currentPaintArtifact.displayItems().begin() + foundIndex; | |
226 | |
227 return findOutOfOrderCachedItemForward(id, context); | |
228 } | |
229 | |
230 // Find forward for the item and index all skipped indexable items. | |
231 DisplayItems::iterator DisplayItemList::findOutOfOrderCachedItemForward(const Di
splayItem::Id& id, OutOfOrderIndexContext& context) | |
232 { | |
233 DisplayItems::iterator currentEnd = m_currentPaintArtifact.displayItems().en
d(); | |
234 for (; context.nextItemToIndex != currentEnd; ++context.nextItemToIndex) { | |
235 const DisplayItem& item = *context.nextItemToIndex; | |
236 ASSERT(item.isValid()); | |
237 if (item.isCacheable() && clientCacheIsValid(item.client())) { | |
238 if (id.matches(item)) | |
239 return context.nextItemToIndex++; | |
240 | |
241 addItemToIndexIfNeeded(item, context.nextItemToIndex - m_currentPain
tArtifact.displayItems().begin(), context.displayItemIndicesByClient); | |
242 } | |
243 } | |
244 return currentEnd; | |
245 } | |
246 | |
247 void DisplayItemList::copyCachedSubsequence(DisplayItems::iterator& currentIt, D
isplayItems& updatedList) | |
248 { | |
249 ASSERT(currentIt->isSubsequence()); | |
250 ASSERT(!currentIt->scope()); | |
251 DisplayItem::Id endSubsequenceId(currentIt->client(), DisplayItem::subsequen
ceTypeToEndSubsequenceType(currentIt->type()), 0); | |
252 do { | |
253 // We should always find the EndSubsequence display item. | |
254 ASSERT(currentIt != m_currentPaintArtifact.displayItems().end()); | |
255 ASSERT(currentIt->isValid()); | |
256 updatedList.appendByMoving(*currentIt); | |
257 ++currentIt; | |
258 } while (!endSubsequenceId.matches(updatedList.last())); | |
259 } | |
260 | |
261 // Update the existing display items by removing invalidated entries, updating | |
262 // repainted ones, and appending new items. | |
263 // - For cached drawing display item, copy the corresponding cached DrawingDispl
ayItem; | |
264 // - For cached subsequence display item, copy the cached display items between
the | |
265 // corresponding SubsequenceDisplayItem and EndSubsequenceDisplayItem (incl.); | |
266 // - Otherwise, copy the new display item. | |
267 // | |
268 // The algorithm is O(|m_currentDisplayItems| + |m_newDisplayItems|). | |
269 // Coefficients are related to the ratio of out-of-order CachedDisplayItems | |
270 // and the average number of (Drawing|Subsequence)DisplayItems per client. | |
271 // | |
272 void DisplayItemList::commitNewDisplayItems(GraphicsLayer* graphicsLayer) | |
273 { | |
274 TRACE_EVENT2("blink,benchmark", "DisplayItemList::commitNewDisplayItems", | |
275 "current_display_list_size", (int)m_currentPaintArtifact.displayItems().
size(), | |
276 "num_non_cached_new_items", (int)m_newDisplayItems.size() - m_numCachedI
tems); | |
277 | |
278 if (RuntimeEnabledFeatures::slimmingPaintSynchronizedPaintingEnabled()) { | |
279 for (const auto& invalidation : m_invalidations) | |
280 graphicsLayer->setNeedsDisplayInRect(invalidation.rect, invalidation
.invalidationReason); | |
281 m_invalidations.clear(); | |
282 m_clientsCheckedPaintInvalidation.clear(); | |
283 } | |
284 | |
285 // These data structures are used during painting only. | |
286 ASSERT(m_scopeStack.isEmpty()); | |
287 m_scopeStack.clear(); | |
288 m_nextScope = 1; | |
289 ASSERT(!skippingCache()); | |
290 #if ENABLE(ASSERT) | |
291 m_newDisplayItemIndicesByClient.clear(); | |
292 m_clientsWithPaintOffsetInvalidations.clear(); | |
293 #endif | |
294 | |
295 if (m_currentPaintArtifact.isEmpty()) { | |
296 #if ENABLE(ASSERT) | |
297 for (const auto& item : m_newDisplayItems) | |
298 ASSERT(!item.isCached()); | |
299 #endif | |
300 m_currentPaintArtifact.displayItems().swap(m_newDisplayItems); | |
301 m_currentPaintArtifact.paintChunks() = m_newPaintChunks.releasePaintChun
ks(); | |
302 m_validlyCachedClientsDirty = true; | |
303 m_numCachedItems = 0; | |
304 return; | |
305 } | |
306 | |
307 updateValidlyCachedClientsIfNeeded(); | |
308 | |
309 // Stores indices to valid DrawingDisplayItems in m_currentDisplayItems that
have not been matched | |
310 // by CachedDisplayItems during synchronized matching. The indexed items wil
l be matched | |
311 // by later out-of-order CachedDisplayItems in m_newDisplayItems. This ensur
es that when | |
312 // out-of-order CachedDisplayItems occur, we only traverse at most once over
m_currentDisplayItems | |
313 // looking for potential matches. Thus we can ensure that the algorithm runs
in linear time. | |
314 OutOfOrderIndexContext outOfOrderIndexContext(m_currentPaintArtifact.display
Items().begin()); | |
315 | |
316 // TODO(jbroman): Consider revisiting this heuristic. | |
317 DisplayItems updatedList(std::max(m_currentPaintArtifact.displayItems().used
CapacityInBytes(), m_newDisplayItems.usedCapacityInBytes())); | |
318 Vector<PaintChunk> updatedPaintChunks; | |
319 DisplayItems::iterator currentIt = m_currentPaintArtifact.displayItems().beg
in(); | |
320 DisplayItems::iterator currentEnd = m_currentPaintArtifact.displayItems().en
d(); | |
321 for (DisplayItems::iterator newIt = m_newDisplayItems.begin(); newIt != m_ne
wDisplayItems.end(); ++newIt) { | |
322 const DisplayItem& newDisplayItem = *newIt; | |
323 const DisplayItem::Id newDisplayItemId = newDisplayItem.nonCachedId(); | |
324 bool newDisplayItemHasCachedType = newDisplayItem.type() != newDisplayIt
emId.type; | |
325 | |
326 bool isSynchronized = currentIt != currentEnd && newDisplayItemId.matche
s(*currentIt); | |
327 | |
328 if (newDisplayItemHasCachedType) { | |
329 ASSERT(newDisplayItem.isCached()); | |
330 ASSERT(clientCacheIsValid(newDisplayItem.client()) || (RuntimeEnable
dFeatures::slimmingPaintOffsetCachingEnabled() && !paintOffsetWasInvalidated(new
DisplayItem.client()))); | |
331 if (!isSynchronized) { | |
332 currentIt = findOutOfOrderCachedItem(newDisplayItemId, outOfOrde
rIndexContext); | |
333 | |
334 if (currentIt == currentEnd) { | |
335 #ifndef NDEBUG | |
336 showDebugData(); | |
337 WTFLogAlways("%s not found in m_currentDisplayItems\n", newD
isplayItem.asDebugString().utf8().data()); | |
338 #endif | |
339 ASSERT_NOT_REACHED(); | |
340 // We did not find the cached display item. This should be i
mpossible, but may occur if there is a bug | |
341 // in the system, such as under-invalidation, incorrect cach
e checking or duplicate display ids. | |
342 // In this case, attempt to recover rather than crashing or
bailing on display of the rest of the display list. | |
343 continue; | |
344 } | |
345 } | |
346 #if ENABLE(ASSERT) | |
347 if (RuntimeEnabledFeatures::slimmingPaintUnderInvalidationCheckingEn
abled()) { | |
348 DisplayItems::iterator temp = currentIt; | |
349 checkUnderInvalidation(newIt, temp); | |
350 } | |
351 #endif | |
352 if (newDisplayItem.isCachedDrawing()) { | |
353 updatedList.appendByMoving(*currentIt); | |
354 ++currentIt; | |
355 } else { | |
356 ASSERT(newDisplayItem.isCachedSubsequence()); | |
357 copyCachedSubsequence(currentIt, updatedList); | |
358 ASSERT(updatedList.last().isEndSubsequence()); | |
359 } | |
360 } else { | |
361 ASSERT(!newDisplayItem.isDrawing() | |
362 || newDisplayItem.skippedCache() | |
363 || !clientCacheIsValid(newDisplayItem.client()) | |
364 || (RuntimeEnabledFeatures::slimmingPaintOffsetCachingEnabled()
&& paintOffsetWasInvalidated(newDisplayItem.client()))); | |
365 | |
366 updatedList.appendByMoving(*newIt); | |
367 | |
368 if (isSynchronized) | |
369 ++currentIt; | |
370 } | |
371 // Items before currentIt should have been copied so we don't need to in
dex them. | |
372 if (currentIt - outOfOrderIndexContext.nextItemToIndex > 0) | |
373 outOfOrderIndexContext.nextItemToIndex = currentIt; | |
374 } | |
375 | |
376 #if ENABLE(ASSERT) | |
377 if (RuntimeEnabledFeatures::slimmingPaintUnderInvalidationCheckingEnabled()) | |
378 checkNoRemainingCachedDisplayItems(); | |
379 #endif // ENABLE(ASSERT) | |
380 | |
381 | |
382 // TODO(jbroman): When subsequence caching applies to SPv2, we'll need to | |
383 // merge the paint chunks as well. | |
384 m_currentPaintArtifact.displayItems().swap(updatedList); | |
385 m_currentPaintArtifact.paintChunks() = m_newPaintChunks.releasePaintChunks()
; | |
386 | |
387 m_newDisplayItems.clear(); | |
388 m_validlyCachedClientsDirty = true; | |
389 m_numCachedItems = 0; | |
390 } | |
391 | |
392 size_t DisplayItemList::approximateUnsharedMemoryUsage() const | |
393 { | |
394 size_t memoryUsage = sizeof(*this); | |
395 | |
396 // Memory outside this class due to m_currentPaintArtifact. | |
397 memoryUsage += m_currentPaintArtifact.approximateUnsharedMemoryUsage() - siz
eof(m_currentPaintArtifact); | |
398 | |
399 // TODO(jbroman): If display items begin to have significant external memory | |
400 // usage that's not shared with the embedder, we should account for it here. | |
401 // | |
402 // External objects, shared with the embedder, such as SkPicture, should be | |
403 // excluded to avoid double counting. It is the embedder's responsibility to | |
404 // count such objects. | |
405 // | |
406 // At time of writing, the only known case of unshared external memory was | |
407 // the rounded clips vector in ClipDisplayItem, which is not expected to | |
408 // contribute significantly to memory usage. | |
409 | |
410 // Memory outside this class due to m_newDisplayItems. | |
411 ASSERT(m_newDisplayItems.isEmpty()); | |
412 memoryUsage += m_newDisplayItems.memoryUsageInBytes(); | |
413 | |
414 return memoryUsage; | |
415 } | |
416 | |
417 void DisplayItemList::updateValidlyCachedClientsIfNeeded() const | |
418 { | |
419 if (!m_validlyCachedClientsDirty) | |
420 return; | |
421 | |
422 m_validlyCachedClients.clear(); | |
423 m_validlyCachedClientsDirty = false; | |
424 | |
425 DisplayItemClient lastAddedClient = nullptr; | |
426 for (const DisplayItem& displayItem : m_currentPaintArtifact.displayItems())
{ | |
427 if (displayItem.client() == lastAddedClient) | |
428 continue; | |
429 if (displayItem.isCacheable()) { | |
430 lastAddedClient = displayItem.client(); | |
431 m_validlyCachedClients.add(lastAddedClient); | |
432 } | |
433 } | |
434 } | |
435 | |
436 #if ENABLE(ASSERT) | |
437 | |
438 void DisplayItemList::checkUnderInvalidation(DisplayItems::iterator& newIt, Disp
layItems::iterator& currentIt) | |
439 { | |
440 ASSERT(RuntimeEnabledFeatures::slimmingPaintUnderInvalidationCheckingEnabled
()); | |
441 ASSERT(newIt->isCached()); | |
442 | |
443 // When under-invalidation-checking is enabled, the forced painting is follo
wing the cached display item. | |
444 DisplayItem::Type nextItemType = DisplayItem::nonCachedType(newIt->type()); | |
445 ++newIt; | |
446 ASSERT(newIt->type() == nextItemType); | |
447 | |
448 if (newIt->isDrawing()) { | |
449 checkCachedDisplayItemIsUnchanged("", *newIt, *currentIt); | |
450 return; | |
451 } | |
452 | |
453 ASSERT(newIt->isSubsequence()); | |
454 | |
455 #ifndef NDEBUG | |
456 CString messagePrefix = String::format("(In CachedSubsequence of %s)", newIt
->clientDebugString().utf8().data()).utf8(); | |
457 #else | |
458 CString messagePrefix = "(In CachedSubsequence)"; | |
459 #endif | |
460 | |
461 DisplayItem::Id endSubsequenceId(newIt->client(), DisplayItem::subsequenceTy
peToEndSubsequenceType(newIt->type()), 0); | |
462 while (true) { | |
463 ASSERT(newIt != m_newDisplayItems.end()); | |
464 if (newIt->isCached()) | |
465 checkUnderInvalidation(newIt, currentIt); | |
466 else | |
467 checkCachedDisplayItemIsUnchanged(messagePrefix.data(), *newIt, *cur
rentIt); | |
468 | |
469 if (endSubsequenceId.matches(*newIt)) | |
470 break; | |
471 | |
472 ++newIt; | |
473 ++currentIt; | |
474 } | |
475 } | |
476 | |
477 static void showUnderInvalidationError(const char* messagePrefix, const char* re
ason, const DisplayItem* newItem, const DisplayItem* oldItem) | |
478 { | |
479 #ifndef NDEBUG | |
480 WTFLogAlways("%s %s:\nNew display item: %s\nOld display item: %s\nSee http:/
/crbug.com/450725.", messagePrefix, reason, | |
481 newItem ? newItem->asDebugString().utf8().data() : "None", | |
482 oldItem ? oldItem->asDebugString().utf8().data() : "None"); | |
483 #else | |
484 WTFLogAlways("%s %s. Run debug build to get more details\nSee http://crbug.c
om/450725.", messagePrefix, reason); | |
485 #endif // NDEBUG | |
486 } | |
487 | |
488 void DisplayItemList::checkCachedDisplayItemIsUnchanged(const char* messagePrefi
x, const DisplayItem& newItem, const DisplayItem& oldItem) | |
489 { | |
490 ASSERT(RuntimeEnabledFeatures::slimmingPaintUnderInvalidationCheckingEnabled
()); | |
491 ASSERT(!newItem.isCached()); | |
492 ASSERT(!oldItem.isCached()); | |
493 | |
494 if (newItem.skippedCache()) { | |
495 showUnderInvalidationError(messagePrefix, "ERROR: under-invalidation: sk
ipped-cache in cached subsequence", &newItem, &oldItem); | |
496 ASSERT_NOT_REACHED(); | |
497 } | |
498 | |
499 if (newItem.isCacheable() && !m_validlyCachedClients.contains(newItem.client
())) { | |
500 showUnderInvalidationError(messagePrefix, "ERROR: under-invalidation: in
validated in cached subsequence", &newItem, &oldItem); | |
501 ASSERT_NOT_REACHED(); | |
502 } | |
503 | |
504 if (newItem.equals(oldItem)) | |
505 return; | |
506 | |
507 showUnderInvalidationError(messagePrefix, "ERROR: under-invalidation: displa
y item changed", &newItem, &oldItem); | |
508 | |
509 #ifndef NDEBUG | |
510 if (newItem.isDrawing()) { | |
511 RefPtr<const SkPicture> newPicture = static_cast<const DrawingDisplayIte
m&>(newItem).picture(); | |
512 RefPtr<const SkPicture> oldPicture = static_cast<const DrawingDisplayIte
m&>(oldItem).picture(); | |
513 String oldPictureDebugString = oldPicture ? pictureAsDebugString(oldPict
ure.get()) : "None"; | |
514 String newPictureDebugString = newPicture ? pictureAsDebugString(newPict
ure.get()) : "None"; | |
515 WTFLogAlways("old picture:\n%s\n", oldPictureDebugString.utf8().data()); | |
516 WTFLogAlways("new picture:\n%s\n", newPictureDebugString.utf8().data()); | |
517 } | |
518 #endif // NDEBUG | |
519 | |
520 ASSERT_NOT_REACHED(); | |
521 } | |
522 | |
523 void DisplayItemList::checkNoRemainingCachedDisplayItems() | |
524 { | |
525 ASSERT(RuntimeEnabledFeatures::slimmingPaintUnderInvalidationCheckingEnabled
()); | |
526 | |
527 for (const auto& displayItem : m_currentPaintArtifact.displayItems()) { | |
528 if (!displayItem.isValid() || !displayItem.isCacheable() || !clientCache
IsValid(displayItem.client())) | |
529 continue; | |
530 showUnderInvalidationError("", "May be under-invalidation: no new displa
y item", nullptr, &displayItem); | |
531 } | |
532 } | |
533 | |
534 #endif // ENABLE(ASSERT) | |
535 | |
536 #ifndef NDEBUG | |
537 | |
538 WTF::String DisplayItemList::displayItemsAsDebugString(const DisplayItems& list)
const | |
539 { | |
540 StringBuilder stringBuilder; | |
541 size_t i = 0; | |
542 for (auto it = list.begin(); it != list.end(); ++it, ++i) { | |
543 const DisplayItem& displayItem = *it; | |
544 if (i) | |
545 stringBuilder.append(",\n"); | |
546 stringBuilder.append(String::format("{index: %d, ", (int)i)); | |
547 displayItem.dumpPropertiesAsDebugString(stringBuilder); | |
548 if (displayItem.isValid()) { | |
549 stringBuilder.append(", cacheIsValid: "); | |
550 stringBuilder.append(clientCacheIsValid(displayItem.client()) ? "tru
e" : "false"); | |
551 } | |
552 stringBuilder.append('}'); | |
553 } | |
554 return stringBuilder.toString(); | |
555 } | |
556 | |
557 void DisplayItemList::showDebugData() const | |
558 { | |
559 WTFLogAlways("current display items: [%s]\n", displayItemsAsDebugString(m_cu
rrentPaintArtifact.displayItems()).utf8().data()); | |
560 WTFLogAlways("new display items: [%s]\n", displayItemsAsDebugString(m_newDis
playItems).utf8().data()); | |
561 } | |
562 | |
563 #endif // ifndef NDEBUG | |
564 | |
565 } // namespace blink | |
OLD | NEW |