Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(777)

Side by Side Diff: chrome/renderer/paint_aggregator.cc

Issue 6685072: Move a bunch more random files from chrome\renderer to content\renderer. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 9 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « chrome/renderer/paint_aggregator.h ('k') | chrome/renderer/paint_aggregator_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2009 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 "chrome/renderer/paint_aggregator.h"
6
7 #include "base/logging.h"
8 #include "base/metrics/histogram.h"
9
10 // ----------------------------------------------------------------------------
11 // ALGORITHM NOTES
12 //
13 // We attempt to maintain a scroll rect in the presence of invalidations that
14 // are contained within the scroll rect. If an invalidation crosses a scroll
15 // rect, then we just treat the scroll rect as an invalidation rect.
16 //
17 // For invalidations performed prior to scrolling and contained within the
18 // scroll rect, we offset the invalidation rects to account for the fact that
19 // the consumer will perform scrolling before painting.
20 //
21 // We only support scrolling along one axis at a time. A diagonal scroll will
22 // therefore be treated as an invalidation.
23 // ----------------------------------------------------------------------------
24
25 // If the combined area of paint rects contained within the scroll rect grows
26 // too large, then we might as well just treat the scroll rect as a paint rect.
27 // This constant sets the max ratio of paint rect area to scroll rect area that
28 // we will tolerate before dograding the scroll into a repaint.
29 static const float kMaxRedundantPaintToScrollArea = 0.8f;
30
31 // The maximum number of paint rects. If we exceed this limit, then we'll
32 // start combining paint rects (see CombinePaintRects). This limiting is
33 // important since the WebKit code associated with deciding what to paint given
34 // a paint rect can be significant.
35 static const size_t kMaxPaintRects = 5;
36
37 // If the combined area of paint rects divided by the area of the union of all
38 // paint rects exceeds this threshold, then we will combine the paint rects.
39 static const float kMaxPaintRectsAreaRatio = 0.7f;
40
41 PaintAggregator::PendingUpdate::PendingUpdate() {}
42
43 PaintAggregator::PendingUpdate::~PendingUpdate() {}
44
45 gfx::Rect PaintAggregator::PendingUpdate::GetScrollDamage() const {
46 // Should only be scrolling in one direction at a time.
47 DCHECK(!(scroll_delta.x() && scroll_delta.y()));
48
49 gfx::Rect damaged_rect;
50
51 // Compute the region we will expose by scrolling, and paint that into a
52 // shared memory section.
53 if (scroll_delta.x()) {
54 int dx = scroll_delta.x();
55 damaged_rect.set_y(scroll_rect.y());
56 damaged_rect.set_height(scroll_rect.height());
57 if (dx > 0) {
58 damaged_rect.set_x(scroll_rect.x());
59 damaged_rect.set_width(dx);
60 } else {
61 damaged_rect.set_x(scroll_rect.right() + dx);
62 damaged_rect.set_width(-dx);
63 }
64 } else {
65 int dy = scroll_delta.y();
66 damaged_rect.set_x(scroll_rect.x());
67 damaged_rect.set_width(scroll_rect.width());
68 if (dy > 0) {
69 damaged_rect.set_y(scroll_rect.y());
70 damaged_rect.set_height(dy);
71 } else {
72 damaged_rect.set_y(scroll_rect.bottom() + dy);
73 damaged_rect.set_height(-dy);
74 }
75 }
76
77 // In case the scroll offset exceeds the width/height of the scroll rect
78 return scroll_rect.Intersect(damaged_rect);
79 }
80
81 gfx::Rect PaintAggregator::PendingUpdate::GetPaintBounds() const {
82 gfx::Rect bounds;
83 for (size_t i = 0; i < paint_rects.size(); ++i)
84 bounds = bounds.Union(paint_rects[i]);
85 return bounds;
86 }
87
88 bool PaintAggregator::HasPendingUpdate() const {
89 return !update_.scroll_rect.IsEmpty() || !update_.paint_rects.empty();
90 }
91
92 void PaintAggregator::ClearPendingUpdate() {
93 update_ = PendingUpdate();
94 }
95
96 void PaintAggregator::PopPendingUpdate(PendingUpdate* update) {
97 // Combine paint rects if their combined area is not sufficiently less than
98 // the area of the union of all paint rects. We skip this if there is a
99 // scroll rect since scrolling benefits from smaller paint rects.
100 if (update_.scroll_rect.IsEmpty() && update_.paint_rects.size() > 1) {
101 int paint_area = 0;
102 gfx::Rect union_rect;
103 for (size_t i = 0; i < update_.paint_rects.size(); ++i) {
104 paint_area += update_.paint_rects[i].size().GetArea();
105 union_rect = union_rect.Union(update_.paint_rects[i]);
106 }
107 int union_area = union_rect.size().GetArea();
108 if (float(paint_area) / float(union_area) > kMaxPaintRectsAreaRatio)
109 CombinePaintRects();
110 }
111 *update = update_;
112 ClearPendingUpdate();
113 }
114
115 void PaintAggregator::InvalidateRect(const gfx::Rect& rect) {
116 // Combine overlapping paints using smallest bounding box.
117 for (size_t i = 0; i < update_.paint_rects.size(); ++i) {
118 const gfx::Rect& existing_rect = update_.paint_rects[i];
119 if (existing_rect.Contains(rect)) // Optimize out redundancy.
120 return;
121 if (rect.Intersects(existing_rect) || rect.SharesEdgeWith(existing_rect)) {
122 // Re-invalidate in case the union intersects other paint rects.
123 gfx::Rect combined_rect = existing_rect.Union(rect);
124 update_.paint_rects.erase(update_.paint_rects.begin() + i);
125 InvalidateRect(combined_rect);
126 return;
127 }
128 }
129
130 // Add a non-overlapping paint.
131 update_.paint_rects.push_back(rect);
132
133 // If the new paint overlaps with a scroll, then it forces an invalidation of
134 // the scroll. If the new paint is contained by a scroll, then trim off the
135 // scroll damage to avoid redundant painting.
136 if (!update_.scroll_rect.IsEmpty()) {
137 if (ShouldInvalidateScrollRect(rect)) {
138 InvalidateScrollRect();
139 } else if (update_.scroll_rect.Contains(rect)) {
140 update_.paint_rects[update_.paint_rects.size() - 1] =
141 rect.Subtract(update_.GetScrollDamage());
142 if (update_.paint_rects[update_.paint_rects.size() - 1].IsEmpty())
143 update_.paint_rects.erase(update_.paint_rects.end() - 1);
144 }
145 }
146
147 if (update_.paint_rects.size() > kMaxPaintRects)
148 CombinePaintRects();
149
150 // Track how large the paint_rects vector grows during an invalidation
151 // sequence. Note: A subsequent invalidation may end up being combined
152 // with all existing paints, which means that tracking the size of
153 // paint_rects at the time when PopPendingUpdate() is called may mask
154 // certain performance problems.
155 HISTOGRAM_COUNTS_100("MPArch.RW_IntermediatePaintRectCount",
156 update_.paint_rects.size());
157 }
158
159 void PaintAggregator::ScrollRect(int dx, int dy, const gfx::Rect& clip_rect) {
160 // We only support scrolling along one axis at a time.
161 if (dx != 0 && dy != 0) {
162 InvalidateRect(clip_rect);
163 return;
164 }
165
166 // We can only scroll one rect at a time.
167 if (!update_.scroll_rect.IsEmpty() &&
168 !update_.scroll_rect.Equals(clip_rect)) {
169 InvalidateRect(clip_rect);
170 return;
171 }
172
173 // Again, we only support scrolling along one axis at a time. Make sure this
174 // update doesn't scroll on a different axis than any existing one.
175 if ((dx && update_.scroll_delta.y()) || (dy && update_.scroll_delta.x())) {
176 InvalidateRect(clip_rect);
177 return;
178 }
179
180 // The scroll rect is new or isn't changing (though the scroll amount may
181 // be changing).
182 update_.scroll_rect = clip_rect;
183 update_.scroll_delta.Offset(dx, dy);
184
185 // We might have just wiped out a pre-existing scroll.
186 if (update_.scroll_delta == gfx::Point()) {
187 update_.scroll_rect = gfx::Rect();
188 return;
189 }
190
191 // Adjust any contained paint rects and check for any overlapping paints.
192 for (size_t i = 0; i < update_.paint_rects.size(); ++i) {
193 if (update_.scroll_rect.Contains(update_.paint_rects[i])) {
194 update_.paint_rects[i] = ScrollPaintRect(update_.paint_rects[i], dx, dy);
195 // The rect may have been scrolled out of view.
196 if (update_.paint_rects[i].IsEmpty()) {
197 update_.paint_rects.erase(update_.paint_rects.begin() + i);
198 i--;
199 }
200 } else if (update_.scroll_rect.Intersects(update_.paint_rects[i])) {
201 InvalidateScrollRect();
202 return;
203 }
204 }
205
206 // If the new scroll overlaps too much with contained paint rects, then force
207 // an invalidation of the scroll.
208 if (ShouldInvalidateScrollRect(gfx::Rect()))
209 InvalidateScrollRect();
210 }
211
212 gfx::Rect PaintAggregator::ScrollPaintRect(const gfx::Rect& paint_rect,
213 int dx, int dy) const {
214 gfx::Rect result = paint_rect;
215
216 result.Offset(dx, dy);
217 result = update_.scroll_rect.Intersect(result);
218
219 // Subtract out the scroll damage rect to avoid redundant painting.
220 return result.Subtract(update_.GetScrollDamage());
221 }
222
223 bool PaintAggregator::ShouldInvalidateScrollRect(const gfx::Rect& rect) const {
224 if (!rect.IsEmpty()) {
225 if (!update_.scroll_rect.Intersects(rect))
226 return false;
227
228 if (!update_.scroll_rect.Contains(rect))
229 return true;
230 }
231
232 // Check if the combined area of all contained paint rects plus this new
233 // rect comes too close to the area of the scroll_rect. If so, then we
234 // might as well invalidate the scroll rect.
235
236 int paint_area = rect.size().GetArea();
237 for (size_t i = 0; i < update_.paint_rects.size(); ++i) {
238 const gfx::Rect& existing_rect = update_.paint_rects[i];
239 if (update_.scroll_rect.Contains(existing_rect))
240 paint_area += existing_rect.size().GetArea();
241 }
242 int scroll_area = update_.scroll_rect.size().GetArea();
243 if (float(paint_area) / float(scroll_area) > kMaxRedundantPaintToScrollArea)
244 return true;
245
246 return false;
247 }
248
249 void PaintAggregator::InvalidateScrollRect() {
250 gfx::Rect scroll_rect = update_.scroll_rect;
251 update_.scroll_rect = gfx::Rect();
252 update_.scroll_delta = gfx::Point();
253 InvalidateRect(scroll_rect);
254 }
255
256 void PaintAggregator::CombinePaintRects() {
257 // Combine paint rects do to at most two rects: one inside the scroll_rect
258 // and one outside the scroll_rect. If there is no scroll_rect, then just
259 // use the smallest bounding box for all paint rects.
260 //
261 // NOTE: This is a fairly simple algorithm. We could get fancier by only
262 // combining two rects to get us under the kMaxPaintRects limit, but if we
263 // reach this method then it means we're hitting a rare case, so there's no
264 // need to over-optimize it.
265 //
266 if (update_.scroll_rect.IsEmpty()) {
267 gfx::Rect bounds = update_.GetPaintBounds();
268 update_.paint_rects.clear();
269 update_.paint_rects.push_back(bounds);
270 } else {
271 gfx::Rect inner, outer;
272 for (size_t i = 0; i < update_.paint_rects.size(); ++i) {
273 const gfx::Rect& existing_rect = update_.paint_rects[i];
274 if (update_.scroll_rect.Contains(existing_rect)) {
275 inner = inner.Union(existing_rect);
276 } else {
277 outer = outer.Union(existing_rect);
278 }
279 }
280 update_.paint_rects.clear();
281 update_.paint_rects.push_back(inner);
282 update_.paint_rects.push_back(outer);
283 }
284 }
OLDNEW
« no previous file with comments | « chrome/renderer/paint_aggregator.h ('k') | chrome/renderer/paint_aggregator_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698