#include "cc/debug/micro_benchmark_impl.h"
#include "cc/debug/traced_value.h"
#include "cc/layers/append_quads_data.h"
+#include "cc/layers/solid_color_layer_impl.h"
+#include "cc/output/begin_frame_args.h"
#include "cc/quads/checkerboard_draw_quad.h"
#include "cc/quads/debug_border_draw_quad.h"
#include "cc/quads/picture_draw_quad.h"
layer_impl->twin_layer_ = NULL;
twin_layer_ = NULL;
- layer_impl->pile_ = pile_;
+ layer_impl->UpdatePile(pile_);
+ DCHECK(!pile_->is_solid_color() || !tilings_->num_tilings());
// Tilings would be expensive to push, so we swap.
layer_impl->tilings_.swap(tilings_);
+ layer_impl->tilings_->SetClient(layer_impl);
+ if (tilings_)
+ tilings_->SetClient(this);
+
+ // Ensure that the recycle tree doesn't have any unshared tiles.
+ if (tilings_ && pile_->is_solid_color())
+ tilings_->RemoveAllTilings();
// Remove invalidated tiles from what will become a recycle tree.
if (tilings_)
tilings_->RemoveTilesInRegion(invalidation_);
- layer_impl->tilings_->SetClient(layer_impl);
- if (tilings_)
- tilings_->SetClient(this);
-
layer_impl->raster_page_scale_ = raster_page_scale_;
layer_impl->raster_device_scale_ = raster_device_scale_;
layer_impl->raster_source_scale_ = raster_source_scale_;
needs_push_properties_ = true;
}
+void PictureLayerImpl::UpdatePile(scoped_refptr<PicturePileImpl> pile) {
+ bool could_have_tilings = CanHaveTilings();
+ pile_.swap(pile);
+
+ // Need to call UpdateTiles again if CanHaveTilings changed.
+ if (could_have_tilings != CanHaveTilings()) {
+ layer_tree_impl()->set_needs_update_draw_properties();
+ }
+}
+
void PictureLayerImpl::AppendQuads(
RenderPass* render_pass,
const OcclusionTracker<LayerImpl>& occlusion_tracker,
AppendQuadsData* append_quads_data) {
DCHECK(!needs_post_commit_initialization_);
+ SharedQuadState* shared_quad_state =
+ render_pass->CreateAndAppendSharedQuadState();
+
+ if (pile_->is_solid_color()) {
+ PopulateSharedQuadState(shared_quad_state);
+
+ AppendDebugBorderQuad(
+ render_pass, content_bounds(), shared_quad_state, append_quads_data);
+
+ SolidColorLayerImpl::AppendSolidQuads(
+ render_pass,
+ occlusion_tracker,
+ shared_quad_state,
+ visible_content_rect(),
+ draw_properties().target_space_transform,
+ pile_->solid_color());
+ return;
+ }
+
float max_contents_scale = MaximumTilingContentsScale();
gfx::Transform scaled_draw_transform = draw_transform();
scaled_draw_transform.Scale(SK_MScalar1 / max_contents_scale,
gfx::ScaleToEnclosingRect(visible_content_rect(), max_contents_scale);
scaled_visible_content_rect.Intersect(gfx::Rect(scaled_content_bounds));
- SharedQuadState* shared_quad_state =
- render_pass->CreateAndAppendSharedQuadState();
+ Occlusion occlusion =
+ occlusion_tracker.GetCurrentOcclusionForLayer(scaled_draw_transform);
+
shared_quad_state->SetAll(scaled_draw_transform,
scaled_content_bounds,
scaled_visible_content_rect,
gfx::Rect geometry_rect = scaled_visible_content_rect;
gfx::Rect opaque_rect = contents_opaque() ? geometry_rect : gfx::Rect();
- gfx::Rect visible_geometry_rect = occlusion_tracker.UnoccludedContentRect(
- geometry_rect, scaled_draw_transform);
+ gfx::Rect visible_geometry_rect =
+ occlusion.GetUnoccludedContentRect(geometry_rect);
if (visible_geometry_rect.IsEmpty())
return;
iter;
++iter) {
gfx::Rect geometry_rect = iter.geometry_rect();
- gfx::Rect visible_geometry_rect = occlusion_tracker.UnoccludedContentRect(
- geometry_rect, scaled_draw_transform);
+ gfx::Rect opaque_rect = contents_opaque() ? geometry_rect : gfx::Rect();
+ gfx::Rect visible_geometry_rect =
+ occlusion.GetUnoccludedContentRect(geometry_rect);
if (visible_geometry_rect.IsEmpty())
continue;
switch (tile_version.mode()) {
case ManagedTileState::TileVersion::RESOURCE_MODE: {
gfx::RectF texture_rect = iter.texture_rect();
- gfx::Rect opaque_rect = iter->opaque_rect();
- opaque_rect.Intersect(geometry_rect);
- if (iter->contents_scale() != ideal_contents_scale_ &&
+ // The raster_contents_scale_ is the best scale that the layer is
+ // trying to produce, even though it may not be ideal. Since that's
+ // the best the layer can promise in the future, consider those as
+ // complete. But if a tile is ideal scale, we don't want to consider
+ // it incomplete and trying to replace it with a tile at a worse
+ // scale.
+ if (iter->contents_scale() != raster_contents_scale_ &&
+ iter->contents_scale() != ideal_contents_scale_ &&
geometry_rect.Intersects(scaled_viewport_for_tile_priority)) {
append_quads_data->num_incomplete_tiles++;
}
}
gfx::RectF texture_rect = iter.texture_rect();
- gfx::Rect opaque_rect = iter->opaque_rect();
- opaque_rect.Intersect(geometry_rect);
ResourceProvider* resource_provider =
layer_tree_impl()->resource_provider();
CleanUpTilingsOnActiveLayer(seen_tilings);
}
-void PictureLayerImpl::UpdateTiles(
- const OcclusionTracker<LayerImpl>* occlusion_tracker) {
+void PictureLayerImpl::UpdateTiles(const Occlusion& occlusion_in_content_space,
+ bool resourceless_software_draw) {
TRACE_EVENT0("cc", "PictureLayerImpl::UpdateTiles");
+ DCHECK_EQ(1.f, contents_scale_x());
+ DCHECK_EQ(1.f, contents_scale_y());
DoPostCommitInitializationIfNeeded();
- // TODO(danakj): We should always get an occlusion tracker when we are using
- // occlusion, so update this check when we don't use a pending tree in the
- // browser compositor.
- DCHECK(!occlusion_tracker ||
- layer_tree_impl()->settings().use_occlusion_for_tile_prioritization);
-
- // Transforms and viewport are invalid for tile management inside a
- // resourceless software draw, so don't update them.
- if (!layer_tree_impl()->resourceless_software_draw()) {
+ // Any draw properties derived from |transform|, |viewport|, and |clip|
+ // parameters in LayerTreeHostImpl::SetExternalDrawConstraints are not valid
+ // for prioritizing tiles during resourceless software draws. This is because
+ // resourceless software draws can have wildly different transforms/viewports
+ // from regular draws.
+ if (!resourceless_software_draw) {
visible_rect_for_tile_priority_ = visible_content_rect();
- viewport_rect_for_tile_priority_ =
- layer_tree_impl()->ViewportRectForTilePriority();
- screen_space_transform_for_tile_priority_ = screen_space_transform();
}
+ viewport_rect_for_tile_priority_ =
+ layer_tree_impl()->ViewportRectForTilePriority();
+ screen_space_transform_for_tile_priority_ = screen_space_transform();
if (!CanHaveTilings()) {
ideal_page_scale_ = 0.f;
should_update_tile_priorities_ = true;
- UpdateTilePriorities(occlusion_tracker);
+ UpdateTilePriorities(occlusion_in_content_space);
if (layer_tree_impl()->IsPendingTree())
MarkVisibleResourcesAsRequired();
}
void PictureLayerImpl::UpdateTilePriorities(
- const OcclusionTracker<LayerImpl>* occlusion_tracker) {
+ const Occlusion& occlusion_in_content_space) {
+ DCHECK(!pile_->is_solid_color() || !tilings_->num_tilings());
+
TRACE_EVENT0("cc", "PictureLayerImpl::UpdateTilePriorities");
double current_frame_time_in_seconds =
- (layer_tree_impl()->CurrentFrameTimeTicks() -
+ (layer_tree_impl()->CurrentBeginFrameArgs().frame_time -
base::TimeTicks()).InSecondsF();
+ gfx::Rect viewport_rect_in_layer_space =
+ GetViewportForTilePriorityInContentSpace();
bool tiling_needs_update = false;
for (size_t i = 0; i < tilings_->num_tilings(); ++i) {
- if (tilings_->tiling_at(i)->NeedsUpdateForFrameAtTime(
- current_frame_time_in_seconds)) {
+ if (tilings_->tiling_at(i)->NeedsUpdateForFrameAtTimeAndViewport(
+ current_frame_time_in_seconds, viewport_rect_in_layer_space)) {
tiling_needs_update = true;
break;
}
if (!tiling_needs_update)
return;
- gfx::Rect visible_rect_in_content_space(
- GetViewportForTilePriorityInContentSpace());
- visible_rect_in_content_space.Intersect(visible_content_rect());
- gfx::Rect visible_layer_rect = gfx::ScaleToEnclosingRect(
- visible_rect_in_content_space, 1.f / contents_scale_x());
WhichTree tree =
layer_tree_impl()->IsActiveTree() ? ACTIVE_TREE : PENDING_TREE;
for (size_t i = 0; i < tilings_->num_tilings(); ++i) {
+ // Pass |occlusion_in_content_space| for |occlusion_in_layer_space| since
+ // they are the same space in picture lbayer, as contents scale is always 1.
tilings_->tiling_at(i)->UpdateTilePriorities(tree,
- visible_layer_rect,
+ viewport_rect_in_layer_space,
ideal_contents_scale_,
current_frame_time_in_seconds,
- occlusion_tracker,
- render_target(),
- draw_transform());
+ occlusion_in_content_space);
}
// Tile priorities were modified.
visible_rect_in_content_space =
gfx::ToEnclosingRect(MathUtil::ProjectClippedRect(
view_to_layer, viewport_rect_for_tile_priority_));
-
- visible_rect_in_content_space.Intersect(gfx::Rect(content_bounds()));
}
}
-
return visible_rect_in_content_space;
}
scoped_refptr<Tile> PictureLayerImpl::CreateTile(PictureLayerTiling* tiling,
const gfx::Rect& content_rect) {
+ DCHECK(!pile_->is_solid_color());
if (!pile_->CanRaster(tiling->contents_scale(), content_rect))
return scoped_refptr<Tile>();
+ int flags = 0;
+
// TODO(vmpstr): Revisit this. For now, enabling analysis means that we get as
// much savings on memory as we can. However, for some cases like ganesh or
// small layers, the amount of time we spend analyzing might not justify
// memory savings that we can get. Note that we don't handle solid color
// masks, so we shouldn't bother analyzing those.
// Bugs: crbug.com/397198, crbug.com/396908
- int flags = 0;
if (!pile_->is_mask())
flags = Tile::USE_PICTURE_ANALYSIS;
pile_.get(),
content_rect.size(),
content_rect,
- contents_opaque() ? content_rect : gfx::Rect(),
tiling->contents_scale(),
id(),
layer_tree_impl()->source_frame_number(),
const PictureLayerTiling* tiling) const {
if (!twin_layer_)
return NULL;
- for (size_t i = 0; i < twin_layer_->tilings_->num_tilings(); ++i)
- if (twin_layer_->tilings_->tiling_at(i)->contents_scale() ==
- tiling->contents_scale())
- return twin_layer_->tilings_->tiling_at(i);
- return NULL;
+ return twin_layer_->tilings_->TilingAtScale(tiling->contents_scale());
}
PictureLayerTiling* PictureLayerImpl::GetRecycledTwinTiling(
// when we stop using the pending tree in the browser compositor. If we want
// to support occlusion tracking here, we need to dirty the draw properties
// or save occlusion as a draw property.
- UpdateTilePriorities(NULL);
+ UpdateTilePriorities(Occlusion());
}
}
ResourceProvider::ResourceId PictureLayerImpl::ContentsResourceId() const {
gfx::Rect content_rect(content_bounds());
- float scale = MaximumTilingContentsScale();
PictureLayerTilingSet::CoverageIterator iter(
- tilings_.get(), scale, content_rect, ideal_contents_scale_);
+ tilings_.get(), 1.f, content_rect, ideal_contents_scale_);
// Mask resource not ready yet.
if (!iter || !*iter)
// higher res on the active tree to a lower res on the pending tree.
// First, early out for layers with no visible content.
- if (visible_content_rect().IsEmpty())
+ if (visible_rect_for_tile_priority_.IsEmpty())
return;
- gfx::Rect rect(visible_content_rect());
-
// Only mark tiles inside the viewport for tile priority as required for
// activation. This viewport is normally the same as the draw viewport but
// can be independently overridden by embedders like Android WebView with
// SetExternalDrawConstraints.
- rect.Intersect(GetViewportForTilePriorityInContentSpace());
+ gfx::Rect rect = GetViewportForTilePriorityInContentSpace();
+ rect.Intersect(visible_rect_for_tile_priority_);
float min_acceptable_scale =
std::min(raster_contents_scale_, ideal_contents_scale_);
high_res = tiling;
continue;
}
- for (PictureLayerTiling::CoverageIterator iter(tiling,
- contents_scale_x(),
- rect);
- iter;
+ for (PictureLayerTiling::CoverageIterator iter(tiling, 1.f, rect); iter;
++iter) {
if (!*iter || !iter->IsReadyToDraw())
continue;
// As a second pass, mark as required any visible high res tiles not filled in
// by acceptable non-ideal tiles from the first pass.
if (MarkVisibleTilesAsRequired(
- high_res, twin_high_res, contents_scale_x(), rect, missing_region)) {
+ high_res, twin_high_res, rect, missing_region)) {
// As an optional third pass, if a high res tile was skipped because its
// twin was also missing, then fall back to mark low res tiles as required
// in case the active twin is substituting those for missing high res
// content. Only suitable, when low res is enabled.
if (low_res) {
- MarkVisibleTilesAsRequired(
- low_res, twin_low_res, contents_scale_x(), rect, missing_region);
+ MarkVisibleTilesAsRequired(low_res, twin_low_res, rect, missing_region);
}
}
}
bool PictureLayerImpl::MarkVisibleTilesAsRequired(
PictureLayerTiling* tiling,
const PictureLayerTiling* optional_twin_tiling,
- float contents_scale,
const gfx::Rect& rect,
const Region& missing_region) const {
bool twin_had_missing_tile = false;
- for (PictureLayerTiling::CoverageIterator iter(tiling,
- contents_scale,
- rect);
- iter;
+ for (PictureLayerTiling::CoverageIterator iter(tiling, 1.f, rect); iter;
++iter) {
Tile* tile = *iter;
// A null tile (i.e. missing recording) can just be skipped.
if (optional_twin_tiling) {
Tile* twin_tile = optional_twin_tiling->TileAt(iter.i(), iter.j());
if (!twin_tile || twin_tile == tile) {
- twin_had_missing_tile = true;
+ // However if the shared tile is being used on the active tree, then
+ // there's no missing content in this place, and low res is not needed.
+ if (!twin_tile || !twin_tile->IsReadyToDraw())
+ twin_had_missing_tile = true;
continue;
}
}
draw_properties().screen_space_transform_is_animating)
return true;
+ if (draw_properties().screen_space_transform_is_animating &&
+ raster_contents_scale_ != ideal_contents_scale_ &&
+ ShouldAdjustRasterScaleDuringScaleAnimations())
+ return true;
+
bool is_pinching = layer_tree_impl()->PinchGestureActive();
if (is_pinching && raster_page_scale_) {
// We change our raster scale when it is:
// TODO(danakj): Adjust raster source scale closer to ideal source scale at
// a throttled rate. Possibly make use of invalidation_.IsEmpty() on pending
// tree. This will allow CSS scale changes to get re-rastered at an
- // appropriate rate.
+ // appropriate rate. (crbug.com/413636)
if (raster_source_scale_is_fixed_) {
raster_contents_scale_ /= raster_source_scale_;
raster_source_scale_ = 1.f;
raster_contents_scale_ =
std::max(raster_contents_scale_, MinimumContentsScale());
- // Since we're not re-rasterizing during animation, rasterize at the maximum
+ // If we're not re-rasterizing during animation, rasterize at the maximum
// scale that will occur during the animation, if the maximum scale is
- // known. However, to avoid excessive memory use, don't rasterize at a scale
- // at which this layer would become larger than the viewport.
- if (draw_properties().screen_space_transform_is_animating) {
+ // known. However we want to avoid excessive memory use. If the scale is
+ // smaller than what we would choose otherwise, then it's always better off
+ // for us memory-wise. But otherwise, we don't choose a scale at which this
+ // layer's rastered content would become larger than the viewport.
+ if (draw_properties().screen_space_transform_is_animating &&
+ !ShouldAdjustRasterScaleDuringScaleAnimations()) {
bool can_raster_at_maximum_scale = false;
- if (draw_properties().maximum_animation_contents_scale > 0.f) {
- gfx::Size bounds_at_maximum_scale = gfx::ToCeiledSize(gfx::ScaleSize(
- bounds(), draw_properties().maximum_animation_contents_scale));
+ // TODO(ajuma): If we need to deal with scale-down animations starting right
+ // as a layer gets promoted, then we'd want to have the
+ // |starting_animation_contents_scale| passed in here as a separate draw
+ // property so we could try use that when the max is too large.
+ // See crbug.com/422341.
+ float maximum_scale = draw_properties().maximum_animation_contents_scale;
+ if (maximum_scale) {
+ gfx::Size bounds_at_maximum_scale =
+ gfx::ToCeiledSize(gfx::ScaleSize(bounds(), maximum_scale));
if (bounds_at_maximum_scale.GetArea() <=
layer_tree_impl()->device_viewport_size().GetArea())
can_raster_at_maximum_scale = true;
}
- if (can_raster_at_maximum_scale) {
- raster_contents_scale_ =
- std::max(raster_contents_scale_,
- draw_properties().maximum_animation_contents_scale);
- } else {
- raster_contents_scale_ =
- std::max(raster_contents_scale_,
- 1.f * ideal_page_scale_ * ideal_device_scale_);
- }
+ // Use the computed scales for the raster scale directly, do not try to use
+ // the ideal scale here. The current ideal scale may be way too large in the
+ // case of an animation with scale, and will be constantly changing.
+ if (can_raster_at_maximum_scale)
+ raster_contents_scale_ = maximum_scale;
+ else
+ raster_contents_scale_ = 1.f * ideal_page_scale_ * ideal_device_scale_;
}
// If this layer would create zero or one tiles at this content scale,
}
bool PictureLayerImpl::CanHaveTilings() const {
+ if (pile_->is_solid_color())
+ return false;
if (!DrawsContent())
return false;
if (!pile_->HasRecordings())
#endif
}
+bool PictureLayerImpl::ShouldAdjustRasterScaleDuringScaleAnimations() const {
+ if (!layer_tree_impl()->use_gpu_rasterization())
+ return false;
+
+ // Re-rastering text at different scales using GPU rasterization causes
+ // texture uploads for glyphs at each scale (see crbug.com/366225). To
+ // workaround this performance issue, we don't re-rasterize layers with
+ // text during scale animations.
+ // TODO(ajuma): Remove this workaround once text can be efficiently
+ // re-rastered at different scales (e.g. by using distance-field fonts).
+ if (pile_->has_text())
+ return false;
+
+ return true;
+}
+
float PictureLayerImpl::MaximumTilingContentsScale() const {
float max_contents_scale = MinimumContentsScale();
for (size_t i = 0; i < tilings_->num_tilings(); ++i) {
tilings_->AsValueInto(state);
state->EndArray();
+ state->BeginArray("tile_priority_rect");
+ MathUtil::AddToTracedValue(GetViewportForTilePriorityInContentSpace(), state);
+ state->EndArray();
+
+ state->BeginArray("visible_rect");
+ MathUtil::AddToTracedValue(visible_content_rect(), state);
+ state->EndArray();
+
state->BeginArray("pictures");
pile_->AsValueInto(state);
state->EndArray();
state->BeginArray("coverage_tiles");
for (PictureLayerTilingSet::CoverageIterator iter(tilings_.get(),
- contents_scale_x(),
+ 1.f,
gfx::Rect(content_bounds()),
ideal_contents_scale_);
iter;
if (!tilings_)
return true;
- if (visible_content_rect().IsEmpty())
+ if (visible_rect_for_tile_priority_.IsEmpty())
return true;
+ gfx::Rect rect = GetViewportForTilePriorityInContentSpace();
+ rect.Intersect(visible_rect_for_tile_priority_);
+
for (size_t i = 0; i < tilings_->num_tilings(); ++i) {
PictureLayerTiling* tiling = tilings_->tiling_at(i);
if (tiling->resolution() != HIGH_RESOLUTION &&
tiling->resolution() != LOW_RESOLUTION)
continue;
- gfx::Rect rect(visible_content_rect());
- for (PictureLayerTiling::CoverageIterator iter(
- tiling, contents_scale_x(), rect);
- iter;
+ for (PictureLayerTiling::CoverageIterator iter(tiling, 1.f, rect); iter;
++iter) {
const Tile* tile = *iter;
// A null tile (i.e. missing recording) can just be skipped.
IteratorType index = stages_[current_stage_].iterator_type;
TilePriority::PriorityBin tile_type = stages_[current_stage_].tile_type;
if (!iterators_[index] || iterators_[index].get_type() != tile_type)
- ++(*this);
+ AdvanceToNextStage();
}
PictureLayerImpl::LayerRasterTileIterator::~LayerRasterTileIterator() {}
TilePriority::PriorityBin tile_type = stages_[current_stage_].tile_type;
// First advance the iterator.
- if (iterators_[index])
- ++iterators_[index];
-
- if (iterators_[index] && iterators_[index].get_type() == tile_type)
- return *this;
+ DCHECK(iterators_[index]);
+ DCHECK(iterators_[index].get_type() == tile_type);
+ ++iterators_[index];
- // Next, advance the stage.
- ++current_stage_;
- while (current_stage_ < arraysize(stages_)) {
- index = stages_[current_stage_].iterator_type;
- tile_type = stages_[current_stage_].tile_type;
+ if (!iterators_[index] || iterators_[index].get_type() != tile_type)
+ AdvanceToNextStage();
- if (iterators_[index] && iterators_[index].get_type() == tile_type)
- break;
- ++current_stage_;
- }
return *this;
}
return *iterators_[index];
}
+void PictureLayerImpl::LayerRasterTileIterator::AdvanceToNextStage() {
+ DCHECK_LT(current_stage_, arraysize(stages_));
+ ++current_stage_;
+ while (current_stage_ < arraysize(stages_)) {
+ IteratorType index = stages_[current_stage_].iterator_type;
+ TilePriority::PriorityBin tile_type = stages_[current_stage_].tile_type;
+
+ if (iterators_[index] && iterators_[index].get_type() == tile_type)
+ break;
+ ++current_stage_;
+ }
+}
+
PictureLayerImpl::LayerEvictionTileIterator::LayerEvictionTileIterator()
: layer_(NULL),
tree_priority_(SAME_PRIORITY_FOR_BOTH_TREES),