Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / cc / trees / occlusion_tracker.cc
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     const 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,
58     const gfx::Rect& screen_space_clip_rect) {
59   gfx::Transform inverse_screen_space_transform(
60       gfx::Transform::kSkipInitialization);
61   if (!target_surface->screen_space_transform().GetInverse(
62           &inverse_screen_space_transform))
63     return target_surface->content_rect();
64
65   return MathUtil::ProjectEnclosingClippedRect(inverse_screen_space_transform,
66                                                screen_space_clip_rect);
67 }
68
69 template <typename RenderSurfaceType>
70 static Region TransformSurfaceOpaqueRegion(
71     const Region& region,
72     bool have_clip_rect,
73     const gfx::Rect& clip_rect_in_new_target,
74     const gfx::Transform& transform) {
75   if (region.IsEmpty())
76     return Region();
77
78   // Verify that rects within the |surface| will remain rects in its target
79   // surface after applying |transform|. If this is true, then apply |transform|
80   // to each rect within |region| in order to transform the entire Region.
81
82   // TODO(danakj): Find a rect interior to each transformed quad.
83   if (!transform.Preserves2dAxisAlignment())
84     return Region();
85
86   // TODO(danakj): If the Region is too complex, degrade gracefully here by
87   // skipping rects in it.
88   Region transformed_region;
89   for (Region::Iterator rects(region); rects.has_rect(); rects.next()) {
90     bool clipped;
91     gfx::QuadF transformed_quad =
92         MathUtil::MapQuad(transform, gfx::QuadF(rects.rect()), &clipped);
93     gfx::Rect transformed_rect =
94         gfx::ToEnclosedRect(transformed_quad.BoundingBox());
95     DCHECK(!clipped);  // We only map if the transform preserves axis alignment.
96     if (have_clip_rect)
97       transformed_rect.Intersect(clip_rect_in_new_target);
98     transformed_region.Union(transformed_rect);
99   }
100   return transformed_region;
101 }
102
103 static inline bool LayerOpacityKnown(const Layer* layer) {
104   return !layer->draw_opacity_is_animating();
105 }
106 static inline bool LayerOpacityKnown(const LayerImpl* layer) {
107   return true;
108 }
109 static inline bool LayerTransformsToTargetKnown(const Layer* layer) {
110   return !layer->draw_transform_is_animating();
111 }
112 static inline bool LayerTransformsToTargetKnown(const LayerImpl* layer) {
113   return true;
114 }
115
116 static inline bool SurfaceOpacityKnown(const RenderSurface* rs) {
117   return !rs->draw_opacity_is_animating();
118 }
119 static inline bool SurfaceOpacityKnown(const RenderSurfaceImpl* rs) {
120   return true;
121 }
122 static inline bool SurfaceTransformsToTargetKnown(const RenderSurface* rs) {
123   return !rs->target_surface_transforms_are_animating();
124 }
125 static inline bool SurfaceTransformsToTargetKnown(const RenderSurfaceImpl* rs) {
126   return true;
127 }
128 static inline bool SurfaceTransformsToScreenKnown(const RenderSurface* rs) {
129   return !rs->screen_space_transforms_are_animating();
130 }
131 static inline bool SurfaceTransformsToScreenKnown(const RenderSurfaceImpl* rs) {
132   return true;
133 }
134
135 static inline bool LayerIsInUnsorted3dRenderingContext(const Layer* layer) {
136   return layer->is_3d_sorted();
137 }
138 static inline bool LayerIsInUnsorted3dRenderingContext(const LayerImpl* layer) {
139   return false;
140 }
141
142 template <typename LayerType>
143 static inline bool LayerIsHidden(const LayerType* layer) {
144   return layer->hide_layer_and_subtree() ||
145          (layer->parent() && LayerIsHidden(layer->parent()));
146 }
147
148 template <typename LayerType, typename RenderSurfaceType>
149 void 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_occlusion_immune_ancestor = NULL;
156   if (!stack_.empty()) {
157     old_target = stack_.back().target;
158     old_occlusion_immune_ancestor =
159         old_target->render_surface()->nearest_occlusion_immune_ancestor();
160   }
161   const RenderSurfaceType* new_occlusion_immune_ancestor =
162       new_target->render_surface()->nearest_occlusion_immune_ancestor();
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 entering an unoccluded subtree, do not carry forward the outside
171   // occlusion calculated so far.
172   bool entering_unoccluded_subtree =
173       new_occlusion_immune_ancestor &&
174       new_occlusion_immune_ancestor != old_occlusion_immune_ancestor;
175
176   bool have_transform_from_screen_to_new_target = false;
177   gfx::Transform inverse_new_target_screen_space_transform(
178       // Note carefully, not used if screen space transform is uninvertible.
179       gfx::Transform::kSkipInitialization);
180   if (SurfaceTransformsToScreenKnown(new_target->render_surface())) {
181     have_transform_from_screen_to_new_target =
182         new_target->render_surface()->screen_space_transform().GetInverse(
183             &inverse_new_target_screen_space_transform);
184   }
185
186   bool entering_root_target = new_target->parent() == NULL;
187
188   bool copy_outside_occlusion_forward =
189       stack_.size() > 1 &&
190       !entering_unoccluded_subtree &&
191       have_transform_from_screen_to_new_target &&
192       !entering_root_target;
193   if (!copy_outside_occlusion_forward)
194     return;
195
196   int last_index = stack_.size() - 1;
197   gfx::Transform old_target_to_new_target_transform(
198       inverse_new_target_screen_space_transform,
199       old_target->render_surface()->screen_space_transform());
200   stack_[last_index].occlusion_from_outside_target =
201       TransformSurfaceOpaqueRegion<RenderSurfaceType>(
202           stack_[last_index - 1].occlusion_from_outside_target,
203           false,
204           gfx::Rect(),
205           old_target_to_new_target_transform);
206   stack_[last_index].occlusion_from_outside_target.Union(
207       TransformSurfaceOpaqueRegion<RenderSurfaceType>(
208           stack_[last_index - 1].occlusion_from_inside_target,
209           false,
210           gfx::Rect(),
211           old_target_to_new_target_transform));
212 }
213
214 template <typename LayerType, typename RenderSurfaceType>
215 void OcclusionTrackerBase<LayerType, RenderSurfaceType>::FinishedRenderTarget(
216     const LayerType* finished_target) {
217   // Make sure we know about the target surface.
218   EnterRenderTarget(finished_target);
219
220   RenderSurfaceType* surface = 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, typename RenderSurfaceType>
298 void OcclusionTrackerBase<LayerType, RenderSurfaceType>::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 RenderSurfaceType* old_surface = old_target->render_surface();
310
311   Region old_occlusion_from_inside_target_in_new_target =
312       TransformSurfaceOpaqueRegion<RenderSurfaceType>(
313           stack_[last_index].occlusion_from_inside_target,
314           old_surface->is_clipped(),
315           old_surface->clip_rect(),
316           old_surface->draw_transform());
317   if (old_target->has_replica() && !old_target->replica_has_mask()) {
318     old_occlusion_from_inside_target_in_new_target.Union(
319         TransformSurfaceOpaqueRegion<RenderSurfaceType>(
320             stack_[last_index].occlusion_from_inside_target,
321             old_surface->is_clipped(),
322             old_surface->clip_rect(),
323             old_surface->replica_draw_transform()));
324   }
325
326   Region old_occlusion_from_outside_target_in_new_target =
327       TransformSurfaceOpaqueRegion<RenderSurfaceType>(
328           stack_[last_index].occlusion_from_outside_target,
329           false,
330           gfx::Rect(),
331           old_surface->draw_transform());
332
333   gfx::Rect unoccluded_surface_rect;
334   gfx::Rect unoccluded_replica_rect;
335   if (old_target->background_filters().HasFilterThatMovesPixels()) {
336     unoccluded_surface_rect = UnoccludedContributingSurfaceContentRect(
337         old_target, false, old_surface->content_rect());
338     if (old_target->has_replica()) {
339       unoccluded_replica_rect = UnoccludedContributingSurfaceContentRect(
340           old_target, true, old_surface->content_rect());
341     }
342   }
343
344   if (surface_will_be_at_top_after_pop) {
345     // Merge the top of the stack down.
346     stack_[last_index - 1].occlusion_from_inside_target.Union(
347         old_occlusion_from_inside_target_in_new_target);
348     // TODO(danakj): Strictly this should subtract the inside target occlusion
349     // before union.
350     if (new_target->parent()) {
351       stack_[last_index - 1].occlusion_from_outside_target.Union(
352           old_occlusion_from_outside_target_in_new_target);
353     }
354     stack_.pop_back();
355   } else {
356     // Replace the top of the stack with the new pushed surface.
357     stack_.back().target = new_target;
358     stack_.back().occlusion_from_inside_target =
359         old_occlusion_from_inside_target_in_new_target;
360     if (new_target->parent()) {
361       stack_.back().occlusion_from_outside_target =
362           old_occlusion_from_outside_target_in_new_target;
363     } else {
364       stack_.back().occlusion_from_outside_target.Clear();
365     }
366   }
367
368   if (!old_target->background_filters().HasFilterThatMovesPixels())
369     return;
370
371   ReduceOcclusionBelowSurface(old_target,
372                               unoccluded_surface_rect,
373                               old_surface->draw_transform(),
374                               new_target,
375                               &stack_.back().occlusion_from_inside_target);
376   ReduceOcclusionBelowSurface(old_target,
377                               unoccluded_surface_rect,
378                               old_surface->draw_transform(),
379                               new_target,
380                               &stack_.back().occlusion_from_outside_target);
381
382   if (!old_target->has_replica())
383     return;
384   ReduceOcclusionBelowSurface(old_target,
385                               unoccluded_replica_rect,
386                               old_surface->replica_draw_transform(),
387                               new_target,
388                               &stack_.back().occlusion_from_inside_target);
389   ReduceOcclusionBelowSurface(old_target,
390                               unoccluded_replica_rect,
391                               old_surface->replica_draw_transform(),
392                               new_target,
393                               &stack_.back().occlusion_from_outside_target);
394 }
395
396 template <typename LayerType, typename RenderSurfaceType>
397 void OcclusionTrackerBase<LayerType, RenderSurfaceType>::
398     MarkOccludedBehindLayer(const LayerType* layer) {
399   DCHECK(!stack_.empty());
400   DCHECK_EQ(layer->render_target(), stack_.back().target);
401   if (stack_.empty())
402     return;
403
404   if (!layer->DrawsContent())
405     return;
406
407   if (!LayerOpacityKnown(layer) || layer->draw_opacity() < 1)
408     return;
409
410   if (!layer->uses_default_blend_mode())
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   // TODO(danakj): Find a rect interior to each transformed quad.
426   if (!layer->draw_transform().Preserves2dAxisAlignment())
427     return;
428
429   gfx::Rect clip_rect_in_target = ScreenSpaceClipRectInTargetSurface(
430       layer->render_target()->render_surface(), screen_space_clip_rect_);
431   if (layer->is_clipped()) {
432     clip_rect_in_target.Intersect(layer->clip_rect());
433   } else {
434     clip_rect_in_target.Intersect(
435         layer->render_target()->render_surface()->content_rect());
436   }
437
438   for (Region::Iterator opaque_content_rects(opaque_contents);
439        opaque_content_rects.has_rect();
440        opaque_content_rects.next()) {
441     bool clipped;
442     gfx::QuadF transformed_quad = MathUtil::MapQuad(
443         layer->draw_transform(),
444         gfx::QuadF(opaque_content_rects.rect()),
445         &clipped);
446     gfx::Rect transformed_rect =
447         gfx::ToEnclosedRect(transformed_quad.BoundingBox());
448     DCHECK(!clipped);  // We only map if the transform preserves axis alignment.
449     transformed_rect.Intersect(clip_rect_in_target);
450     if (transformed_rect.width() < minimum_tracking_size_.width() &&
451         transformed_rect.height() < minimum_tracking_size_.height())
452       continue;
453     stack_.back().occlusion_from_inside_target.Union(transformed_rect);
454
455     if (!occluding_screen_space_rects_)
456       continue;
457
458     // Save the occluding area in screen space for debug visualization.
459     gfx::QuadF screen_space_quad = MathUtil::MapQuad(
460         layer->render_target()->render_surface()->screen_space_transform(),
461         gfx::QuadF(transformed_rect), &clipped);
462     // TODO(danakj): Store the quad in the debug info instead of the bounding
463     // box.
464     gfx::Rect screen_space_rect =
465         gfx::ToEnclosedRect(screen_space_quad.BoundingBox());
466     occluding_screen_space_rects_->push_back(screen_space_rect);
467   }
468
469   if (!non_occluding_screen_space_rects_)
470     return;
471
472   Region non_opaque_contents =
473       SubtractRegions(gfx::Rect(layer->content_bounds()), opaque_contents);
474   for (Region::Iterator non_opaque_content_rects(non_opaque_contents);
475        non_opaque_content_rects.has_rect();
476        non_opaque_content_rects.next()) {
477     // We've already checked for clipping in the MapQuad call above, these calls
478     // should not clip anything further.
479     gfx::Rect transformed_rect = gfx::ToEnclosedRect(
480         MathUtil::MapClippedRect(layer->draw_transform(),
481                                  gfx::RectF(non_opaque_content_rects.rect())));
482     transformed_rect.Intersect(clip_rect_in_target);
483     if (transformed_rect.IsEmpty())
484       continue;
485
486     bool clipped;
487     gfx::QuadF screen_space_quad = MathUtil::MapQuad(
488         layer->render_target()->render_surface()->screen_space_transform(),
489         gfx::QuadF(transformed_rect),
490         &clipped);
491     // TODO(danakj): Store the quad in the debug info instead of the bounding
492     // box.
493     gfx::Rect screen_space_rect =
494         gfx::ToEnclosedRect(screen_space_quad.BoundingBox());
495     non_occluding_screen_space_rects_->push_back(screen_space_rect);
496   }
497 }
498
499 template <typename LayerType, typename RenderSurfaceType>
500 bool OcclusionTrackerBase<LayerType, RenderSurfaceType>::Occluded(
501     const LayerType* render_target,
502     const gfx::Rect& content_rect,
503     const gfx::Transform& draw_transform,
504     bool impl_draw_transform_is_unknown) const {
505   DCHECK(!stack_.empty());
506   if (stack_.empty())
507     return false;
508   if (content_rect.IsEmpty())
509     return true;
510   if (impl_draw_transform_is_unknown)
511     return false;
512
513   // For tests with no render target.
514   if (!render_target)
515     return false;
516
517   DCHECK_EQ(render_target->render_target(), render_target);
518   DCHECK(render_target->render_surface());
519   DCHECK_EQ(render_target, stack_.back().target);
520
521   if (stack_.back().occlusion_from_inside_target.IsEmpty() &&
522       stack_.back().occlusion_from_outside_target.IsEmpty()) {
523     return false;
524   }
525
526   gfx::Transform inverse_draw_transform(gfx::Transform::kSkipInitialization);
527   if (!draw_transform.GetInverse(&inverse_draw_transform))
528     return false;
529
530   // Take the ToEnclosingRect at each step, as we want to contain any unoccluded
531   // partial pixels in the resulting Rect.
532   Region unoccluded_region_in_target_surface =
533       MathUtil::MapEnclosingClippedRect(draw_transform, content_rect);
534   unoccluded_region_in_target_surface.Subtract(
535       stack_.back().occlusion_from_inside_target);
536   gfx::RectF unoccluded_rect_in_target_surface_without_outside_occlusion =
537       unoccluded_region_in_target_surface.bounds();
538   unoccluded_region_in_target_surface.Subtract(
539       stack_.back().occlusion_from_outside_target);
540
541   gfx::RectF unoccluded_rect_in_target_surface =
542       unoccluded_region_in_target_surface.bounds();
543
544   return unoccluded_rect_in_target_surface.IsEmpty();
545 }
546
547 template <typename LayerType, typename RenderSurfaceType>
548 gfx::Rect OcclusionTrackerBase<LayerType, RenderSurfaceType>::
549     UnoccludedContentRect(
550         const LayerType* render_target,
551         const gfx::Rect& content_rect,
552         const gfx::Transform& draw_transform,
553         bool impl_draw_transform_is_unknown) const {
554   DCHECK(!stack_.empty());
555   if (stack_.empty())
556     return content_rect;
557   if (content_rect.IsEmpty())
558     return content_rect;
559   if (impl_draw_transform_is_unknown)
560     return content_rect;
561
562   // For tests with no render target.
563   if (!render_target)
564     return content_rect;
565
566   DCHECK_EQ(render_target->render_target(), render_target);
567   DCHECK(render_target->render_surface());
568   DCHECK_EQ(render_target, stack_.back().target);
569
570   if (stack_.back().occlusion_from_inside_target.IsEmpty() &&
571       stack_.back().occlusion_from_outside_target.IsEmpty()) {
572     return content_rect;
573   }
574
575   gfx::Transform inverse_draw_transform(gfx::Transform::kSkipInitialization);
576   if (!draw_transform.GetInverse(&inverse_draw_transform))
577     return content_rect;
578
579   // Take the ToEnclosingRect at each step, as we want to contain any unoccluded
580   // partial pixels in the resulting Rect.
581   Region unoccluded_region_in_target_surface =
582       MathUtil::MapEnclosingClippedRect(draw_transform, content_rect);
583   unoccluded_region_in_target_surface.Subtract(
584       stack_.back().occlusion_from_inside_target);
585   unoccluded_region_in_target_surface.Subtract(
586       stack_.back().occlusion_from_outside_target);
587
588   gfx::Rect unoccluded_rect_in_target_surface =
589       unoccluded_region_in_target_surface.bounds();
590   gfx::Rect unoccluded_rect = MathUtil::ProjectEnclosingClippedRect(
591       inverse_draw_transform, unoccluded_rect_in_target_surface);
592   unoccluded_rect.Intersect(content_rect);
593
594   return unoccluded_rect;
595 }
596
597 template <typename LayerType, typename RenderSurfaceType>
598 gfx::Rect OcclusionTrackerBase<LayerType, RenderSurfaceType>::
599     UnoccludedContributingSurfaceContentRect(
600         const LayerType* layer,
601         bool for_replica,
602         const gfx::Rect& content_rect) const {
603   DCHECK(!stack_.empty());
604   // The layer is a contributing render_target so it should have a surface.
605   DCHECK(layer->render_surface());
606   // The layer is a contributing render_target so its target should be itself.
607   DCHECK_EQ(layer->render_target(), layer);
608   // The layer should not be the root, else what is is contributing to?
609   DCHECK(layer->parent());
610   // This should be called while the layer is still considered the current
611   // target in the occlusion tracker.
612   DCHECK_EQ(layer, stack_.back().target);
613
614   if (content_rect.IsEmpty())
615     return content_rect;
616
617   const RenderSurfaceType* surface = layer->render_surface();
618   const LayerType* contributing_surface_render_target =
619       layer->parent()->render_target();
620
621   if (!SurfaceTransformsToTargetKnown(surface))
622     return content_rect;
623
624   gfx::Transform draw_transform =
625       for_replica ? surface->replica_draw_transform()
626                   : surface->draw_transform();
627   gfx::Transform inverse_draw_transform(gfx::Transform::kSkipInitialization);
628   if (!draw_transform.GetInverse(&inverse_draw_transform))
629     return content_rect;
630
631   // A contributing surface doesn't get occluded by things inside its own
632   // surface, so only things outside the surface can occlude it. That occlusion
633   // is found just below the top of the stack (if it exists).
634   bool has_occlusion = stack_.size() > 1;
635
636   // Take the ToEnclosingRect at each step, as we want to contain any unoccluded
637   // partial pixels in the resulting Rect.
638   Region unoccluded_region_in_target_surface =
639       MathUtil::MapEnclosingClippedRect(draw_transform, content_rect);
640   // Layers can't clip across surfaces, so count this as internal occlusion.
641   if (surface->is_clipped())
642     unoccluded_region_in_target_surface.Intersect(surface->clip_rect());
643   if (has_occlusion) {
644     const StackObject& second_last = stack_[stack_.size() - 2];
645     unoccluded_region_in_target_surface.Subtract(
646         second_last.occlusion_from_inside_target);
647     unoccluded_region_in_target_surface.Subtract(
648         second_last.occlusion_from_outside_target);
649   }
650
651   // Treat other clipping as occlusion from outside the target surface.
652   unoccluded_region_in_target_surface.Intersect(
653       contributing_surface_render_target->render_surface()->content_rect());
654   unoccluded_region_in_target_surface.Intersect(
655       ScreenSpaceClipRectInTargetSurface(
656           contributing_surface_render_target->render_surface(),
657           screen_space_clip_rect_));
658
659   gfx::Rect unoccluded_rect_in_target_surface =
660       unoccluded_region_in_target_surface.bounds();
661   gfx::Rect unoccluded_rect = MathUtil::ProjectEnclosingClippedRect(
662       inverse_draw_transform, 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