Home | History | Annotate | Download | only in trees
      1 // Copyright 2012 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 "cc/trees/occlusion_tracker.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "cc/base/math_util.h"
     10 #include "cc/debug/overdraw_metrics.h"
     11 #include "cc/layers/layer.h"
     12 #include "cc/layers/layer_impl.h"
     13 #include "cc/layers/render_surface.h"
     14 #include "cc/layers/render_surface_impl.h"
     15 #include "ui/gfx/quad_f.h"
     16 #include "ui/gfx/rect_conversions.h"
     17 
     18 namespace cc {
     19 
     20 template <typename LayerType, typename RenderSurfaceType>
     21 OcclusionTrackerBase<LayerType, RenderSurfaceType>::OcclusionTrackerBase(
     22     gfx::Rect screen_space_clip_rect, bool record_metrics_for_frame)
     23     : screen_space_clip_rect_(screen_space_clip_rect),
     24       overdraw_metrics_(OverdrawMetrics::Create(record_metrics_for_frame)),
     25       prevent_occlusion_(false),
     26       occluding_screen_space_rects_(NULL),
     27       non_occluding_screen_space_rects_(NULL) {}
     28 
     29 template <typename LayerType, typename RenderSurfaceType>
     30 OcclusionTrackerBase<LayerType, RenderSurfaceType>::~OcclusionTrackerBase() {}
     31 
     32 template <typename LayerType, typename RenderSurfaceType>
     33 void OcclusionTrackerBase<LayerType, RenderSurfaceType>::EnterLayer(
     34     const LayerIteratorPosition<LayerType>& layer_iterator,
     35     bool prevent_occlusion) {
     36   LayerType* render_target = layer_iterator.target_render_surface_layer;
     37 
     38   if (layer_iterator.represents_itself)
     39     EnterRenderTarget(render_target);
     40   else if (layer_iterator.represents_target_render_surface)
     41     FinishedRenderTarget(render_target);
     42 
     43   prevent_occlusion_ = prevent_occlusion;
     44 }
     45 
     46 template <typename LayerType, typename RenderSurfaceType>
     47 void OcclusionTrackerBase<LayerType, RenderSurfaceType>::LeaveLayer(
     48     const LayerIteratorPosition<LayerType>& layer_iterator) {
     49   LayerType* render_target = layer_iterator.target_render_surface_layer;
     50 
     51   if (layer_iterator.represents_itself)
     52     MarkOccludedBehindLayer(layer_iterator.current_layer);
     53   // TODO(danakj): This should be done when entering the contributing surface,
     54   // but in a way that the surface's own occlusion won't occlude itself.
     55   else if (layer_iterator.represents_contributing_render_surface)
     56     LeaveToRenderTarget(render_target);
     57 
     58   prevent_occlusion_ = false;
     59 }
     60 
     61 template <typename RenderSurfaceType>
     62 static gfx::Rect ScreenSpaceClipRectInTargetSurface(
     63     const RenderSurfaceType* target_surface, gfx::Rect screen_space_clip_rect) {
     64   gfx::Transform inverse_screen_space_transform(
     65       gfx::Transform::kSkipInitialization);
     66   if (!target_surface->screen_space_transform().GetInverse(
     67           &inverse_screen_space_transform))
     68     return target_surface->content_rect();
     69 
     70   return gfx::ToEnclosingRect(MathUtil::ProjectClippedRect(
     71       inverse_screen_space_transform, screen_space_clip_rect));
     72 }
     73 
     74 template <typename RenderSurfaceType>
     75 static Region TransformSurfaceOpaqueRegion(const Region& region,
     76                                            bool have_clip_rect,
     77                                            gfx::Rect clip_rect_in_new_target,
     78                                            const gfx::Transform& transform) {
     79   if (region.IsEmpty())
     80     return Region();
     81 
     82   // Verify that rects within the |surface| will remain rects in its target
     83   // surface after applying |transform|. If this is true, then apply |transform|
     84   // to each rect within |region| in order to transform the entire Region.
     85 
     86   // TODO(danakj): Find a rect interior to each transformed quad.
     87   if (!transform.Preserves2dAxisAlignment())
     88     return Region();
     89 
     90   // TODO(danakj): If the Region is too complex, degrade gracefully here by
     91   // skipping rects in it.
     92   Region transformed_region;
     93   for (Region::Iterator rects(region); rects.has_rect(); rects.next()) {
     94     bool clipped;
     95     gfx::QuadF transformed_quad =
     96         MathUtil::MapQuad(transform, gfx::QuadF(rects.rect()), &clipped);
     97     gfx::Rect transformed_rect =
     98         gfx::ToEnclosedRect(transformed_quad.BoundingBox());
     99     DCHECK(!clipped);  // We only map if the transform preserves axis alignment.
    100     if (have_clip_rect)
    101       transformed_rect.Intersect(clip_rect_in_new_target);
    102     transformed_region.Union(transformed_rect);
    103   }
    104   return transformed_region;
    105 }
    106 
    107 static inline bool LayerOpacityKnown(const Layer* layer) {
    108   return !layer->draw_opacity_is_animating();
    109 }
    110 static inline bool LayerOpacityKnown(const LayerImpl* layer) {
    111   return true;
    112 }
    113 static inline bool LayerTransformsToTargetKnown(const Layer* layer) {
    114   return !layer->draw_transform_is_animating();
    115 }
    116 static inline bool LayerTransformsToTargetKnown(const LayerImpl* layer) {
    117   return true;
    118 }
    119 
    120 static inline bool SurfaceOpacityKnown(const RenderSurface* rs) {
    121   return !rs->draw_opacity_is_animating();
    122 }
    123 static inline bool SurfaceOpacityKnown(const RenderSurfaceImpl* rs) {
    124   return true;
    125 }
    126 static inline bool SurfaceTransformsToTargetKnown(const RenderSurface* rs) {
    127   return !rs->target_surface_transforms_are_animating();
    128 }
    129 static inline bool SurfaceTransformsToTargetKnown(const RenderSurfaceImpl* rs) {
    130   return true;
    131 }
    132 static inline bool SurfaceTransformsToScreenKnown(const RenderSurface* rs) {
    133   return !rs->screen_space_transforms_are_animating();
    134 }
    135 static inline bool SurfaceTransformsToScreenKnown(const RenderSurfaceImpl* rs) {
    136   return true;
    137 }
    138 
    139 static inline bool LayerIsInUnsorted3dRenderingContext(const Layer* layer) {
    140   return layer->parent() && layer->parent()->preserves_3d();
    141 }
    142 static inline bool LayerIsInUnsorted3dRenderingContext(const LayerImpl* layer) {
    143   return false;
    144 }
    145 
    146 template <typename LayerType>
    147 static inline bool LayerIsHidden(const LayerType* layer) {
    148   return layer->hide_layer_and_subtree() ||
    149          (layer->parent() && LayerIsHidden(layer->parent()));
    150 }
    151 
    152 template <typename LayerType, typename RenderSurfaceType>
    153 void OcclusionTrackerBase<LayerType, RenderSurfaceType>::EnterRenderTarget(
    154     const LayerType* new_target) {
    155   if (!stack_.empty() && stack_.back().target == new_target)
    156     return;
    157 
    158   const LayerType* old_target = NULL;
    159   const RenderSurfaceType* old_ancestor_that_moves_pixels = NULL;
    160   if (!stack_.empty()) {
    161     old_target = stack_.back().target;
    162     old_ancestor_that_moves_pixels =
    163         old_target->render_surface()->nearest_ancestor_that_moves_pixels();
    164   }
    165   const RenderSurfaceType* new_ancestor_that_moves_pixels =
    166       new_target->render_surface()->nearest_ancestor_that_moves_pixels();
    167 
    168   stack_.push_back(StackObject(new_target));
    169 
    170   // We copy the screen occlusion into the new RenderSurface subtree, but we
    171   // never copy in the occlusion from inside the target, since we are looking
    172   // at a new RenderSurface target.
    173 
    174   // If we are entering a subtree that is going to move pixels around, then the
    175   // occlusion we've computed so far won't apply to the pixels we're drawing
    176   // here in the same way. We discard the occlusion thus far to be safe, and
    177   // ensure we don't cull any pixels that are moved such that they become
    178   //  visible.
    179   bool entering_subtree_that_moves_pixels =
    180       new_ancestor_that_moves_pixels &&
    181       new_ancestor_that_moves_pixels != old_ancestor_that_moves_pixels;
    182 
    183   bool have_transform_from_screen_to_new_target = false;
    184   gfx::Transform inverse_new_target_screen_space_transform(
    185       // Note carefully, not used if screen space transform is uninvertible.
    186       gfx::Transform::kSkipInitialization);
    187   if (SurfaceTransformsToScreenKnown(new_target->render_surface())) {
    188     have_transform_from_screen_to_new_target =
    189         new_target->render_surface()->screen_space_transform().GetInverse(
    190             &inverse_new_target_screen_space_transform);
    191   }
    192 
    193   bool entering_root_target = new_target->parent() == NULL;
    194 
    195   bool copy_outside_occlusion_forward =
    196       stack_.size() > 1 &&
    197       !entering_subtree_that_moves_pixels &&
    198       have_transform_from_screen_to_new_target &&
    199       !entering_root_target;
    200   if (!copy_outside_occlusion_forward)
    201     return;
    202 
    203   int last_index = stack_.size() - 1;
    204   gfx::Transform old_target_to_new_target_transform(
    205       inverse_new_target_screen_space_transform,
    206       old_target->render_surface()->screen_space_transform());
    207   stack_[last_index].occlusion_from_outside_target =
    208       TransformSurfaceOpaqueRegion<RenderSurfaceType>(
    209           stack_[last_index - 1].occlusion_from_outside_target,
    210           false,
    211           gfx::Rect(),
    212           old_target_to_new_target_transform);
    213   stack_[last_index].occlusion_from_outside_target.Union(
    214       TransformSurfaceOpaqueRegion<RenderSurfaceType>(
    215           stack_[last_index - 1].occlusion_from_inside_target,
    216           false,
    217           gfx::Rect(),
    218           old_target_to_new_target_transform));
    219 }
    220 
    221 template <typename LayerType, typename RenderSurfaceType>
    222 void OcclusionTrackerBase<LayerType, RenderSurfaceType>::FinishedRenderTarget(
    223     const LayerType* finished_target) {
    224   // Make sure we know about the target surface.
    225   EnterRenderTarget(finished_target);
    226 
    227   RenderSurfaceType* surface = finished_target->render_surface();
    228 
    229   // Readbacks always happen on render targets so we only need to check
    230   // for readbacks here.
    231   bool target_is_only_for_copy_request =
    232       finished_target->HasCopyRequest() && LayerIsHidden(finished_target);
    233 
    234   // If the occlusion within the surface can not be applied to things outside of
    235   // the surface's subtree, then clear the occlusion here so it won't be used.
    236   // TODO(senorblanco):  Make this smarter for SkImageFilter case:  once
    237   // SkImageFilters can report affectsOpacity(), call that.
    238   if (finished_target->mask_layer() ||
    239       !SurfaceOpacityKnown(surface) ||
    240       surface->draw_opacity() < 1 ||
    241       target_is_only_for_copy_request ||
    242       finished_target->filters().HasFilterThatAffectsOpacity() ||
    243       finished_target->filter()) {
    244     stack_.back().occlusion_from_outside_target.Clear();
    245     stack_.back().occlusion_from_inside_target.Clear();
    246   } else if (!SurfaceTransformsToTargetKnown(surface)) {
    247     stack_.back().occlusion_from_inside_target.Clear();
    248     stack_.back().occlusion_from_outside_target.Clear();
    249   }
    250 }
    251 
    252 template <typename LayerType>
    253 static void ReduceOcclusionBelowSurface(LayerType* contributing_layer,
    254                                         gfx::Rect surface_rect,
    255                                         const gfx::Transform& surface_transform,
    256                                         LayerType* render_target,
    257                                         Region* occlusion_from_inside_target) {
    258   if (surface_rect.IsEmpty())
    259     return;
    260 
    261   gfx::Rect affected_area_in_target = gfx::ToEnclosingRect(
    262       MathUtil::MapClippedRect(surface_transform, gfx::RectF(surface_rect)));
    263   if (contributing_layer->render_surface()->is_clipped()) {
    264     affected_area_in_target.Intersect(
    265         contributing_layer->render_surface()->clip_rect());
    266   }
    267   if (affected_area_in_target.IsEmpty())
    268     return;
    269 
    270   int outset_top, outset_right, outset_bottom, outset_left;
    271   contributing_layer->background_filters().GetOutsets(
    272       &outset_top, &outset_right, &outset_bottom, &outset_left);
    273 
    274   // The filter can move pixels from outside of the clip, so allow affected_area
    275   // to expand outside the clip.
    276   affected_area_in_target.Inset(
    277       -outset_left, -outset_top, -outset_right, -outset_bottom);
    278 
    279   gfx::Rect FilterOutsetsInTarget(-outset_left,
    280                                   -outset_top,
    281                                   outset_left + outset_right,
    282                                   outset_top + outset_bottom);
    283 
    284   Region affected_occlusion = IntersectRegions(*occlusion_from_inside_target,
    285                                                affected_area_in_target);
    286   Region::Iterator affected_occlusion_rects(affected_occlusion);
    287 
    288   occlusion_from_inside_target->Subtract(affected_area_in_target);
    289   for (; affected_occlusion_rects.has_rect(); affected_occlusion_rects.next()) {
    290     gfx::Rect occlusion_rect = affected_occlusion_rects.rect();
    291 
    292     // Shrink the rect by expanding the non-opaque pixels outside the rect.
    293 
    294     // The left outset of the filters moves pixels on the right side of
    295     // the occlusion_rect into it, shrinking its right edge.
    296     int shrink_left =
    297         occlusion_rect.x() == affected_area_in_target.x() ? 0 : outset_right;
    298     int shrink_top =
    299         occlusion_rect.y() == affected_area_in_target.y() ? 0 : outset_bottom;
    300     int shrink_right =
    301         occlusion_rect.right() == affected_area_in_target.right() ?
    302         0 : outset_left;
    303     int shrink_bottom =
    304         occlusion_rect.bottom() == affected_area_in_target.bottom() ?
    305         0 : outset_top;
    306 
    307     occlusion_rect.Inset(shrink_left, shrink_top, shrink_right, shrink_bottom);
    308 
    309     occlusion_from_inside_target->Union(occlusion_rect);
    310   }
    311 }
    312 
    313 template <typename LayerType, typename RenderSurfaceType>
    314 void OcclusionTrackerBase<LayerType, RenderSurfaceType>::LeaveToRenderTarget(
    315     const LayerType* new_target) {
    316   int last_index = stack_.size() - 1;
    317   bool surface_will_be_at_top_after_pop =
    318       stack_.size() > 1 && stack_[last_index - 1].target == new_target;
    319 
    320   // We merge the screen occlusion from the current RenderSurfaceImpl subtree
    321   // out to its parent target RenderSurfaceImpl. The target occlusion can be
    322   // merged out as well but needs to be transformed to the new target.
    323 
    324   const LayerType* old_target = stack_[last_index].target;
    325   const RenderSurfaceType* old_surface = old_target->render_surface();
    326 
    327   Region old_occlusion_from_inside_target_in_new_target =
    328       TransformSurfaceOpaqueRegion<RenderSurfaceType>(
    329           stack_[last_index].occlusion_from_inside_target,
    330           old_surface->is_clipped(),
    331           old_surface->clip_rect(),
    332           old_surface->draw_transform());
    333   if (old_target->has_replica() && !old_target->replica_has_mask()) {
    334     old_occlusion_from_inside_target_in_new_target.Union(
    335         TransformSurfaceOpaqueRegion<RenderSurfaceType>(
    336             stack_[last_index].occlusion_from_inside_target,
    337             old_surface->is_clipped(),
    338             old_surface->clip_rect(),
    339             old_surface->replica_draw_transform()));
    340   }
    341 
    342   Region old_occlusion_from_outside_target_in_new_target =
    343       TransformSurfaceOpaqueRegion<RenderSurfaceType>(
    344           stack_[last_index].occlusion_from_outside_target,
    345           false,
    346           gfx::Rect(),
    347           old_surface->draw_transform());
    348 
    349   gfx::Rect unoccluded_surface_rect;
    350   gfx::Rect unoccluded_replica_rect;
    351   if (old_target->background_filters().HasFilterThatMovesPixels()) {
    352     unoccluded_surface_rect = UnoccludedContributingSurfaceContentRect(
    353         old_target, false, old_surface->content_rect(), NULL);
    354     if (old_target->has_replica()) {
    355       unoccluded_replica_rect = UnoccludedContributingSurfaceContentRect(
    356           old_target, true, old_surface->content_rect(), NULL);
    357     }
    358   }
    359 
    360   if (surface_will_be_at_top_after_pop) {
    361     // Merge the top of the stack down.
    362     stack_[last_index - 1].occlusion_from_inside_target.Union(
    363         old_occlusion_from_inside_target_in_new_target);
    364     // TODO(danakj): Strictly this should subtract the inside target occlusion
    365     // before union.
    366     if (new_target->parent()) {
    367       stack_[last_index - 1].occlusion_from_outside_target.Union(
    368           old_occlusion_from_outside_target_in_new_target);
    369     }
    370     stack_.pop_back();
    371   } else {
    372     // Replace the top of the stack with the new pushed surface.
    373     stack_.back().target = new_target;
    374     stack_.back().occlusion_from_inside_target =
    375         old_occlusion_from_inside_target_in_new_target;
    376     if (new_target->parent()) {
    377       stack_.back().occlusion_from_outside_target =
    378           old_occlusion_from_outside_target_in_new_target;
    379     } else {
    380       stack_.back().occlusion_from_outside_target.Clear();
    381     }
    382   }
    383 
    384   if (!old_target->background_filters().HasFilterThatMovesPixels())
    385     return;
    386 
    387   ReduceOcclusionBelowSurface(old_target,
    388                               unoccluded_surface_rect,
    389                               old_surface->draw_transform(),
    390                               new_target,
    391                               &stack_.back().occlusion_from_inside_target);
    392   ReduceOcclusionBelowSurface(old_target,
    393                               unoccluded_surface_rect,
    394                               old_surface->draw_transform(),
    395                               new_target,
    396                               &stack_.back().occlusion_from_outside_target);
    397 
    398   if (!old_target->has_replica())
    399     return;
    400   ReduceOcclusionBelowSurface(old_target,
    401                               unoccluded_replica_rect,
    402                               old_surface->replica_draw_transform(),
    403                               new_target,
    404                               &stack_.back().occlusion_from_inside_target);
    405   ReduceOcclusionBelowSurface(old_target,
    406                               unoccluded_replica_rect,
    407                               old_surface->replica_draw_transform(),
    408                               new_target,
    409                               &stack_.back().occlusion_from_outside_target);
    410 }
    411 
    412 template <typename LayerType, typename RenderSurfaceType>
    413 void OcclusionTrackerBase<LayerType, RenderSurfaceType>::
    414     MarkOccludedBehindLayer(const LayerType* layer) {
    415   DCHECK(!stack_.empty());
    416   DCHECK_EQ(layer->render_target(), stack_.back().target);
    417   if (stack_.empty())
    418     return;
    419 
    420   if (!LayerOpacityKnown(layer) || layer->draw_opacity() < 1)
    421     return;
    422 
    423   if (LayerIsInUnsorted3dRenderingContext(layer))
    424     return;
    425 
    426   if (!LayerTransformsToTargetKnown(layer))
    427     return;
    428 
    429   Region opaque_contents = layer->VisibleContentOpaqueRegion();
    430   if (opaque_contents.IsEmpty())
    431     return;
    432 
    433   DCHECK(layer->visible_content_rect().Contains(opaque_contents.bounds()));
    434 
    435   // TODO(danakj): Find a rect interior to each transformed quad.
    436   if (!layer->draw_transform().Preserves2dAxisAlignment())
    437     return;
    438 
    439   gfx::Rect clip_rect_in_target = ScreenSpaceClipRectInTargetSurface(
    440       layer->render_target()->render_surface(), screen_space_clip_rect_);
    441   if (layer->is_clipped()) {
    442     clip_rect_in_target.Intersect(layer->clip_rect());
    443   } else {
    444     clip_rect_in_target.Intersect(
    445         layer->render_target()->render_surface()->content_rect());
    446   }
    447 
    448   for (Region::Iterator opaque_content_rects(opaque_contents);
    449        opaque_content_rects.has_rect();
    450        opaque_content_rects.next()) {
    451     bool clipped;
    452     gfx::QuadF transformed_quad = MathUtil::MapQuad(
    453         layer->draw_transform(),
    454         gfx::QuadF(opaque_content_rects.rect()),
    455         &clipped);
    456     gfx::Rect transformed_rect =
    457         gfx::ToEnclosedRect(transformed_quad.BoundingBox());
    458     DCHECK(!clipped);  // We only map if the transform preserves axis alignment.
    459     transformed_rect.Intersect(clip_rect_in_target);
    460     if (transformed_rect.width() < minimum_tracking_size_.width() &&
    461         transformed_rect.height() < minimum_tracking_size_.height())
    462       continue;
    463     stack_.back().occlusion_from_inside_target.Union(transformed_rect);
    464 
    465     if (!occluding_screen_space_rects_)
    466       continue;
    467 
    468     // Save the occluding area in screen space for debug visualization.
    469     gfx::QuadF screen_space_quad = MathUtil::MapQuad(
    470         layer->render_target()->render_surface()->screen_space_transform(),
    471         gfx::QuadF(transformed_rect), &clipped);
    472     // TODO(danakj): Store the quad in the debug info instead of the bounding
    473     // box.
    474     gfx::Rect screen_space_rect =
    475         gfx::ToEnclosedRect(screen_space_quad.BoundingBox());
    476     occluding_screen_space_rects_->push_back(screen_space_rect);
    477   }
    478 
    479   if (!non_occluding_screen_space_rects_)
    480     return;
    481 
    482   Region non_opaque_contents =
    483       SubtractRegions(gfx::Rect(layer->content_bounds()), opaque_contents);
    484   for (Region::Iterator non_opaque_content_rects(non_opaque_contents);
    485        non_opaque_content_rects.has_rect();
    486        non_opaque_content_rects.next()) {
    487     // We've already checked for clipping in the MapQuad call above, these calls
    488     // should not clip anything further.
    489     gfx::Rect transformed_rect = gfx::ToEnclosedRect(
    490         MathUtil::MapClippedRect(layer->draw_transform(),
    491                                  gfx::RectF(non_opaque_content_rects.rect())));
    492     transformed_rect.Intersect(clip_rect_in_target);
    493     if (transformed_rect.IsEmpty())
    494       continue;
    495 
    496     bool clipped;
    497     gfx::QuadF screen_space_quad = MathUtil::MapQuad(
    498         layer->render_target()->render_surface()->screen_space_transform(),
    499         gfx::QuadF(transformed_rect),
    500         &clipped);
    501     // TODO(danakj): Store the quad in the debug info instead of the bounding
    502     // box.
    503     gfx::Rect screen_space_rect =
    504         gfx::ToEnclosedRect(screen_space_quad.BoundingBox());
    505     non_occluding_screen_space_rects_->push_back(screen_space_rect);
    506   }
    507 }
    508 
    509 template <typename LayerType, typename RenderSurfaceType>
    510 bool OcclusionTrackerBase<LayerType, RenderSurfaceType>::Occluded(
    511     const LayerType* render_target,
    512     gfx::Rect content_rect,
    513     const gfx::Transform& draw_transform,
    514     bool impl_draw_transform_is_unknown,
    515     bool is_clipped,
    516     gfx::Rect clip_rect_in_target,
    517     bool* has_occlusion_from_outside_target_surface) const {
    518   if (has_occlusion_from_outside_target_surface)
    519     *has_occlusion_from_outside_target_surface = false;
    520   if (prevent_occlusion_)
    521     return false;
    522 
    523   DCHECK(!stack_.empty());
    524   if (stack_.empty())
    525     return false;
    526   if (content_rect.IsEmpty())
    527     return true;
    528   if (impl_draw_transform_is_unknown)
    529     return false;
    530 
    531   // For tests with no render target.
    532   if (!render_target)
    533     return false;
    534 
    535   DCHECK_EQ(render_target->render_target(), render_target);
    536   DCHECK(render_target->render_surface());
    537   DCHECK_EQ(render_target, stack_.back().target);
    538 
    539   gfx::Transform inverse_draw_transform(gfx::Transform::kSkipInitialization);
    540   if (!draw_transform.GetInverse(&inverse_draw_transform))
    541     return false;
    542 
    543   // Take the ToEnclosingRect at each step, as we want to contain any unoccluded
    544   // partial pixels in the resulting Rect.
    545   Region unoccluded_region_in_target_surface = gfx::ToEnclosingRect(
    546       MathUtil::MapClippedRect(draw_transform, gfx::RectF(content_rect)));
    547   // Layers can't clip across surfaces, so count this as internal occlusion.
    548   if (is_clipped)
    549     unoccluded_region_in_target_surface.Intersect(clip_rect_in_target);
    550   unoccluded_region_in_target_surface.Subtract(
    551       stack_.back().occlusion_from_inside_target);
    552   gfx::RectF unoccluded_rect_in_target_surface_without_outside_occlusion =
    553       unoccluded_region_in_target_surface.bounds();
    554   unoccluded_region_in_target_surface.Subtract(
    555       stack_.back().occlusion_from_outside_target);
    556 
    557   // Treat other clipping as occlusion from outside the surface.
    558   // TODO(danakj): Clip to visibleContentRect?
    559   unoccluded_region_in_target_surface.Intersect(
    560       render_target->render_surface()->content_rect());
    561   unoccluded_region_in_target_surface.Intersect(
    562       ScreenSpaceClipRectInTargetSurface(render_target->render_surface(),
    563                                          screen_space_clip_rect_));
    564 
    565   gfx::RectF unoccluded_rect_in_target_surface =
    566       unoccluded_region_in_target_surface.bounds();
    567 
    568   if (has_occlusion_from_outside_target_surface) {
    569     // Check if the unoccluded rect shrank when applying outside occlusion.
    570     *has_occlusion_from_outside_target_surface = !gfx::SubtractRects(
    571         unoccluded_rect_in_target_surface_without_outside_occlusion,
    572         unoccluded_rect_in_target_surface).IsEmpty();
    573   }
    574 
    575   return unoccluded_rect_in_target_surface.IsEmpty();
    576 }
    577 
    578 template <typename LayerType, typename RenderSurfaceType>
    579 gfx::Rect OcclusionTrackerBase<LayerType, RenderSurfaceType>::
    580     UnoccludedContentRect(
    581         const LayerType* render_target,
    582         gfx::Rect content_rect,
    583         const gfx::Transform& draw_transform,
    584         bool impl_draw_transform_is_unknown,
    585         bool is_clipped,
    586         gfx::Rect clip_rect_in_target,
    587         bool* has_occlusion_from_outside_target_surface) const {
    588   if (has_occlusion_from_outside_target_surface)
    589     *has_occlusion_from_outside_target_surface = false;
    590   if (prevent_occlusion_)
    591     return content_rect;
    592 
    593   DCHECK(!stack_.empty());
    594   if (stack_.empty())
    595     return content_rect;
    596   if (content_rect.IsEmpty())
    597     return content_rect;
    598   if (impl_draw_transform_is_unknown)
    599     return content_rect;
    600 
    601   // For tests with no render target.
    602   if (!render_target)
    603     return content_rect;
    604 
    605   DCHECK_EQ(render_target->render_target(), render_target);
    606   DCHECK(render_target->render_surface());
    607   DCHECK_EQ(render_target, stack_.back().target);
    608 
    609   gfx::Transform inverse_draw_transform(gfx::Transform::kSkipInitialization);
    610   if (!draw_transform.GetInverse(&inverse_draw_transform))
    611     return content_rect;
    612 
    613   // Take the ToEnclosingRect at each step, as we want to contain any unoccluded
    614   // partial pixels in the resulting Rect.
    615   Region unoccluded_region_in_target_surface = gfx::ToEnclosingRect(
    616       MathUtil::MapClippedRect(draw_transform, gfx::RectF(content_rect)));
    617   // Layers can't clip across surfaces, so count this as internal occlusion.
    618   if (is_clipped)
    619     unoccluded_region_in_target_surface.Intersect(clip_rect_in_target);
    620   unoccluded_region_in_target_surface.Subtract(
    621       stack_.back().occlusion_from_inside_target);
    622   gfx::RectF unoccluded_rect_in_target_surface_without_outside_occlusion =
    623       unoccluded_region_in_target_surface.bounds();
    624   unoccluded_region_in_target_surface.Subtract(
    625       stack_.back().occlusion_from_outside_target);
    626 
    627   // Treat other clipping as occlusion from outside the surface.
    628   // TODO(danakj): Clip to visibleContentRect?
    629   unoccluded_region_in_target_surface.Intersect(
    630       render_target->render_surface()->content_rect());
    631   unoccluded_region_in_target_surface.Intersect(
    632       ScreenSpaceClipRectInTargetSurface(render_target->render_surface(),
    633                                          screen_space_clip_rect_));
    634 
    635   gfx::RectF unoccluded_rect_in_target_surface =
    636       unoccluded_region_in_target_surface.bounds();
    637   gfx::Rect unoccluded_rect = gfx::ToEnclosingRect(
    638       MathUtil::ProjectClippedRect(inverse_draw_transform,
    639                                    unoccluded_rect_in_target_surface));
    640   unoccluded_rect.Intersect(content_rect);
    641 
    642   if (has_occlusion_from_outside_target_surface) {
    643     // Check if the unoccluded rect shrank when applying outside occlusion.
    644     *has_occlusion_from_outside_target_surface = !gfx::SubtractRects(
    645         unoccluded_rect_in_target_surface_without_outside_occlusion,
    646         unoccluded_rect_in_target_surface).IsEmpty();
    647   }
    648 
    649   return unoccluded_rect;
    650 }
    651 
    652 template <typename LayerType, typename RenderSurfaceType>
    653 gfx::Rect OcclusionTrackerBase<LayerType, RenderSurfaceType>::
    654     UnoccludedContributingSurfaceContentRect(
    655         const LayerType* layer,
    656         bool for_replica,
    657         gfx::Rect content_rect,
    658         bool* has_occlusion_from_outside_target_surface) const {
    659   DCHECK(!stack_.empty());
    660   // The layer is a contributing render_target so it should have a surface.
    661   DCHECK(layer->render_surface());
    662   // The layer is a contributing render_target so its target should be itself.
    663   DCHECK_EQ(layer->render_target(), layer);
    664   // The layer should not be the root, else what is is contributing to?
    665   DCHECK(layer->parent());
    666   // This should be called while the layer is still considered the current
    667   // target in the occlusion tracker.
    668   DCHECK_EQ(layer, stack_.back().target);
    669 
    670   if (has_occlusion_from_outside_target_surface)
    671     *has_occlusion_from_outside_target_surface = false;
    672   if (prevent_occlusion_)
    673     return content_rect;
    674 
    675   if (content_rect.IsEmpty())
    676     return content_rect;
    677 
    678   const RenderSurfaceType* surface = layer->render_surface();
    679   const LayerType* contributing_surface_render_target =
    680       layer->parent()->render_target();
    681 
    682   if (!SurfaceTransformsToTargetKnown(surface))
    683     return content_rect;
    684 
    685   gfx::Transform draw_transform =
    686       for_replica ? surface->replica_draw_transform()
    687                   : surface->draw_transform();
    688   gfx::Transform inverse_draw_transform(gfx::Transform::kSkipInitialization);
    689   if (!draw_transform.GetInverse(&inverse_draw_transform))
    690     return content_rect;
    691 
    692   // A contributing surface doesn't get occluded by things inside its own
    693   // surface, so only things outside the surface can occlude it. That occlusion
    694   // is found just below the top of the stack (if it exists).
    695   bool has_occlusion = stack_.size() > 1;
    696 
    697   // Take the ToEnclosingRect at each step, as we want to contain any unoccluded
    698   // partial pixels in the resulting Rect.
    699   Region unoccluded_region_in_target_surface = gfx::ToEnclosingRect(
    700       MathUtil::MapClippedRect(draw_transform, gfx::RectF(content_rect)));
    701   // Layers can't clip across surfaces, so count this as internal occlusion.
    702   if (surface->is_clipped())
    703     unoccluded_region_in_target_surface.Intersect(surface->clip_rect());
    704   if (has_occlusion) {
    705     const StackObject& second_last = stack_[stack_.size() - 2];
    706     unoccluded_region_in_target_surface.Subtract(
    707         second_last.occlusion_from_inside_target);
    708   }
    709   gfx::RectF unoccluded_rect_in_target_surface_without_outside_occlusion =
    710       unoccluded_region_in_target_surface.bounds();
    711   if (has_occlusion) {
    712     const StackObject& second_last = stack_[stack_.size() - 2];
    713     unoccluded_region_in_target_surface.Subtract(
    714         second_last.occlusion_from_outside_target);
    715   }
    716 
    717   // Treat other clipping as occlusion from outside the target surface.
    718   unoccluded_region_in_target_surface.Intersect(
    719       contributing_surface_render_target->render_surface()->content_rect());
    720   unoccluded_region_in_target_surface.Intersect(
    721       ScreenSpaceClipRectInTargetSurface(
    722           contributing_surface_render_target->render_surface(),
    723           screen_space_clip_rect_));
    724 
    725   gfx::RectF unoccluded_rect_in_target_surface =
    726       unoccluded_region_in_target_surface.bounds();
    727   gfx::Rect unoccluded_rect = gfx::ToEnclosingRect(
    728       MathUtil::ProjectClippedRect(inverse_draw_transform,
    729                                    unoccluded_rect_in_target_surface));
    730   unoccluded_rect.Intersect(content_rect);
    731 
    732   if (has_occlusion_from_outside_target_surface) {
    733     // Check if the unoccluded rect shrank when applying outside occlusion.
    734     *has_occlusion_from_outside_target_surface = !gfx::SubtractRects(
    735         unoccluded_rect_in_target_surface_without_outside_occlusion,
    736         unoccluded_rect_in_target_surface).IsEmpty();
    737   }
    738 
    739   return unoccluded_rect;
    740 }
    741 
    742 // Instantiate (and export) templates here for the linker.
    743 template class OcclusionTrackerBase<Layer, RenderSurface>;
    744 template class OcclusionTrackerBase<LayerImpl, RenderSurfaceImpl>;
    745 
    746 }  // namespace cc
    747