1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
|
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "cc/playback/display_list_recording_source.h"
#include <algorithm>
#include "cc/base/histograms.h"
#include "cc/base/region.h"
#include "cc/layers/content_layer_client.h"
#include "cc/playback/display_item_list.h"
#include "cc/playback/display_list_raster_source.h"
#include "skia/ext/analysis_canvas.h"
namespace {
// Layout pixel buffer around the visible layer rect to record. Any base
// picture that intersects the visible layer rect expanded by this distance
// will be recorded.
const int kPixelDistanceToRecord = 8000;
// This is the distance, in layer space, by which the recorded viewport has to
// change before causing a paint of the new content. For example, it means
// that one has to scroll a very large page by 512 pixels before we will
// re-record a new DisplayItemList for an updated recorded viewport.
const int kMinimumDistanceBeforeUpdatingRecordedViewport = 512;
#ifdef NDEBUG
const bool kDefaultClearCanvasSetting = false;
#else
const bool kDefaultClearCanvasSetting = true;
#endif
DEFINE_SCOPED_UMA_HISTOGRAM_AREA_TIMER(
ScopedDisplayListRecordingSourceUpdateTimer,
"Compositing.DisplayListRecordingSource.UpdateUs",
"Compositing.DisplayListRecordingSource.UpdateInvalidatedAreaPerMs");
} // namespace
namespace cc {
DisplayListRecordingSource::DisplayListRecordingSource(
const gfx::Size& grid_cell_size)
: slow_down_raster_scale_factor_for_debug_(0),
gather_pixel_refs_(false),
requires_clear_(false),
is_solid_color_(false),
clear_canvas_with_debug_color_(kDefaultClearCanvasSetting),
solid_color_(SK_ColorTRANSPARENT),
background_color_(SK_ColorTRANSPARENT),
pixel_record_distance_(kPixelDistanceToRecord),
grid_cell_size_(grid_cell_size),
painter_reported_memory_usage_(0),
is_suitable_for_gpu_rasterization_(true) {}
DisplayListRecordingSource::~DisplayListRecordingSource() {
}
// This method only really makes sense to call if the size of the layer didn't
// change.
bool DisplayListRecordingSource::ExposesEnoughNewArea(
const gfx::Rect& current_recorded_viewport,
const gfx::Rect& potential_new_recorded_viewport,
const gfx::Size& layer_size) {
// If both are empty, nothing to do.
if (current_recorded_viewport.IsEmpty() &&
potential_new_recorded_viewport.IsEmpty())
return false;
// Re-record when going from empty to not-empty, to cover cases where
// the layer is recorded for the first time, or otherwise becomes visible.
if (current_recorded_viewport.IsEmpty())
return true;
// Re-record if the new viewport includes area outside of a skirt around the
// existing viewport.
gfx::Rect expanded_viewport(current_recorded_viewport);
expanded_viewport.Inset(-kMinimumDistanceBeforeUpdatingRecordedViewport,
-kMinimumDistanceBeforeUpdatingRecordedViewport);
if (!expanded_viewport.Contains(potential_new_recorded_viewport))
return true;
// Even if the new viewport doesn't include enough new area to satisfy the
// condition above, re-record anyway if touches a layer edge not touched by
// the existing viewport. Viewports are clipped to layer boundaries, so if the
// new viewport touches a layer edge not touched by the existing viewport,
// the new viewport must expose new area that touches this layer edge. Since
// this new area touches a layer edge, it's impossible to expose more area in
// that direction, so recording cannot be deferred until the exposed new area
// satisfies the condition above.
if (potential_new_recorded_viewport.x() == 0 &&
current_recorded_viewport.x() != 0)
return true;
if (potential_new_recorded_viewport.y() == 0 &&
current_recorded_viewport.y() != 0)
return true;
if (potential_new_recorded_viewport.right() == layer_size.width() &&
current_recorded_viewport.right() != layer_size.width())
return true;
if (potential_new_recorded_viewport.bottom() == layer_size.height() &&
current_recorded_viewport.bottom() != layer_size.height())
return true;
return false;
}
bool DisplayListRecordingSource::UpdateAndExpandInvalidation(
ContentLayerClient* painter,
Region* invalidation,
const gfx::Size& layer_size,
const gfx::Rect& visible_layer_rect,
int frame_number,
RecordingMode recording_mode) {
ScopedDisplayListRecordingSourceUpdateTimer timer;
bool updated = false;
if (size_ != layer_size) {
size_ = layer_size;
updated = true;
}
// The recorded viewport is the visible layer rect, expanded
// by the pixel record distance, up to a maximum of the total
// layer size.
gfx::Rect potential_new_recorded_viewport = visible_layer_rect;
potential_new_recorded_viewport.Inset(-pixel_record_distance_,
-pixel_record_distance_);
potential_new_recorded_viewport.Intersect(gfx::Rect(GetSize()));
if (updated ||
ExposesEnoughNewArea(recorded_viewport_, potential_new_recorded_viewport,
GetSize())) {
gfx::Rect old_recorded_viewport = recorded_viewport_;
recorded_viewport_ = potential_new_recorded_viewport;
// Invalidate newly-exposed and no-longer-exposed areas.
Region newly_exposed_region(recorded_viewport_);
newly_exposed_region.Subtract(old_recorded_viewport);
invalidation->Union(newly_exposed_region);
Region no_longer_exposed_region(old_recorded_viewport);
no_longer_exposed_region.Subtract(recorded_viewport_);
invalidation->Union(no_longer_exposed_region);
updated = true;
}
// Count the area that is being invalidated.
Region recorded_invalidation(*invalidation);
recorded_invalidation.Intersect(recorded_viewport_);
for (Region::Iterator it(recorded_invalidation); it.has_rect(); it.next())
timer.AddArea(it.rect().size().GetArea());
if (!updated && !invalidation->Intersects(recorded_viewport_))
return false;
ContentLayerClient::PaintingControlSetting painting_control =
ContentLayerClient::PAINTING_BEHAVIOR_NORMAL;
switch (recording_mode) {
case RECORD_NORMALLY:
// Already setup for normal recording.
break;
case RECORD_WITH_PAINTING_DISABLED:
painting_control = ContentLayerClient::DISPLAY_LIST_PAINTING_DISABLED;
break;
case RECORD_WITH_CACHING_DISABLED:
painting_control = ContentLayerClient::DISPLAY_LIST_CACHING_DISABLED;
break;
case RECORD_WITH_CONSTRUCTION_DISABLED:
painting_control = ContentLayerClient::DISPLAY_LIST_CONSTRUCTION_DISABLED;
break;
default:
// case RecordingSource::RECORD_WITH_SK_NULL_CANVAS should not be reached
NOTREACHED();
}
// TODO(vmpstr): Add a slow_down_recording_scale_factor_for_debug_ to be able
// to slow down recording.
display_list_ =
painter->PaintContentsToDisplayList(recorded_viewport_, painting_control);
painter_reported_memory_usage_ = painter->GetApproximateUnsharedMemoryUsage();
is_suitable_for_gpu_rasterization_ =
display_list_->IsSuitableForGpuRasterization();
DetermineIfSolidColor();
display_list_->EmitTraceSnapshot();
if (gather_pixel_refs_)
display_list_->GatherPixelRefs(grid_cell_size_);
return true;
}
gfx::Size DisplayListRecordingSource::GetSize() const {
return size_;
}
void DisplayListRecordingSource::SetEmptyBounds() {
size_ = gfx::Size();
Clear();
}
void DisplayListRecordingSource::SetSlowdownRasterScaleFactor(int factor) {
slow_down_raster_scale_factor_for_debug_ = factor;
}
void DisplayListRecordingSource::SetGatherPixelRefs(bool gather_pixel_refs) {
gather_pixel_refs_ = gather_pixel_refs;
}
void DisplayListRecordingSource::SetBackgroundColor(SkColor background_color) {
background_color_ = background_color;
}
void DisplayListRecordingSource::SetRequiresClear(bool requires_clear) {
requires_clear_ = requires_clear;
}
void DisplayListRecordingSource::SetUnsuitableForGpuRasterizationForTesting() {
is_suitable_for_gpu_rasterization_ = false;
}
bool DisplayListRecordingSource::IsSuitableForGpuRasterization() const {
return is_suitable_for_gpu_rasterization_;
}
scoped_refptr<RasterSource> DisplayListRecordingSource::CreateRasterSource(
bool can_use_lcd_text) const {
return scoped_refptr<RasterSource>(
DisplayListRasterSource::CreateFromDisplayListRecordingSource(
this, can_use_lcd_text));
}
gfx::Size DisplayListRecordingSource::GetTileGridSizeForTesting() const {
return gfx::Size();
}
void DisplayListRecordingSource::DetermineIfSolidColor() {
DCHECK(display_list_.get());
is_solid_color_ = false;
solid_color_ = SK_ColorTRANSPARENT;
if (!display_list_->ShouldBeAnalyzedForSolidColor())
return;
gfx::Size layer_size = GetSize();
skia::AnalysisCanvas canvas(layer_size.width(), layer_size.height());
display_list_->Raster(&canvas, nullptr, gfx::Rect(), 1.f);
is_solid_color_ = canvas.GetColorIfSolid(&solid_color_);
}
void DisplayListRecordingSource::Clear() {
recorded_viewport_ = gfx::Rect();
display_list_ = NULL;
painter_reported_memory_usage_ = 0;
is_solid_color_ = false;
}
} // namespace cc
|