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