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