occlusion_tracker.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
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
18namespace cc {
19
20template <typename LayerType, typename RenderSurfaceType>
21OcclusionTrackerBase<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
29template <typename LayerType, typename RenderSurfaceType>
30OcclusionTrackerBase<LayerType, RenderSurfaceType>::~OcclusionTrackerBase() {}
31
32template <typename LayerType, typename RenderSurfaceType>
33void 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
46template <typename LayerType, typename RenderSurfaceType>
47void 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
61template <typename RenderSurfaceType>
62static 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
74template <typename RenderSurfaceType>
75static 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  bool clipped;
87  gfx::QuadF transformed_bounds_quad =
88      MathUtil::MapQuad(transform, gfx::QuadF(region.bounds()), &clipped);
89  // FIXME: Find a rect interior to each transformed quad.
90  if (clipped || !transformed_bounds_quad.IsRectilinear())
91    return Region();
92
93  // TODO(danakj): If the Region is too complex, degrade gracefully here by
94  // skipping rects in it.
95  Region transformed_region;
96  for (Region::Iterator rects(region); rects.has_rect(); rects.next()) {
97    gfx::QuadF transformed_quad =
98        MathUtil::MapQuad(transform, gfx::QuadF(rects.rect()), &clipped);
99    gfx::Rect transformed_rect =
100        gfx::ToEnclosedRect(transformed_quad.BoundingBox());
101    DCHECK(!clipped);  // We only map if the transform preserves axis alignment.
102    if (have_clip_rect)
103      transformed_rect.Intersect(clip_rect_in_new_target);
104    transformed_region.Union(transformed_rect);
105  }
106  return transformed_region;
107}
108
109static inline bool LayerOpacityKnown(const Layer* layer) {
110  return !layer->draw_opacity_is_animating();
111}
112static inline bool LayerOpacityKnown(const LayerImpl* layer) {
113  return true;
114}
115static inline bool LayerTransformsToTargetKnown(const Layer* layer) {
116  return !layer->draw_transform_is_animating();
117}
118static inline bool LayerTransformsToTargetKnown(const LayerImpl* layer) {
119  return true;
120}
121
122static inline bool SurfaceOpacityKnown(const RenderSurface* rs) {
123  return !rs->draw_opacity_is_animating();
124}
125static inline bool SurfaceOpacityKnown(const RenderSurfaceImpl* rs) {
126  return true;
127}
128static inline bool SurfaceTransformsToTargetKnown(const RenderSurface* rs) {
129  return !rs->target_surface_transforms_are_animating();
130}
131static inline bool SurfaceTransformsToTargetKnown(const RenderSurfaceImpl* rs) {
132  return true;
133}
134static inline bool SurfaceTransformsToScreenKnown(const RenderSurface* rs) {
135  return !rs->screen_space_transforms_are_animating();
136}
137static inline bool SurfaceTransformsToScreenKnown(const RenderSurfaceImpl* rs) {
138  return true;
139}
140
141static inline bool LayerIsInUnsorted3dRenderingContext(const Layer* layer) {
142  return layer->parent() && layer->parent()->preserves_3d();
143}
144static inline bool LayerIsInUnsorted3dRenderingContext(const LayerImpl* layer) {
145  return false;
146}
147
148template <typename LayerType, typename RenderSurfaceType>
149void OcclusionTrackerBase<LayerType, RenderSurfaceType>::EnterRenderTarget(
150    const LayerType* new_target) {
151  if (!stack_.empty() && stack_.back().target == new_target)
152    return;
153
154  const LayerType* old_target = NULL;
155  const RenderSurfaceType* old_ancestor_that_moves_pixels = NULL;
156  if (!stack_.empty()) {
157    old_target = stack_.back().target;
158    old_ancestor_that_moves_pixels =
159        old_target->render_surface()->nearest_ancestor_that_moves_pixels();
160  }
161  const RenderSurfaceType* new_ancestor_that_moves_pixels =
162      new_target->render_surface()->nearest_ancestor_that_moves_pixels();
163
164  stack_.push_back(StackObject(new_target));
165
166  // We copy the screen occlusion into the new RenderSurface subtree, but we
167  // never copy in the occlusion from inside the target, since we are looking
168  // at a new RenderSurface target.
169
170  // If we are entering a subtree that is going to move pixels around, then the
171  // occlusion we've computed so far won't apply to the pixels we're drawing
172  // here in the same way. We discard the occlusion thus far to be safe, and
173  // ensure we don't cull any pixels that are moved such that they become
174  //  visible.
175  bool entering_subtree_that_moves_pixels =
176      new_ancestor_that_moves_pixels &&
177      new_ancestor_that_moves_pixels != old_ancestor_that_moves_pixels;
178
179  bool have_transform_from_screen_to_new_target = false;
180  gfx::Transform inverse_new_target_screen_space_transform(
181      // Note carefully, not used if screen space transform is uninvertible.
182      gfx::Transform::kSkipInitialization);
183  if (SurfaceTransformsToScreenKnown(new_target->render_surface())) {
184    have_transform_from_screen_to_new_target =
185        new_target->render_surface()->screen_space_transform().GetInverse(
186            &inverse_new_target_screen_space_transform);
187  }
188
189  bool entering_root_target = new_target->parent() == NULL;
190
191  bool copy_outside_occlusion_forward =
192      stack_.size() > 1 &&
193      !entering_subtree_that_moves_pixels &&
194      have_transform_from_screen_to_new_target &&
195      !entering_root_target;
196  if (!copy_outside_occlusion_forward)
197    return;
198
199  int last_index = stack_.size() - 1;
200  gfx::Transform old_target_to_new_target_transform(
201      inverse_new_target_screen_space_transform,
202      old_target->render_surface()->screen_space_transform());
203  stack_[last_index].occlusion_from_outside_target =
204      TransformSurfaceOpaqueRegion<RenderSurfaceType>(
205          stack_[last_index - 1].occlusion_from_outside_target,
206          false,
207          gfx::Rect(),
208          old_target_to_new_target_transform);
209  stack_[last_index].occlusion_from_outside_target.Union(
210      TransformSurfaceOpaqueRegion<RenderSurfaceType>(
211          stack_[last_index - 1].occlusion_from_inside_target,
212          false,
213          gfx::Rect(),
214          old_target_to_new_target_transform));
215}
216
217template <typename LayerType, typename RenderSurfaceType>
218void OcclusionTrackerBase<LayerType, RenderSurfaceType>::FinishedRenderTarget(
219    const LayerType* finished_target) {
220  // Make sure we know about the target surface.
221  EnterRenderTarget(finished_target);
222
223  RenderSurfaceType* surface = finished_target->render_surface();
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  // TODO(senorblanco):  Make this smarter for SkImageFilter case:  once
228  // SkImageFilters can report affectsOpacity(), call that.
229  if (finished_target->mask_layer() ||
230      !SurfaceOpacityKnown(surface) ||
231      surface->draw_opacity() < 1 ||
232      finished_target->filters().hasFilterThatAffectsOpacity() ||
233      finished_target->filter()) {
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
242template <typename LayerType>
243static void ReduceOcclusionBelowSurface(LayerType* contributing_layer,
244                                        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 = gfx::ToEnclosingRect(
252      MathUtil::MapClippedRect(surface_transform, gfx::RectF(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
269  gfx::Rect FilterOutsetsInTarget(-outset_left,
270                                  -outset_top,
271                                  outset_left + outset_right,
272                                  outset_top + outset_bottom);
273
274  Region affected_occlusion = IntersectRegions(*occlusion_from_inside_target,
275                                               affected_area_in_target);
276  Region::Iterator affected_occlusion_rects(affected_occlusion);
277
278  occlusion_from_inside_target->Subtract(affected_area_in_target);
279  for (; affected_occlusion_rects.has_rect(); affected_occlusion_rects.next()) {
280    gfx::Rect occlusion_rect = affected_occlusion_rects.rect();
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
303template <typename LayerType, typename RenderSurfaceType>
304void OcclusionTrackerBase<LayerType, RenderSurfaceType>::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 RenderSurfaceType* old_surface = old_target->render_surface();
316
317  Region old_occlusion_from_inside_target_in_new_target =
318      TransformSurfaceOpaqueRegion<RenderSurfaceType>(
319          stack_[last_index].occlusion_from_inside_target,
320          old_surface->is_clipped(),
321          old_surface->clip_rect(),
322          old_surface->draw_transform());
323  if (old_target->has_replica() && !old_target->replica_has_mask()) {
324    old_occlusion_from_inside_target_in_new_target.Union(
325        TransformSurfaceOpaqueRegion<RenderSurfaceType>(
326            stack_[last_index].occlusion_from_inside_target,
327            old_surface->is_clipped(),
328            old_surface->clip_rect(),
329            old_surface->replica_draw_transform()));
330  }
331
332  Region old_occlusion_from_outside_target_in_new_target =
333      TransformSurfaceOpaqueRegion<RenderSurfaceType>(
334          stack_[last_index].occlusion_from_outside_target,
335          false,
336          gfx::Rect(),
337          old_surface->draw_transform());
338
339  gfx::Rect unoccluded_surface_rect;
340  gfx::Rect unoccluded_replica_rect;
341  if (old_target->background_filters().hasFilterThatMovesPixels()) {
342    unoccluded_surface_rect = UnoccludedContributingSurfaceContentRect(
343        old_target, false, old_surface->content_rect(), NULL);
344    if (old_target->has_replica()) {
345      unoccluded_replica_rect = UnoccludedContributingSurfaceContentRect(
346          old_target, true, old_surface->content_rect(), NULL);
347    }
348  }
349
350  if (surface_will_be_at_top_after_pop) {
351    // Merge the top of the stack down.
352    stack_[last_index - 1].occlusion_from_inside_target.Union(
353        old_occlusion_from_inside_target_in_new_target);
354    // TODO(danakj): Strictly this should subtract the inside target occlusion
355    // before union.
356    if (new_target->parent()) {
357      stack_[last_index - 1].occlusion_from_outside_target.Union(
358          old_occlusion_from_outside_target_in_new_target);
359    }
360    stack_.pop_back();
361  } else {
362    // Replace the top of the stack with the new pushed surface.
363    stack_.back().target = new_target;
364    stack_.back().occlusion_from_inside_target =
365        old_occlusion_from_inside_target_in_new_target;
366    if (new_target->parent()) {
367      stack_.back().occlusion_from_outside_target =
368          old_occlusion_from_outside_target_in_new_target;
369    } else {
370      stack_.back().occlusion_from_outside_target.Clear();
371    }
372  }
373
374  if (!old_target->background_filters().hasFilterThatMovesPixels())
375    return;
376
377  ReduceOcclusionBelowSurface(old_target,
378                              unoccluded_surface_rect,
379                              old_surface->draw_transform(),
380                              new_target,
381                              &stack_.back().occlusion_from_inside_target);
382  ReduceOcclusionBelowSurface(old_target,
383                              unoccluded_surface_rect,
384                              old_surface->draw_transform(),
385                              new_target,
386                              &stack_.back().occlusion_from_outside_target);
387
388  if (!old_target->has_replica())
389    return;
390  ReduceOcclusionBelowSurface(old_target,
391                              unoccluded_replica_rect,
392                              old_surface->replica_draw_transform(),
393                              new_target,
394                              &stack_.back().occlusion_from_inside_target);
395  ReduceOcclusionBelowSurface(old_target,
396                              unoccluded_replica_rect,
397                              old_surface->replica_draw_transform(),
398                              new_target,
399                              &stack_.back().occlusion_from_outside_target);
400}
401
402template <typename LayerType, typename RenderSurfaceType>
403void OcclusionTrackerBase<LayerType, RenderSurfaceType>::
404    MarkOccludedBehindLayer(const LayerType* layer) {
405  DCHECK(!stack_.empty());
406  DCHECK_EQ(layer->render_target(), stack_.back().target);
407  if (stack_.empty())
408    return;
409
410  if (!LayerOpacityKnown(layer) || layer->draw_opacity() < 1)
411    return;
412
413  if (LayerIsInUnsorted3dRenderingContext(layer))
414    return;
415
416  if (!LayerTransformsToTargetKnown(layer))
417    return;
418
419  Region opaque_contents = layer->VisibleContentOpaqueRegion();
420  if (opaque_contents.IsEmpty())
421    return;
422
423  DCHECK(layer->visible_content_rect().Contains(opaque_contents.bounds()));
424
425  bool clipped;
426  gfx::QuadF visible_transformed_quad = MathUtil::MapQuad(
427      layer->draw_transform(), gfx::QuadF(opaque_contents.bounds()), &clipped);
428  // FIXME: Find a rect interior to each transformed quad.
429  if (clipped || !visible_transformed_quad.IsRectilinear())
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 (Region::Iterator opaque_content_rects(opaque_contents);
442       opaque_content_rects.has_rect();
443       opaque_content_rects.next()) {
444    gfx::QuadF transformed_quad = MathUtil::MapQuad(
445        layer->draw_transform(),
446        gfx::QuadF(opaque_content_rects.rect()),
447        &clipped);
448    gfx::Rect transformed_rect =
449        gfx::ToEnclosedRect(transformed_quad.BoundingBox());
450    DCHECK(!clipped);  // We only map if the transform preserves axis alignment.
451    transformed_rect.Intersect(clip_rect_in_target);
452    if (transformed_rect.width() < minimum_tracking_size_.width() &&
453        transformed_rect.height() < minimum_tracking_size_.height())
454      continue;
455    stack_.back().occlusion_from_inside_target.Union(transformed_rect);
456
457    if (!occluding_screen_space_rects_)
458      continue;
459
460    // Save the occluding area in screen space for debug visualization.
461    gfx::QuadF screen_space_quad = MathUtil::MapQuad(
462        layer->render_target()->render_surface()->screen_space_transform(),
463        gfx::QuadF(transformed_rect), &clipped);
464    // TODO(danakj): Store the quad in the debug info instead of the bounding
465    // box.
466    gfx::Rect screen_space_rect =
467        gfx::ToEnclosedRect(screen_space_quad.BoundingBox());
468    occluding_screen_space_rects_->push_back(screen_space_rect);
469  }
470
471  if (!non_occluding_screen_space_rects_)
472    return;
473
474  Region non_opaque_contents =
475      SubtractRegions(gfx::Rect(layer->content_bounds()), opaque_contents);
476  for (Region::Iterator non_opaque_content_rects(non_opaque_contents);
477       non_opaque_content_rects.has_rect();
478       non_opaque_content_rects.next()) {
479    // We've already checked for clipping in the MapQuad call above, these calls
480    // should not clip anything further.
481    gfx::Rect transformed_rect = gfx::ToEnclosedRect(
482        MathUtil::MapClippedRect(layer->draw_transform(),
483                                 gfx::RectF(non_opaque_content_rects.rect())));
484    transformed_rect.Intersect(clip_rect_in_target);
485    if (transformed_rect.IsEmpty())
486      continue;
487
488    gfx::QuadF screen_space_quad = MathUtil::MapQuad(
489        layer->render_target()->render_surface()->screen_space_transform(),
490        gfx::QuadF(transformed_rect),
491        &clipped);
492    // TODO(danakj): Store the quad in the debug info instead of the bounding
493    // box.
494    gfx::Rect screen_space_rect =
495        gfx::ToEnclosedRect(screen_space_quad.BoundingBox());
496    non_occluding_screen_space_rects_->push_back(screen_space_rect);
497  }
498}
499
500template <typename LayerType, typename RenderSurfaceType>
501bool OcclusionTrackerBase<LayerType, RenderSurfaceType>::Occluded(
502    const LayerType* render_target,
503    gfx::Rect content_rect,
504    const gfx::Transform& draw_transform,
505    bool impl_draw_transform_is_unknown,
506    bool is_clipped,
507    gfx::Rect clip_rect_in_target,
508    bool* has_occlusion_from_outside_target_surface) const {
509  if (has_occlusion_from_outside_target_surface)
510    *has_occlusion_from_outside_target_surface = false;
511  if (prevent_occlusion_)
512    return false;
513
514  DCHECK(!stack_.empty());
515  if (stack_.empty())
516    return false;
517  if (content_rect.IsEmpty())
518    return true;
519  if (impl_draw_transform_is_unknown)
520    return false;
521
522  // For tests with no render target.
523  if (!render_target)
524    return false;
525
526  DCHECK_EQ(render_target->render_target(), render_target);
527  DCHECK(render_target->render_surface());
528  DCHECK_EQ(render_target, stack_.back().target);
529
530  gfx::Transform inverse_draw_transform(gfx::Transform::kSkipInitialization);
531  if (!draw_transform.GetInverse(&inverse_draw_transform))
532    return false;
533
534  // Take the ToEnclosingRect at each step, as we want to contain any unoccluded
535  // partial pixels in the resulting Rect.
536  Region unoccluded_region_in_target_surface = gfx::ToEnclosingRect(
537      MathUtil::MapClippedRect(draw_transform, gfx::RectF(content_rect)));
538  // Layers can't clip across surfaces, so count this as internal occlusion.
539  if (is_clipped)
540    unoccluded_region_in_target_surface.Intersect(clip_rect_in_target);
541  unoccluded_region_in_target_surface.Subtract(
542      stack_.back().occlusion_from_inside_target);
543  gfx::RectF unoccluded_rect_in_target_surface_without_outside_occlusion =
544      unoccluded_region_in_target_surface.bounds();
545  unoccluded_region_in_target_surface.Subtract(
546      stack_.back().occlusion_from_outside_target);
547
548  // Treat other clipping as occlusion from outside the surface.
549  // TODO(danakj): Clip to visibleContentRect?
550  unoccluded_region_in_target_surface.Intersect(
551      render_target->render_surface()->content_rect());
552  unoccluded_region_in_target_surface.Intersect(
553      ScreenSpaceClipRectInTargetSurface(render_target->render_surface(),
554                                         screen_space_clip_rect_));
555
556  gfx::RectF unoccluded_rect_in_target_surface =
557      unoccluded_region_in_target_surface.bounds();
558
559  if (has_occlusion_from_outside_target_surface) {
560    // Check if the unoccluded rect shrank when applying outside occlusion.
561    *has_occlusion_from_outside_target_surface = !gfx::SubtractRects(
562        unoccluded_rect_in_target_surface_without_outside_occlusion,
563        unoccluded_rect_in_target_surface).IsEmpty();
564  }
565
566  return unoccluded_rect_in_target_surface.IsEmpty();
567}
568
569template <typename LayerType, typename RenderSurfaceType>
570gfx::Rect OcclusionTrackerBase<LayerType, RenderSurfaceType>::
571    UnoccludedContentRect(
572        const LayerType* render_target,
573        gfx::Rect content_rect,
574        const gfx::Transform& draw_transform,
575        bool impl_draw_transform_is_unknown,
576        bool is_clipped,
577        gfx::Rect clip_rect_in_target,
578        bool* has_occlusion_from_outside_target_surface) const {
579  if (has_occlusion_from_outside_target_surface)
580    *has_occlusion_from_outside_target_surface = false;
581  if (prevent_occlusion_)
582    return content_rect;
583
584  DCHECK(!stack_.empty());
585  if (stack_.empty())
586    return content_rect;
587  if (content_rect.IsEmpty())
588    return content_rect;
589  if (impl_draw_transform_is_unknown)
590    return content_rect;
591
592  // For tests with no render target.
593  if (!render_target)
594    return content_rect;
595
596  DCHECK_EQ(render_target->render_target(), render_target);
597  DCHECK(render_target->render_surface());
598  DCHECK_EQ(render_target, stack_.back().target);
599
600  gfx::Transform inverse_draw_transform(gfx::Transform::kSkipInitialization);
601  if (!draw_transform.GetInverse(&inverse_draw_transform))
602    return content_rect;
603
604  // Take the ToEnclosingRect at each step, as we want to contain any unoccluded
605  // partial pixels in the resulting Rect.
606  Region unoccluded_region_in_target_surface = gfx::ToEnclosingRect(
607      MathUtil::MapClippedRect(draw_transform, gfx::RectF(content_rect)));
608  // Layers can't clip across surfaces, so count this as internal occlusion.
609  if (is_clipped)
610    unoccluded_region_in_target_surface.Intersect(clip_rect_in_target);
611  unoccluded_region_in_target_surface.Subtract(
612      stack_.back().occlusion_from_inside_target);
613  gfx::RectF unoccluded_rect_in_target_surface_without_outside_occlusion =
614      unoccluded_region_in_target_surface.bounds();
615  unoccluded_region_in_target_surface.Subtract(
616      stack_.back().occlusion_from_outside_target);
617
618  // Treat other clipping as occlusion from outside the surface.
619  // TODO(danakj): Clip to visibleContentRect?
620  unoccluded_region_in_target_surface.Intersect(
621      render_target->render_surface()->content_rect());
622  unoccluded_region_in_target_surface.Intersect(
623      ScreenSpaceClipRectInTargetSurface(render_target->render_surface(),
624                                         screen_space_clip_rect_));
625
626  gfx::RectF unoccluded_rect_in_target_surface =
627      unoccluded_region_in_target_surface.bounds();
628  gfx::Rect unoccluded_rect = gfx::ToEnclosingRect(
629      MathUtil::ProjectClippedRect(inverse_draw_transform,
630                                   unoccluded_rect_in_target_surface));
631  unoccluded_rect.Intersect(content_rect);
632
633  if (has_occlusion_from_outside_target_surface) {
634    // Check if the unoccluded rect shrank when applying outside occlusion.
635    *has_occlusion_from_outside_target_surface = !gfx::SubtractRects(
636        unoccluded_rect_in_target_surface_without_outside_occlusion,
637        unoccluded_rect_in_target_surface).IsEmpty();
638  }
639
640  return unoccluded_rect;
641}
642
643template <typename LayerType, typename RenderSurfaceType>
644gfx::Rect OcclusionTrackerBase<LayerType, RenderSurfaceType>::
645    UnoccludedContributingSurfaceContentRect(
646        const LayerType* layer,
647        bool for_replica,
648        gfx::Rect content_rect,
649        bool* has_occlusion_from_outside_target_surface) const {
650  DCHECK(!stack_.empty());
651  // The layer is a contributing render_target so it should have a surface.
652  DCHECK(layer->render_surface());
653  // The layer is a contributing render_target so its target should be itself.
654  DCHECK_EQ(layer->render_target(), layer);
655  // The layer should not be the root, else what is is contributing to?
656  DCHECK(layer->parent());
657  // This should be called while the layer is still considered the current
658  // target in the occlusion tracker.
659  DCHECK_EQ(layer, stack_.back().target);
660
661  if (has_occlusion_from_outside_target_surface)
662    *has_occlusion_from_outside_target_surface = false;
663  if (prevent_occlusion_)
664    return content_rect;
665
666  if (content_rect.IsEmpty())
667    return content_rect;
668
669  const RenderSurfaceType* surface = layer->render_surface();
670  const LayerType* contributing_surface_render_target =
671      layer->parent()->render_target();
672
673  if (!SurfaceTransformsToTargetKnown(surface))
674    return content_rect;
675
676  gfx::Transform draw_transform =
677      for_replica ? surface->replica_draw_transform()
678                  : surface->draw_transform();
679  gfx::Transform inverse_draw_transform(gfx::Transform::kSkipInitialization);
680  if (!draw_transform.GetInverse(&inverse_draw_transform))
681    return content_rect;
682
683  // A contributing surface doesn't get occluded by things inside its own
684  // surface, so only things outside the surface can occlude it. That occlusion
685  // is found just below the top of the stack (if it exists).
686  bool has_occlusion = stack_.size() > 1;
687
688  // Take the ToEnclosingRect at each step, as we want to contain any unoccluded
689  // partial pixels in the resulting Rect.
690  Region unoccluded_region_in_target_surface = gfx::ToEnclosingRect(
691      MathUtil::MapClippedRect(draw_transform, gfx::RectF(content_rect)));
692  // Layers can't clip across surfaces, so count this as internal occlusion.
693  if (surface->is_clipped())
694    unoccluded_region_in_target_surface.Intersect(surface->clip_rect());
695  if (has_occlusion) {
696    const StackObject& second_last = stack_[stack_.size() - 2];
697    unoccluded_region_in_target_surface.Subtract(
698        second_last.occlusion_from_inside_target);
699  }
700  gfx::RectF unoccluded_rect_in_target_surface_without_outside_occlusion =
701      unoccluded_region_in_target_surface.bounds();
702  if (has_occlusion) {
703    const StackObject& second_last = stack_[stack_.size() - 2];
704    unoccluded_region_in_target_surface.Subtract(
705        second_last.occlusion_from_outside_target);
706  }
707
708  // Treat other clipping as occlusion from outside the target surface.
709  unoccluded_region_in_target_surface.Intersect(
710      contributing_surface_render_target->render_surface()->content_rect());
711  unoccluded_region_in_target_surface.Intersect(
712      ScreenSpaceClipRectInTargetSurface(
713          contributing_surface_render_target->render_surface(),
714          screen_space_clip_rect_));
715
716  gfx::RectF unoccluded_rect_in_target_surface =
717      unoccluded_region_in_target_surface.bounds();
718  gfx::Rect unoccluded_rect = gfx::ToEnclosingRect(
719      MathUtil::ProjectClippedRect(inverse_draw_transform,
720                                   unoccluded_rect_in_target_surface));
721  unoccluded_rect.Intersect(content_rect);
722
723  if (has_occlusion_from_outside_target_surface) {
724    // Check if the unoccluded rect shrank when applying outside occlusion.
725    *has_occlusion_from_outside_target_surface = !gfx::SubtractRects(
726        unoccluded_rect_in_target_surface_without_outside_occlusion,
727        unoccluded_rect_in_target_surface).IsEmpty();
728  }
729
730  return unoccluded_rect;
731}
732
733// Instantiate (and export) templates here for the linker.
734template class OcclusionTrackerBase<Layer, RenderSurface>;
735template class OcclusionTrackerBase<LayerImpl, RenderSurfaceImpl>;
736
737}  // namespace cc
738