1 // Copyright 2015 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 "third_party/blink/renderer/platform/graphics/paint/display_item_raster_invalidator.h"
6 
7 #include "third_party/blink/renderer/platform/graphics/paint/paint_artifact.h"
8 
9 namespace blink {
10 
Generate()11 void DisplayItemRasterInvalidator::Generate() {
12   struct OldAndNewDisplayItems {
13     const IntRect* old_visual_rect = nullptr;
14     const IntRect* new_visual_rect = nullptr;
15     PaintInvalidationReason reason = PaintInvalidationReason::kNone;
16   };
17   // If there are multiple display items changed for a client, the map will
18   // store only one (pair) of them because we only need the visual rect and the
19   // the multiple items have the same visual rect.
20   HashMap<const DisplayItemClient*, OldAndNewDisplayItems>
21       clients_to_invalidate;
22 
23   Vector<bool> old_display_items_matched;
24   old_display_items_matched.resize(old_chunk_.size());
25   auto next_old_item_to_match = old_chunk_.begin_index;
26   auto max_cached_old_index = next_old_item_to_match;
27 
28   for (const auto& new_item :
29        new_paint_artifact_.GetDisplayItemList().ItemsInPaintChunk(new_chunk_)) {
30     auto matched_old_index =
31         MatchNewDisplayItemInOldChunk(new_item, next_old_item_to_match);
32     if (matched_old_index == kNotFound) {
33       if (new_item.DrawsContent()) {
34         // Will invalidate for the new display item which doesn't match any old
35         // display item.
36         auto& value = clients_to_invalidate
37                           .insert(&new_item.Client(), OldAndNewDisplayItems())
38                           .stored_value->value;
39         if (!value.new_visual_rect) {
40           value.new_visual_rect = &new_item.VisualRect();
41           value.reason = new_item.Client().IsCacheable()
42                              ? PaintInvalidationReason::kAppeared
43                              : PaintInvalidationReason::kUncacheable;
44         }
45       }
46       continue;
47     }
48 
49     auto reason = new_item.Client().GetPaintInvalidationReason();
50     if (!IsFullPaintInvalidationReason(reason) &&
51         matched_old_index < max_cached_old_index) {
52       // |new_item| has been moved above other cached items.
53       reason = PaintInvalidationReason::kReordered;
54     }
55 
56     const auto& old_item =
57         old_paint_artifact_.GetDisplayItemList()[matched_old_index];
58     if (reason != PaintInvalidationReason::kNone &&
59         (old_item.DrawsContent() || new_item.DrawsContent())) {
60       // The display item reordered, skipped cache or changed. Will invalidate
61       // for both the old and new display items.
62       auto& value = clients_to_invalidate
63                         .insert(&new_item.Client(), OldAndNewDisplayItems())
64                         .stored_value->value;
65       if (old_item.IsTombstone() || old_item.DrawsContent())
66         value.old_visual_rect = &old_item.VisualRect();
67       if (new_item.DrawsContent())
68         value.new_visual_rect = &new_item.VisualRect();
69       value.reason = reason;
70     }
71 
72     wtf_size_t offset = matched_old_index - old_chunk_.begin_index;
73     DCHECK(!old_display_items_matched[offset]);
74     old_display_items_matched[offset] = true;
75 
76     // |old_item.IsTombstone()| is true means that |new_item| was copied from
77     // cached |old_item|.
78     if (old_item.IsTombstone())
79       max_cached_old_index = std::max(max_cached_old_index, matched_old_index);
80   }
81 
82   // Invalidate remaining unmatched (disappeared or uncacheable) old items.
83   for (auto i = old_chunk_.begin_index; i < old_chunk_.end_index; ++i) {
84     if (old_display_items_matched[i - old_chunk_.begin_index])
85       continue;
86 
87     const auto& old_item = old_paint_artifact_.GetDisplayItemList()[i];
88     if (old_item.DrawsContent() || old_item.IsTombstone()) {
89       clients_to_invalidate.insert(&old_item.Client(), OldAndNewDisplayItems())
90           .stored_value->value.old_visual_rect = &old_item.VisualRect();
91     }
92   }
93 
94   for (const auto& item : clients_to_invalidate) {
95     GenerateRasterInvalidation(*item.key, item.value.old_visual_rect,
96                                item.value.new_visual_rect, item.value.reason);
97   }
98 }
99 
MatchNewDisplayItemInOldChunk(const DisplayItem & new_item,wtf_size_t & next_old_item_to_match)100 wtf_size_t DisplayItemRasterInvalidator::MatchNewDisplayItemInOldChunk(
101     const DisplayItem& new_item,
102     wtf_size_t& next_old_item_to_match) {
103   if (!new_item.IsCacheable())
104     return kNotFound;
105   for (; next_old_item_to_match < old_chunk_.end_index;
106        next_old_item_to_match++) {
107     const auto& old_item =
108         old_paint_artifact_.GetDisplayItemList()[next_old_item_to_match];
109     if (!old_item.IsCacheable())
110       continue;
111     if (old_item.GetId() == new_item.GetId())
112       return next_old_item_to_match++;
113     // Add the skipped old item into index.
114     old_display_items_index_.insert(&old_item.Client(), Vector<wtf_size_t>())
115         .stored_value->value.push_back(next_old_item_to_match);
116   }
117 
118   // Didn't find matching old item in sequential matching. Look up the index.
119   auto it = old_display_items_index_.find(&new_item.Client());
120   if (it == old_display_items_index_.end())
121     return kNotFound;
122   for (auto i : it->value) {
123     const auto& old_item = old_paint_artifact_.GetDisplayItemList()[i];
124     if (old_item.GetId() == new_item.GetId())
125       return i;
126   }
127   return kNotFound;
128 }
129 
AddRasterInvalidation(const DisplayItemClient & client,const IntRect & rect,PaintInvalidationReason reason,RasterInvalidator::ClientIsOldOrNew old_or_new)130 void DisplayItemRasterInvalidator::AddRasterInvalidation(
131     const DisplayItemClient& client,
132     const IntRect& rect,
133     PaintInvalidationReason reason,
134     RasterInvalidator::ClientIsOldOrNew old_or_new) {
135   IntRect r = invalidator_.ClipByLayerBounds(mapper_.MapVisualRect(rect));
136   if (r.IsEmpty())
137     return;
138 
139   invalidator_.AddRasterInvalidation(raster_invalidation_function_, r, client,
140                                      reason, old_or_new);
141 }
142 
GenerateRasterInvalidation(const DisplayItemClient & client,const IntRect * old_visual_rect,const IntRect * new_visual_rect,PaintInvalidationReason reason)143 void DisplayItemRasterInvalidator::GenerateRasterInvalidation(
144     const DisplayItemClient& client,
145     const IntRect* old_visual_rect,
146     const IntRect* new_visual_rect,
147     PaintInvalidationReason reason) {
148   if (!new_visual_rect || new_visual_rect->IsEmpty()) {
149     if (old_visual_rect && !old_visual_rect->IsEmpty()) {
150       AddRasterInvalidation(client, *old_visual_rect,
151                             PaintInvalidationReason::kDisappeared,
152                             kClientIsOld);
153     }
154     return;
155   }
156 
157   if (!old_visual_rect || old_visual_rect->IsEmpty()) {
158     AddRasterInvalidation(client, *new_visual_rect,
159                           PaintInvalidationReason::kAppeared, kClientIsNew);
160     return;
161   }
162 
163   if (client.IsJustCreated()) {
164     // The old client has been deleted and the new client happens to be at the
165     // same address. They have no relationship.
166     AddRasterInvalidation(client, *old_visual_rect,
167                           PaintInvalidationReason::kDisappeared, kClientIsOld);
168     AddRasterInvalidation(client, *new_visual_rect,
169                           PaintInvalidationReason::kAppeared, kClientIsNew);
170     return;
171   }
172 
173   if (IsFullPaintInvalidationReason(reason)) {
174     GenerateFullRasterInvalidation(client, *old_visual_rect, *new_visual_rect,
175                                    reason);
176     return;
177   }
178 
179   GenerateIncrementalRasterInvalidation(client, *old_visual_rect,
180                                         *new_visual_rect);
181 
182   IntRect partial_rect = client.PartialInvalidationVisualRect();
183   if (!partial_rect.IsEmpty())
184     AddRasterInvalidation(client, partial_rect, reason, kClientIsNew);
185 }
186 
ComputeRightDelta(const IntPoint & location,const IntSize & old_size,const IntSize & new_size)187 static IntRect ComputeRightDelta(const IntPoint& location,
188                                  const IntSize& old_size,
189                                  const IntSize& new_size) {
190   int delta = new_size.Width() - old_size.Width();
191   if (delta > 0) {
192     return IntRect(location.X() + old_size.Width(), location.Y(), delta,
193                    new_size.Height());
194   }
195   if (delta < 0) {
196     return IntRect(location.X() + new_size.Width(), location.Y(), -delta,
197                    old_size.Height());
198   }
199   return IntRect();
200 }
201 
ComputeBottomDelta(const IntPoint & location,const IntSize & old_size,const IntSize & new_size)202 static IntRect ComputeBottomDelta(const IntPoint& location,
203                                   const IntSize& old_size,
204                                   const IntSize& new_size) {
205   int delta = new_size.Height() - old_size.Height();
206   if (delta > 0) {
207     return IntRect(location.X(), location.Y() + old_size.Height(),
208                    new_size.Width(), delta);
209   }
210   if (delta < 0) {
211     return IntRect(location.X(), location.Y() + new_size.Height(),
212                    old_size.Width(), -delta);
213   }
214   return IntRect();
215 }
216 
GenerateIncrementalRasterInvalidation(const DisplayItemClient & client,const IntRect & old_visual_rect,const IntRect & new_visual_rect)217 void DisplayItemRasterInvalidator::GenerateIncrementalRasterInvalidation(
218     const DisplayItemClient& client,
219     const IntRect& old_visual_rect,
220     const IntRect& new_visual_rect) {
221   DCHECK(old_visual_rect.Location() == new_visual_rect.Location());
222 
223   IntRect right_delta =
224       ComputeRightDelta(new_visual_rect.Location(), old_visual_rect.Size(),
225                         new_visual_rect.Size());
226   if (!right_delta.IsEmpty()) {
227     AddRasterInvalidation(client, right_delta,
228                           PaintInvalidationReason::kIncremental, kClientIsNew);
229   }
230 
231   IntRect bottom_delta =
232       ComputeBottomDelta(new_visual_rect.Location(), old_visual_rect.Size(),
233                          new_visual_rect.Size());
234   if (!bottom_delta.IsEmpty()) {
235     AddRasterInvalidation(client, bottom_delta,
236                           PaintInvalidationReason::kIncremental, kClientIsNew);
237   }
238 }
239 
GenerateFullRasterInvalidation(const DisplayItemClient & client,const IntRect & old_visual_rect,const IntRect & new_visual_rect,PaintInvalidationReason reason)240 void DisplayItemRasterInvalidator::GenerateFullRasterInvalidation(
241     const DisplayItemClient& client,
242     const IntRect& old_visual_rect,
243     const IntRect& new_visual_rect,
244     PaintInvalidationReason reason) {
245   if (!new_visual_rect.Contains(old_visual_rect)) {
246     AddRasterInvalidation(client, old_visual_rect, reason, kClientIsNew);
247     if (old_visual_rect.Contains(new_visual_rect))
248       return;
249   }
250 
251   AddRasterInvalidation(client, new_visual_rect, reason, kClientIsNew);
252 }
253 
254 }  // namespace blink
255