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.
5 #include "cc/trees/occlusion_tracker.h"
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"
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
) {}
28 template <typename LayerType
, typename RenderSurfaceType
>
29 OcclusionTrackerBase
<LayerType
, RenderSurfaceType
>::~OcclusionTrackerBase() {}
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
;
36 if (layer_iterator
.represents_itself
)
37 EnterRenderTarget(render_target
);
38 else if (layer_iterator
.represents_target_render_surface
)
39 FinishedRenderTarget(render_target
);
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
;
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
);
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();
64 return gfx::ToEnclosingRect(MathUtil::ProjectClippedRect(
65 inverse_screen_space_transform
, screen_space_clip_rect
));
68 template <typename RenderSurfaceType
>
69 static Region
TransformSurfaceOpaqueRegion(const Region
& region
,
71 gfx::Rect clip_rect_in_new_target
,
72 const gfx::Transform
& transform
) {
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.
81 gfx::QuadF transformed_bounds_quad
=
82 MathUtil::MapQuad(transform
, gfx::QuadF(region
.bounds()), &clipped
);
83 // FIXME: Find a rect interior to each transformed quad.
84 if (clipped
|| !transformed_bounds_quad
.IsRectilinear())
87 // TODO(danakj): If the Region is too complex, degrade gracefully here by
88 // skipping rects in it.
89 Region transformed_region
;
90 for (Region::Iterator
rects(region
); rects
.has_rect(); rects
.next()) {
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.
97 transformed_rect
.Intersect(clip_rect_in_new_target
);
98 transformed_region
.Union(transformed_rect
);
100 return transformed_region
;
103 static inline bool LayerOpacityKnown(const Layer
* layer
) {
104 return !layer
->draw_opacity_is_animating();
106 static inline bool LayerOpacityKnown(const LayerImpl
* layer
) {
109 static inline bool LayerTransformsToTargetKnown(const Layer
* layer
) {
110 return !layer
->draw_transform_is_animating();
112 static inline bool LayerTransformsToTargetKnown(const LayerImpl
* layer
) {
116 static inline bool SurfaceOpacityKnown(const RenderSurface
* rs
) {
117 return !rs
->draw_opacity_is_animating();
119 static inline bool SurfaceOpacityKnown(const RenderSurfaceImpl
* rs
) {
122 static inline bool SurfaceTransformsToTargetKnown(const RenderSurface
* rs
) {
123 return !rs
->target_surface_transforms_are_animating();
125 static inline bool SurfaceTransformsToTargetKnown(const RenderSurfaceImpl
* rs
) {
128 static inline bool SurfaceTransformsToScreenKnown(const RenderSurface
* rs
) {
129 return !rs
->screen_space_transforms_are_animating();
131 static inline bool SurfaceTransformsToScreenKnown(const RenderSurfaceImpl
* rs
) {
135 static inline bool LayerIsInUnsorted3dRenderingContext(const Layer
* layer
) {
136 return layer
->parent() && layer
->parent()->preserves_3d();
138 static inline bool LayerIsInUnsorted3dRenderingContext(const LayerImpl
* layer
) {
142 template <typename LayerType
, typename RenderSurfaceType
>
143 void OcclusionTrackerBase
<LayerType
, RenderSurfaceType
>::EnterRenderTarget(
144 const LayerType
* new_target
) {
145 if (!stack_
.empty() && stack_
.back().target
== new_target
)
148 const LayerType
* old_target
= NULL
;
149 const RenderSurfaceType
* old_ancestor_that_moves_pixels
= NULL
;
150 if (!stack_
.empty()) {
151 old_target
= stack_
.back().target
;
152 old_ancestor_that_moves_pixels
=
153 old_target
->render_surface()->nearest_ancestor_that_moves_pixels();
155 const RenderSurfaceType
* new_ancestor_that_moves_pixels
=
156 new_target
->render_surface()->nearest_ancestor_that_moves_pixels();
158 stack_
.push_back(StackObject(new_target
));
160 // We copy the screen occlusion into the new RenderSurface subtree, but we
161 // never copy in the occlusion from inside the target, since we are looking
162 // at a new RenderSurface target.
164 // If we are entering a subtree that is going to move pixels around, then the
165 // occlusion we've computed so far won't apply to the pixels we're drawing
166 // here in the same way. We discard the occlusion thus far to be safe, and
167 // ensure we don't cull any pixels that are moved such that they become
169 bool entering_subtree_that_moves_pixels
=
170 new_ancestor_that_moves_pixels
&&
171 new_ancestor_that_moves_pixels
!= old_ancestor_that_moves_pixels
;
173 bool have_transform_from_screen_to_new_target
= false;
174 gfx::Transform
inverse_new_target_screen_space_transform(
175 // Note carefully, not used if screen space transform is uninvertible.
176 gfx::Transform::kSkipInitialization
);
177 if (SurfaceTransformsToScreenKnown(new_target
->render_surface())) {
178 have_transform_from_screen_to_new_target
=
179 new_target
->render_surface()->screen_space_transform().GetInverse(
180 &inverse_new_target_screen_space_transform
);
183 bool entering_root_target
= new_target
->parent() == NULL
;
185 bool copy_outside_occlusion_forward
=
187 !entering_subtree_that_moves_pixels
&&
188 have_transform_from_screen_to_new_target
&&
189 !entering_root_target
;
190 if (!copy_outside_occlusion_forward
)
193 int last_index
= stack_
.size() - 1;
194 gfx::Transform
old_target_to_new_target_transform(
195 inverse_new_target_screen_space_transform
,
196 old_target
->render_surface()->screen_space_transform());
197 stack_
[last_index
].occlusion_from_outside_target
=
198 TransformSurfaceOpaqueRegion
<RenderSurfaceType
>(
199 stack_
[last_index
- 1].occlusion_from_outside_target
,
202 old_target_to_new_target_transform
);
203 stack_
[last_index
].occlusion_from_outside_target
.Union(
204 TransformSurfaceOpaqueRegion
<RenderSurfaceType
>(
205 stack_
[last_index
- 1].occlusion_from_inside_target
,
208 old_target_to_new_target_transform
));
211 template <typename LayerType
, typename RenderSurfaceType
>
212 void OcclusionTrackerBase
<LayerType
, RenderSurfaceType
>::FinishedRenderTarget(
213 const LayerType
* finished_target
) {
214 // Make sure we know about the target surface.
215 EnterRenderTarget(finished_target
);
217 RenderSurfaceType
* surface
= finished_target
->render_surface();
219 // If the occlusion within the surface can not be applied to things outside of
220 // the surface's subtree, then clear the occlusion here so it won't be used.
221 // TODO(senorblanco): Make this smarter for SkImageFilter case: once
222 // SkImageFilters can report affectsOpacity(), call that.
223 if (finished_target
->mask_layer() ||
224 !SurfaceOpacityKnown(surface
) ||
225 surface
->draw_opacity() < 1 ||
226 finished_target
->filters().hasFilterThatAffectsOpacity() ||
227 finished_target
->filter()) {
228 stack_
.back().occlusion_from_outside_target
.Clear();
229 stack_
.back().occlusion_from_inside_target
.Clear();
230 } else if (!SurfaceTransformsToTargetKnown(surface
)) {
231 stack_
.back().occlusion_from_inside_target
.Clear();
232 stack_
.back().occlusion_from_outside_target
.Clear();
236 template <typename LayerType
>
237 static void ReduceOcclusionBelowSurface(LayerType
* contributing_layer
,
238 gfx::Rect surface_rect
,
239 const gfx::Transform
& surface_transform
,
240 LayerType
* render_target
,
241 Region
* occlusion_from_inside_target
) {
242 if (surface_rect
.IsEmpty())
245 gfx::Rect affected_area_in_target
= gfx::ToEnclosingRect(
246 MathUtil::MapClippedRect(surface_transform
, gfx::RectF(surface_rect
)));
247 if (contributing_layer
->render_surface()->is_clipped()) {
248 affected_area_in_target
.Intersect(
249 contributing_layer
->render_surface()->clip_rect());
251 if (affected_area_in_target
.IsEmpty())
254 int outset_top
, outset_right
, outset_bottom
, outset_left
;
255 contributing_layer
->background_filters().getOutsets(
256 outset_top
, outset_right
, outset_bottom
, outset_left
);
258 // The filter can move pixels from outside of the clip, so allow affected_area
259 // to expand outside the clip.
260 affected_area_in_target
.Inset(
261 -outset_left
, -outset_top
, -outset_right
, -outset_bottom
);
263 gfx::Rect
FilterOutsetsInTarget(-outset_left
,
265 outset_left
+ outset_right
,
266 outset_top
+ outset_bottom
);
268 Region affected_occlusion
= IntersectRegions(*occlusion_from_inside_target
,
269 affected_area_in_target
);
270 Region::Iterator
affected_occlusion_rects(affected_occlusion
);
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();
276 // Shrink the rect by expanding the non-opaque pixels outside the rect.
278 // The left outset of the filters moves pixels on the right side of
279 // the occlusion_rect into it, shrinking its right edge.
281 occlusion_rect
.x() == affected_area_in_target
.x() ? 0 : outset_right
;
283 occlusion_rect
.y() == affected_area_in_target
.y() ? 0 : outset_bottom
;
285 occlusion_rect
.right() == affected_area_in_target
.right() ?
288 occlusion_rect
.bottom() == affected_area_in_target
.bottom() ?
291 occlusion_rect
.Inset(shrink_left
, shrink_top
, shrink_right
, shrink_bottom
);
293 occlusion_from_inside_target
->Union(occlusion_rect
);
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
;
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.
308 const LayerType
* old_target
= stack_
[last_index
].target
;
309 const RenderSurfaceType
* old_surface
= old_target
->render_surface();
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()));
326 Region old_occlusion_from_outside_target_in_new_target
=
327 TransformSurfaceOpaqueRegion
<RenderSurfaceType
>(
328 stack_
[last_index
].occlusion_from_outside_target
,
331 old_surface
->draw_transform());
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(), NULL
);
338 if (old_target
->has_replica()) {
339 unoccluded_replica_rect
= UnoccludedContributingSurfaceContentRect(
340 old_target
, true, old_surface
->content_rect(), NULL
);
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
350 if (new_target
->parent()) {
351 stack_
[last_index
- 1].occlusion_from_outside_target
.Union(
352 old_occlusion_from_outside_target_in_new_target
);
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
;
364 stack_
.back().occlusion_from_outside_target
.Clear();
368 if (!old_target
->background_filters().hasFilterThatMovesPixels())
371 ReduceOcclusionBelowSurface(old_target
,
372 unoccluded_surface_rect
,
373 old_surface
->draw_transform(),
375 &stack_
.back().occlusion_from_inside_target
);
376 ReduceOcclusionBelowSurface(old_target
,
377 unoccluded_surface_rect
,
378 old_surface
->draw_transform(),
380 &stack_
.back().occlusion_from_outside_target
);
382 if (!old_target
->has_replica())
384 ReduceOcclusionBelowSurface(old_target
,
385 unoccluded_replica_rect
,
386 old_surface
->replica_draw_transform(),
388 &stack_
.back().occlusion_from_inside_target
);
389 ReduceOcclusionBelowSurface(old_target
,
390 unoccluded_replica_rect
,
391 old_surface
->replica_draw_transform(),
393 &stack_
.back().occlusion_from_outside_target
);
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
);
404 if (!LayerOpacityKnown(layer
) || layer
->draw_opacity() < 1)
407 if (LayerIsInUnsorted3dRenderingContext(layer
))
410 if (!LayerTransformsToTargetKnown(layer
))
413 Region opaque_contents
= layer
->VisibleContentOpaqueRegion();
414 if (opaque_contents
.IsEmpty())
417 DCHECK(layer
->visible_content_rect().Contains(opaque_contents
.bounds()));
420 gfx::QuadF visible_transformed_quad
= MathUtil::MapQuad(
421 layer
->draw_transform(), gfx::QuadF(opaque_contents
.bounds()), &clipped
);
422 // FIXME: Find a rect interior to each transformed quad.
423 if (clipped
|| !visible_transformed_quad
.IsRectilinear())
426 gfx::Rect clip_rect_in_target
= gfx::IntersectRects(
427 layer
->render_target()->render_surface()->content_rect(),
428 ScreenSpaceClipRectInTargetSurface(
429 layer
->render_target()->render_surface(), screen_space_clip_rect_
));
431 for (Region::Iterator
opaque_content_rects(opaque_contents
);
432 opaque_content_rects
.has_rect();
433 opaque_content_rects
.next()) {
434 gfx::QuadF transformed_quad
= MathUtil::MapQuad(
435 layer
->draw_transform(),
436 gfx::QuadF(opaque_content_rects
.rect()),
438 gfx::Rect transformed_rect
=
439 gfx::ToEnclosedRect(transformed_quad
.BoundingBox());
440 DCHECK(!clipped
); // We only map if the transform preserves axis alignment.
441 transformed_rect
.Intersect(clip_rect_in_target
);
442 if (transformed_rect
.width() < minimum_tracking_size_
.width() &&
443 transformed_rect
.height() < minimum_tracking_size_
.height())
445 stack_
.back().occlusion_from_inside_target
.Union(transformed_rect
);
447 if (!occluding_screen_space_rects_
)
450 // Save the occluding area in screen space for debug visualization.
451 gfx::QuadF screen_space_quad
= MathUtil::MapQuad(
452 layer
->render_target()->render_surface()->screen_space_transform(),
453 gfx::QuadF(transformed_rect
), &clipped
);
454 // TODO(danakj): Store the quad in the debug info instead of the bounding
456 gfx::Rect screen_space_rect
=
457 gfx::ToEnclosedRect(screen_space_quad
.BoundingBox());
458 occluding_screen_space_rects_
->push_back(screen_space_rect
);
461 if (!non_occluding_screen_space_rects_
)
464 Region non_opaque_contents
=
465 SubtractRegions(gfx::Rect(layer
->content_bounds()), opaque_contents
);
466 for (Region::Iterator
non_opaque_content_rects(non_opaque_contents
);
467 non_opaque_content_rects
.has_rect();
468 non_opaque_content_rects
.next()) {
469 // We've already checked for clipping in the MapQuad call above, these calls
470 // should not clip anything further.
471 gfx::Rect transformed_rect
= gfx::ToEnclosedRect(
472 MathUtil::MapClippedRect(layer
->draw_transform(),
473 gfx::RectF(non_opaque_content_rects
.rect())));
474 transformed_rect
.Intersect(clip_rect_in_target
);
475 if (transformed_rect
.IsEmpty())
478 gfx::QuadF screen_space_quad
= MathUtil::MapQuad(
479 layer
->render_target()->render_surface()->screen_space_transform(),
480 gfx::QuadF(transformed_rect
),
482 // TODO(danakj): Store the quad in the debug info instead of the bounding
484 gfx::Rect screen_space_rect
=
485 gfx::ToEnclosedRect(screen_space_quad
.BoundingBox());
486 non_occluding_screen_space_rects_
->push_back(screen_space_rect
);
490 template <typename LayerType
, typename RenderSurfaceType
>
491 bool OcclusionTrackerBase
<LayerType
, RenderSurfaceType
>::Occluded(
492 const LayerType
* render_target
,
493 gfx::Rect content_rect
,
494 const gfx::Transform
& draw_transform
,
495 bool impl_draw_transform_is_unknown
,
497 gfx::Rect clip_rect_in_target
,
498 bool* has_occlusion_from_outside_target_surface
) const {
499 if (has_occlusion_from_outside_target_surface
)
500 *has_occlusion_from_outside_target_surface
= false;
502 DCHECK(!stack_
.empty());
505 if (content_rect
.IsEmpty())
507 if (impl_draw_transform_is_unknown
)
510 // For tests with no render target.
514 DCHECK_EQ(render_target
->render_target(), render_target
);
515 DCHECK(render_target
->render_surface());
516 DCHECK_EQ(render_target
, stack_
.back().target
);
518 gfx::Transform
inverse_draw_transform(gfx::Transform::kSkipInitialization
);
519 if (!draw_transform
.GetInverse(&inverse_draw_transform
))
522 // Take the ToEnclosingRect at each step, as we want to contain any unoccluded
523 // partial pixels in the resulting Rect.
524 Region unoccluded_region_in_target_surface
= gfx::ToEnclosingRect(
525 MathUtil::MapClippedRect(draw_transform
, gfx::RectF(content_rect
)));
526 // Layers can't clip across surfaces, so count this as internal occlusion.
528 unoccluded_region_in_target_surface
.Intersect(clip_rect_in_target
);
529 unoccluded_region_in_target_surface
.Subtract(
530 stack_
.back().occlusion_from_inside_target
);
531 gfx::RectF unoccluded_rect_in_target_surface_without_outside_occlusion
=
532 unoccluded_region_in_target_surface
.bounds();
533 unoccluded_region_in_target_surface
.Subtract(
534 stack_
.back().occlusion_from_outside_target
);
536 // Treat other clipping as occlusion from outside the surface.
537 // TODO(danakj): Clip to visibleContentRect?
538 unoccluded_region_in_target_surface
.Intersect(
539 render_target
->render_surface()->content_rect());
540 unoccluded_region_in_target_surface
.Intersect(
541 ScreenSpaceClipRectInTargetSurface(render_target
->render_surface(),
542 screen_space_clip_rect_
));
544 gfx::RectF unoccluded_rect_in_target_surface
=
545 unoccluded_region_in_target_surface
.bounds();
547 if (has_occlusion_from_outside_target_surface
) {
548 // Check if the unoccluded rect shrank when applying outside occlusion.
549 *has_occlusion_from_outside_target_surface
= !gfx::SubtractRects(
550 unoccluded_rect_in_target_surface_without_outside_occlusion
,
551 unoccluded_rect_in_target_surface
).IsEmpty();
554 return unoccluded_rect_in_target_surface
.IsEmpty();
557 template <typename LayerType
, typename RenderSurfaceType
>
558 gfx::Rect OcclusionTrackerBase
<LayerType
, RenderSurfaceType
>::
559 UnoccludedContentRect(
560 const LayerType
* render_target
,
561 gfx::Rect content_rect
,
562 const gfx::Transform
& draw_transform
,
563 bool impl_draw_transform_is_unknown
,
565 gfx::Rect clip_rect_in_target
,
566 bool* has_occlusion_from_outside_target_surface
) const {
567 if (has_occlusion_from_outside_target_surface
)
568 *has_occlusion_from_outside_target_surface
= false;
570 DCHECK(!stack_
.empty());
573 if (content_rect
.IsEmpty())
575 if (impl_draw_transform_is_unknown
)
578 // For tests with no render target.
582 DCHECK_EQ(render_target
->render_target(), render_target
);
583 DCHECK(render_target
->render_surface());
584 DCHECK_EQ(render_target
, stack_
.back().target
);
586 gfx::Transform
inverse_draw_transform(gfx::Transform::kSkipInitialization
);
587 if (!draw_transform
.GetInverse(&inverse_draw_transform
))
590 // Take the ToEnclosingRect at each step, as we want to contain any unoccluded
591 // partial pixels in the resulting Rect.
592 Region unoccluded_region_in_target_surface
= gfx::ToEnclosingRect(
593 MathUtil::MapClippedRect(draw_transform
, gfx::RectF(content_rect
)));
594 // Layers can't clip across surfaces, so count this as internal occlusion.
596 unoccluded_region_in_target_surface
.Intersect(clip_rect_in_target
);
597 unoccluded_region_in_target_surface
.Subtract(
598 stack_
.back().occlusion_from_inside_target
);
599 gfx::RectF unoccluded_rect_in_target_surface_without_outside_occlusion
=
600 unoccluded_region_in_target_surface
.bounds();
601 unoccluded_region_in_target_surface
.Subtract(
602 stack_
.back().occlusion_from_outside_target
);
604 // Treat other clipping as occlusion from outside the surface.
605 // TODO(danakj): Clip to visibleContentRect?
606 unoccluded_region_in_target_surface
.Intersect(
607 render_target
->render_surface()->content_rect());
608 unoccluded_region_in_target_surface
.Intersect(
609 ScreenSpaceClipRectInTargetSurface(render_target
->render_surface(),
610 screen_space_clip_rect_
));
612 gfx::RectF unoccluded_rect_in_target_surface
=
613 unoccluded_region_in_target_surface
.bounds();
614 gfx::Rect unoccluded_rect
= gfx::ToEnclosingRect(
615 MathUtil::ProjectClippedRect(inverse_draw_transform
,
616 unoccluded_rect_in_target_surface
));
617 unoccluded_rect
.Intersect(content_rect
);
619 if (has_occlusion_from_outside_target_surface
) {
620 // Check if the unoccluded rect shrank when applying outside occlusion.
621 *has_occlusion_from_outside_target_surface
= !gfx::SubtractRects(
622 unoccluded_rect_in_target_surface_without_outside_occlusion
,
623 unoccluded_rect_in_target_surface
).IsEmpty();
626 return unoccluded_rect
;
629 template <typename LayerType
, typename RenderSurfaceType
>
630 gfx::Rect OcclusionTrackerBase
<LayerType
, RenderSurfaceType
>::
631 UnoccludedContributingSurfaceContentRect(
632 const LayerType
* layer
,
634 gfx::Rect content_rect
,
635 bool* has_occlusion_from_outside_target_surface
) const {
636 DCHECK(!stack_
.empty());
637 // The layer is a contributing render_target so it should have a surface.
638 DCHECK(layer
->render_surface());
639 // The layer is a contributing render_target so its target should be itself.
640 DCHECK_EQ(layer
->render_target(), layer
);
641 // The layer should not be the root, else what is is contributing to?
642 DCHECK(layer
->parent());
643 // This should be called while the layer is still considered the current
644 // target in the occlusion tracker.
645 DCHECK_EQ(layer
, stack_
.back().target
);
647 if (has_occlusion_from_outside_target_surface
)
648 *has_occlusion_from_outside_target_surface
= false;
650 if (content_rect
.IsEmpty())
653 const RenderSurfaceType
* surface
= layer
->render_surface();
654 const LayerType
* contributing_surface_render_target
=
655 layer
->parent()->render_target();
657 if (!SurfaceTransformsToTargetKnown(surface
))
660 gfx::Transform draw_transform
=
661 for_replica
? surface
->replica_draw_transform()
662 : surface
->draw_transform();
663 gfx::Transform
inverse_draw_transform(gfx::Transform::kSkipInitialization
);
664 if (!draw_transform
.GetInverse(&inverse_draw_transform
))
667 // A contributing surface doesn't get occluded by things inside its own
668 // surface, so only things outside the surface can occlude it. That occlusion
669 // is found just below the top of the stack (if it exists).
670 bool has_occlusion
= stack_
.size() > 1;
672 // Take the ToEnclosingRect at each step, as we want to contain any unoccluded
673 // partial pixels in the resulting Rect.
674 Region unoccluded_region_in_target_surface
= gfx::ToEnclosingRect(
675 MathUtil::MapClippedRect(draw_transform
, gfx::RectF(content_rect
)));
676 // Layers can't clip across surfaces, so count this as internal occlusion.
677 if (surface
->is_clipped())
678 unoccluded_region_in_target_surface
.Intersect(surface
->clip_rect());
680 const StackObject
& second_last
= stack_
[stack_
.size() - 2];
681 unoccluded_region_in_target_surface
.Subtract(
682 second_last
.occlusion_from_inside_target
);
684 gfx::RectF unoccluded_rect_in_target_surface_without_outside_occlusion
=
685 unoccluded_region_in_target_surface
.bounds();
687 const StackObject
& second_last
= stack_
[stack_
.size() - 2];
688 unoccluded_region_in_target_surface
.Subtract(
689 second_last
.occlusion_from_outside_target
);
692 // Treat other clipping as occlusion from outside the target surface.
693 unoccluded_region_in_target_surface
.Intersect(
694 contributing_surface_render_target
->render_surface()->content_rect());
695 unoccluded_region_in_target_surface
.Intersect(
696 ScreenSpaceClipRectInTargetSurface(
697 contributing_surface_render_target
->render_surface(),
698 screen_space_clip_rect_
));
700 gfx::RectF unoccluded_rect_in_target_surface
=
701 unoccluded_region_in_target_surface
.bounds();
702 gfx::Rect unoccluded_rect
= gfx::ToEnclosingRect(
703 MathUtil::ProjectClippedRect(inverse_draw_transform
,
704 unoccluded_rect_in_target_surface
));
705 unoccluded_rect
.Intersect(content_rect
);
707 if (has_occlusion_from_outside_target_surface
) {
708 // Check if the unoccluded rect shrank when applying outside occlusion.
709 *has_occlusion_from_outside_target_surface
= !gfx::SubtractRects(
710 unoccluded_rect_in_target_surface_without_outside_occlusion
,
711 unoccluded_rect_in_target_surface
).IsEmpty();
714 return unoccluded_rect
;
717 // Instantiate (and export) templates here for the linker.
718 template class OcclusionTrackerBase
<Layer
, RenderSurface
>;
719 template class OcclusionTrackerBase
<LayerImpl
, RenderSurfaceImpl
>;