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