This source file includes following definitions.
- non_occluding_screen_space_rects_
- EnterLayer
- LeaveLayer
- ScreenSpaceClipRectInTargetSurface
- TransformSurfaceOpaqueRegion
- LayerOpacityKnown
- LayerOpacityKnown
- LayerTransformsToTargetKnown
- LayerTransformsToTargetKnown
- SurfaceOpacityKnown
- SurfaceOpacityKnown
- SurfaceTransformsToTargetKnown
- SurfaceTransformsToTargetKnown
- SurfaceTransformsToScreenKnown
- SurfaceTransformsToScreenKnown
- LayerIsInUnsorted3dRenderingContext
- LayerIsInUnsorted3dRenderingContext
- LayerIsHidden
- EnterRenderTarget
- FinishedRenderTarget
- ReduceOcclusionBelowSurface
- LeaveToRenderTarget
- MarkOccludedBehindLayer
- Occluded
- UnoccludedContentRect
- UnoccludedContributingSurfaceContentRect
#include "cc/trees/occlusion_tracker.h"
#include <algorithm>
#include "cc/base/math_util.h"
#include "cc/layers/layer.h"
#include "cc/layers/layer_impl.h"
#include "cc/layers/render_surface.h"
#include "cc/layers/render_surface_impl.h"
#include "ui/gfx/quad_f.h"
#include "ui/gfx/rect_conversions.h"
namespace cc {
template <typename LayerType>
OcclusionTracker<LayerType>::OcclusionTracker(
const gfx::Rect& screen_space_clip_rect)
: screen_space_clip_rect_(screen_space_clip_rect),
occluding_screen_space_rects_(NULL),
non_occluding_screen_space_rects_(NULL) {}
template <typename LayerType>
OcclusionTracker<LayerType>::~OcclusionTracker() {}
template <typename LayerType>
void OcclusionTracker<LayerType>::EnterLayer(
const LayerIteratorPosition<LayerType>& layer_iterator) {
LayerType* render_target = layer_iterator.target_render_surface_layer;
if (layer_iterator.represents_itself)
EnterRenderTarget(render_target);
else if (layer_iterator.represents_target_render_surface)
FinishedRenderTarget(render_target);
}
template <typename LayerType>
void OcclusionTracker<LayerType>::LeaveLayer(
const LayerIteratorPosition<LayerType>& layer_iterator) {
LayerType* render_target = layer_iterator.target_render_surface_layer;
if (layer_iterator.represents_itself)
MarkOccludedBehindLayer(layer_iterator.current_layer);
else if (layer_iterator.represents_contributing_render_surface)
LeaveToRenderTarget(render_target);
}
template <typename RenderSurfaceType>
static gfx::Rect ScreenSpaceClipRectInTargetSurface(
const RenderSurfaceType* target_surface,
const gfx::Rect& screen_space_clip_rect) {
gfx::Transform inverse_screen_space_transform(
gfx::Transform::kSkipInitialization);
if (!target_surface->screen_space_transform().GetInverse(
&inverse_screen_space_transform))
return target_surface->content_rect();
return MathUtil::ProjectEnclosingClippedRect(inverse_screen_space_transform,
screen_space_clip_rect);
}
template <typename RenderSurfaceType>
static Region TransformSurfaceOpaqueRegion(
const Region& region,
bool have_clip_rect,
const gfx::Rect& clip_rect_in_new_target,
const gfx::Transform& transform) {
if (region.IsEmpty())
return Region();
if (!transform.Preserves2dAxisAlignment())
return Region();
Region transformed_region;
for (Region::Iterator rects(region); rects.has_rect(); rects.next()) {
bool clipped;
gfx::QuadF transformed_quad =
MathUtil::MapQuad(transform, gfx::QuadF(rects.rect()), &clipped);
gfx::Rect transformed_rect =
gfx::ToEnclosedRect(transformed_quad.BoundingBox());
DCHECK(!clipped);
if (have_clip_rect)
transformed_rect.Intersect(clip_rect_in_new_target);
transformed_region.Union(transformed_rect);
}
return transformed_region;
}
static inline bool LayerOpacityKnown(const Layer* layer) {
return !layer->draw_opacity_is_animating();
}
static inline bool LayerOpacityKnown(const LayerImpl* layer) {
return true;
}
static inline bool LayerTransformsToTargetKnown(const Layer* layer) {
return !layer->draw_transform_is_animating();
}
static inline bool LayerTransformsToTargetKnown(const LayerImpl* layer) {
return true;
}
static inline bool SurfaceOpacityKnown(const RenderSurface* rs) {
return !rs->draw_opacity_is_animating();
}
static inline bool SurfaceOpacityKnown(const RenderSurfaceImpl* rs) {
return true;
}
static inline bool SurfaceTransformsToTargetKnown(const RenderSurface* rs) {
return !rs->target_surface_transforms_are_animating();
}
static inline bool SurfaceTransformsToTargetKnown(const RenderSurfaceImpl* rs) {
return true;
}
static inline bool SurfaceTransformsToScreenKnown(const RenderSurface* rs) {
return !rs->screen_space_transforms_are_animating();
}
static inline bool SurfaceTransformsToScreenKnown(const RenderSurfaceImpl* rs) {
return true;
}
static inline bool LayerIsInUnsorted3dRenderingContext(const Layer* layer) {
return layer->is_3d_sorted();
}
static inline bool LayerIsInUnsorted3dRenderingContext(const LayerImpl* layer) {
return false;
}
template <typename LayerType>
static inline bool LayerIsHidden(const LayerType* layer) {
return layer->hide_layer_and_subtree() ||
(layer->parent() && LayerIsHidden(layer->parent()));
}
template <typename LayerType>
void OcclusionTracker<LayerType>::EnterRenderTarget(
const LayerType* new_target) {
if (!stack_.empty() && stack_.back().target == new_target)
return;
const LayerType* old_target = NULL;
const typename LayerType::RenderSurfaceType* old_occlusion_immune_ancestor =
NULL;
if (!stack_.empty()) {
old_target = stack_.back().target;
old_occlusion_immune_ancestor =
old_target->render_surface()->nearest_occlusion_immune_ancestor();
}
const typename LayerType::RenderSurfaceType* new_occlusion_immune_ancestor =
new_target->render_surface()->nearest_occlusion_immune_ancestor();
stack_.push_back(StackObject(new_target));
bool entering_unoccluded_subtree =
new_occlusion_immune_ancestor &&
new_occlusion_immune_ancestor != old_occlusion_immune_ancestor;
bool have_transform_from_screen_to_new_target = false;
gfx::Transform inverse_new_target_screen_space_transform(
gfx::Transform::kSkipInitialization);
if (SurfaceTransformsToScreenKnown(new_target->render_surface())) {
have_transform_from_screen_to_new_target =
new_target->render_surface()->screen_space_transform().GetInverse(
&inverse_new_target_screen_space_transform);
}
bool entering_root_target = new_target->parent() == NULL;
bool copy_outside_occlusion_forward =
stack_.size() > 1 &&
!entering_unoccluded_subtree &&
have_transform_from_screen_to_new_target &&
!entering_root_target;
if (!copy_outside_occlusion_forward)
return;
int last_index = stack_.size() - 1;
gfx::Transform old_target_to_new_target_transform(
inverse_new_target_screen_space_transform,
old_target->render_surface()->screen_space_transform());
stack_[last_index].occlusion_from_outside_target =
TransformSurfaceOpaqueRegion<typename LayerType::RenderSurfaceType>(
stack_[last_index - 1].occlusion_from_outside_target,
false,
gfx::Rect(),
old_target_to_new_target_transform);
stack_[last_index].occlusion_from_outside_target.Union(
TransformSurfaceOpaqueRegion<typename LayerType::RenderSurfaceType>(
stack_[last_index - 1].occlusion_from_inside_target,
false,
gfx::Rect(),
old_target_to_new_target_transform));
}
template <typename LayerType>
void OcclusionTracker<LayerType>::FinishedRenderTarget(
const LayerType* finished_target) {
EnterRenderTarget(finished_target);
typename LayerType::RenderSurfaceType* surface =
finished_target->render_surface();
bool target_is_only_for_copy_request =
finished_target->HasCopyRequest() && LayerIsHidden(finished_target);
if (finished_target->mask_layer() || !SurfaceOpacityKnown(surface) ||
surface->draw_opacity() < 1 ||
!finished_target->uses_default_blend_mode() ||
target_is_only_for_copy_request ||
finished_target->filters().HasFilterThatAffectsOpacity()) {
stack_.back().occlusion_from_outside_target.Clear();
stack_.back().occlusion_from_inside_target.Clear();
} else if (!SurfaceTransformsToTargetKnown(surface)) {
stack_.back().occlusion_from_inside_target.Clear();
stack_.back().occlusion_from_outside_target.Clear();
}
}
template <typename LayerType>
static void ReduceOcclusionBelowSurface(LayerType* contributing_layer,
const gfx::Rect& surface_rect,
const gfx::Transform& surface_transform,
LayerType* render_target,
Region* occlusion_from_inside_target) {
if (surface_rect.IsEmpty())
return;
gfx::Rect affected_area_in_target =
MathUtil::MapEnclosingClippedRect(surface_transform, surface_rect);
if (contributing_layer->render_surface()->is_clipped()) {
affected_area_in_target.Intersect(
contributing_layer->render_surface()->clip_rect());
}
if (affected_area_in_target.IsEmpty())
return;
int outset_top, outset_right, outset_bottom, outset_left;
contributing_layer->background_filters().GetOutsets(
&outset_top, &outset_right, &outset_bottom, &outset_left);
affected_area_in_target.Inset(
-outset_left, -outset_top, -outset_right, -outset_bottom);
Region affected_occlusion = IntersectRegions(*occlusion_from_inside_target,
affected_area_in_target);
Region::Iterator affected_occlusion_rects(affected_occlusion);
occlusion_from_inside_target->Subtract(affected_area_in_target);
for (; affected_occlusion_rects.has_rect(); affected_occlusion_rects.next()) {
gfx::Rect occlusion_rect = affected_occlusion_rects.rect();
int shrink_left =
occlusion_rect.x() == affected_area_in_target.x() ? 0 : outset_right;
int shrink_top =
occlusion_rect.y() == affected_area_in_target.y() ? 0 : outset_bottom;
int shrink_right =
occlusion_rect.right() == affected_area_in_target.right() ?
0 : outset_left;
int shrink_bottom =
occlusion_rect.bottom() == affected_area_in_target.bottom() ?
0 : outset_top;
occlusion_rect.Inset(shrink_left, shrink_top, shrink_right, shrink_bottom);
occlusion_from_inside_target->Union(occlusion_rect);
}
}
template <typename LayerType>
void OcclusionTracker<LayerType>::LeaveToRenderTarget(
const LayerType* new_target) {
int last_index = stack_.size() - 1;
bool surface_will_be_at_top_after_pop =
stack_.size() > 1 && stack_[last_index - 1].target == new_target;
const LayerType* old_target = stack_[last_index].target;
const typename LayerType::RenderSurfaceType* old_surface =
old_target->render_surface();
Region old_occlusion_from_inside_target_in_new_target =
TransformSurfaceOpaqueRegion<typename LayerType::RenderSurfaceType>(
stack_[last_index].occlusion_from_inside_target,
old_surface->is_clipped(),
old_surface->clip_rect(),
old_surface->draw_transform());
if (old_target->has_replica() && !old_target->replica_has_mask()) {
old_occlusion_from_inside_target_in_new_target.Union(
TransformSurfaceOpaqueRegion<typename LayerType::RenderSurfaceType>(
stack_[last_index].occlusion_from_inside_target,
old_surface->is_clipped(),
old_surface->clip_rect(),
old_surface->replica_draw_transform()));
}
Region old_occlusion_from_outside_target_in_new_target =
TransformSurfaceOpaqueRegion<typename LayerType::RenderSurfaceType>(
stack_[last_index].occlusion_from_outside_target,
false,
gfx::Rect(),
old_surface->draw_transform());
gfx::Rect unoccluded_surface_rect;
gfx::Rect unoccluded_replica_rect;
if (old_target->background_filters().HasFilterThatMovesPixels()) {
unoccluded_surface_rect = UnoccludedContributingSurfaceContentRect(
old_target, old_surface->content_rect(), old_surface->draw_transform());
if (old_target->has_replica()) {
unoccluded_replica_rect = UnoccludedContributingSurfaceContentRect(
old_target,
old_surface->content_rect(),
old_surface->replica_draw_transform());
}
}
if (surface_will_be_at_top_after_pop) {
stack_[last_index - 1].occlusion_from_inside_target.Union(
old_occlusion_from_inside_target_in_new_target);
if (new_target->parent()) {
stack_[last_index - 1].occlusion_from_outside_target.Union(
old_occlusion_from_outside_target_in_new_target);
}
stack_.pop_back();
} else {
stack_.back().target = new_target;
stack_.back().occlusion_from_inside_target =
old_occlusion_from_inside_target_in_new_target;
if (new_target->parent()) {
stack_.back().occlusion_from_outside_target =
old_occlusion_from_outside_target_in_new_target;
} else {
stack_.back().occlusion_from_outside_target.Clear();
}
}
if (!old_target->background_filters().HasFilterThatMovesPixels())
return;
ReduceOcclusionBelowSurface(old_target,
unoccluded_surface_rect,
old_surface->draw_transform(),
new_target,
&stack_.back().occlusion_from_inside_target);
ReduceOcclusionBelowSurface(old_target,
unoccluded_surface_rect,
old_surface->draw_transform(),
new_target,
&stack_.back().occlusion_from_outside_target);
if (!old_target->has_replica())
return;
ReduceOcclusionBelowSurface(old_target,
unoccluded_replica_rect,
old_surface->replica_draw_transform(),
new_target,
&stack_.back().occlusion_from_inside_target);
ReduceOcclusionBelowSurface(old_target,
unoccluded_replica_rect,
old_surface->replica_draw_transform(),
new_target,
&stack_.back().occlusion_from_outside_target);
}
template <typename LayerType>
void OcclusionTracker<LayerType>::MarkOccludedBehindLayer(
const LayerType* layer) {
DCHECK(!stack_.empty());
DCHECK_EQ(layer->render_target(), stack_.back().target);
if (stack_.empty())
return;
if (!layer->DrawsContent())
return;
if (!LayerOpacityKnown(layer) || layer->draw_opacity() < 1)
return;
if (!layer->uses_default_blend_mode())
return;
if (LayerIsInUnsorted3dRenderingContext(layer))
return;
if (!LayerTransformsToTargetKnown(layer))
return;
Region opaque_contents = layer->VisibleContentOpaqueRegion();
if (opaque_contents.IsEmpty())
return;
DCHECK(layer->visible_content_rect().Contains(opaque_contents.bounds()));
if (!layer->draw_transform().Preserves2dAxisAlignment())
return;
gfx::Rect clip_rect_in_target = ScreenSpaceClipRectInTargetSurface(
layer->render_target()->render_surface(), screen_space_clip_rect_);
if (layer->is_clipped()) {
clip_rect_in_target.Intersect(layer->clip_rect());
} else {
clip_rect_in_target.Intersect(
layer->render_target()->render_surface()->content_rect());
}
for (Region::Iterator opaque_content_rects(opaque_contents);
opaque_content_rects.has_rect();
opaque_content_rects.next()) {
bool clipped;
gfx::QuadF transformed_quad = MathUtil::MapQuad(
layer->draw_transform(),
gfx::QuadF(opaque_content_rects.rect()),
&clipped);
gfx::Rect transformed_rect =
gfx::ToEnclosedRect(transformed_quad.BoundingBox());
DCHECK(!clipped);
transformed_rect.Intersect(clip_rect_in_target);
if (transformed_rect.width() < minimum_tracking_size_.width() &&
transformed_rect.height() < minimum_tracking_size_.height())
continue;
stack_.back().occlusion_from_inside_target.Union(transformed_rect);
if (!occluding_screen_space_rects_)
continue;
gfx::QuadF screen_space_quad = MathUtil::MapQuad(
layer->render_target()->render_surface()->screen_space_transform(),
gfx::QuadF(transformed_rect), &clipped);
gfx::Rect screen_space_rect =
gfx::ToEnclosedRect(screen_space_quad.BoundingBox());
occluding_screen_space_rects_->push_back(screen_space_rect);
}
if (!non_occluding_screen_space_rects_)
return;
Region non_opaque_contents =
SubtractRegions(gfx::Rect(layer->content_bounds()), opaque_contents);
for (Region::Iterator non_opaque_content_rects(non_opaque_contents);
non_opaque_content_rects.has_rect();
non_opaque_content_rects.next()) {
gfx::Rect transformed_rect = gfx::ToEnclosedRect(
MathUtil::MapClippedRect(layer->draw_transform(),
gfx::RectF(non_opaque_content_rects.rect())));
transformed_rect.Intersect(clip_rect_in_target);
if (transformed_rect.IsEmpty())
continue;
bool clipped;
gfx::QuadF screen_space_quad = MathUtil::MapQuad(
layer->render_target()->render_surface()->screen_space_transform(),
gfx::QuadF(transformed_rect),
&clipped);
gfx::Rect screen_space_rect =
gfx::ToEnclosedRect(screen_space_quad.BoundingBox());
non_occluding_screen_space_rects_->push_back(screen_space_rect);
}
}
template <typename LayerType>
bool OcclusionTracker<LayerType>::Occluded(
const LayerType* render_target,
const gfx::Rect& content_rect,
const gfx::Transform& draw_transform) const {
DCHECK(!stack_.empty());
if (stack_.empty())
return false;
if (content_rect.IsEmpty())
return true;
if (!render_target)
return false;
DCHECK_EQ(render_target->render_target(), render_target);
DCHECK(render_target->render_surface());
DCHECK_EQ(render_target, stack_.back().target);
if (stack_.back().occlusion_from_inside_target.IsEmpty() &&
stack_.back().occlusion_from_outside_target.IsEmpty()) {
return false;
}
gfx::Transform inverse_draw_transform(gfx::Transform::kSkipInitialization);
if (!draw_transform.GetInverse(&inverse_draw_transform))
return false;
Region unoccluded_region_in_target_surface =
MathUtil::MapEnclosingClippedRect(draw_transform, content_rect);
unoccluded_region_in_target_surface.Subtract(
stack_.back().occlusion_from_inside_target);
gfx::RectF unoccluded_rect_in_target_surface_without_outside_occlusion =
unoccluded_region_in_target_surface.bounds();
unoccluded_region_in_target_surface.Subtract(
stack_.back().occlusion_from_outside_target);
gfx::RectF unoccluded_rect_in_target_surface =
unoccluded_region_in_target_surface.bounds();
return unoccluded_rect_in_target_surface.IsEmpty();
}
template <typename LayerType>
gfx::Rect OcclusionTracker<LayerType>::UnoccludedContentRect(
const LayerType* render_target,
const gfx::Rect& content_rect,
const gfx::Transform& draw_transform) const {
DCHECK(!stack_.empty());
if (stack_.empty())
return content_rect;
if (content_rect.IsEmpty())
return content_rect;
if (!render_target)
return content_rect;
DCHECK_EQ(render_target->render_target(), render_target);
DCHECK(render_target->render_surface());
DCHECK_EQ(render_target, stack_.back().target);
if (stack_.back().occlusion_from_inside_target.IsEmpty() &&
stack_.back().occlusion_from_outside_target.IsEmpty()) {
return content_rect;
}
gfx::Transform inverse_draw_transform(gfx::Transform::kSkipInitialization);
if (!draw_transform.GetInverse(&inverse_draw_transform))
return content_rect;
Region unoccluded_region_in_target_surface =
MathUtil::MapEnclosingClippedRect(draw_transform, content_rect);
unoccluded_region_in_target_surface.Subtract(
stack_.back().occlusion_from_inside_target);
unoccluded_region_in_target_surface.Subtract(
stack_.back().occlusion_from_outside_target);
gfx::Rect unoccluded_rect_in_target_surface =
unoccluded_region_in_target_surface.bounds();
gfx::Rect unoccluded_rect = MathUtil::ProjectEnclosingClippedRect(
inverse_draw_transform, unoccluded_rect_in_target_surface);
unoccluded_rect.Intersect(content_rect);
return unoccluded_rect;
}
template <typename LayerType>
gfx::Rect OcclusionTracker<LayerType>::UnoccludedContributingSurfaceContentRect(
const LayerType* layer,
const gfx::Rect& content_rect,
const gfx::Transform& draw_transform) const {
DCHECK(!stack_.empty());
DCHECK(layer->render_surface());
DCHECK_EQ(layer->render_target(), layer);
DCHECK(layer->parent());
DCHECK_EQ(layer, stack_.back().target);
if (content_rect.IsEmpty())
return content_rect;
bool has_occlusion = stack_.size() > 1;
if (!has_occlusion)
return content_rect;
const StackObject& second_last = stack_[stack_.size() - 2];
if (second_last.occlusion_from_inside_target.IsEmpty() &&
second_last.occlusion_from_outside_target.IsEmpty())
return content_rect;
gfx::Transform inverse_draw_transform(gfx::Transform::kSkipInitialization);
if (!draw_transform.GetInverse(&inverse_draw_transform))
return content_rect;
Region unoccluded_region_in_target_surface =
MathUtil::MapEnclosingClippedRect(draw_transform, content_rect);
unoccluded_region_in_target_surface.Subtract(
second_last.occlusion_from_inside_target);
unoccluded_region_in_target_surface.Subtract(
second_last.occlusion_from_outside_target);
gfx::Rect unoccluded_rect_in_target_surface =
unoccluded_region_in_target_surface.bounds();
gfx::Rect unoccluded_rect = MathUtil::ProjectEnclosingClippedRect(
inverse_draw_transform, unoccluded_rect_in_target_surface);
unoccluded_rect.Intersect(content_rect);
return unoccluded_rect;
}
template class OcclusionTracker<Layer>;
template class OcclusionTracker<LayerImpl>;
}