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