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 prevent_occlusion_(false),
26 occluding_screen_space_rects_(NULL
),
27 non_occluding_screen_space_rects_(NULL
) {}
29 template <typename LayerType
, typename RenderSurfaceType
>
30 OcclusionTrackerBase
<LayerType
, RenderSurfaceType
>::~OcclusionTrackerBase() {}
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
;
38 if (layer_iterator
.represents_itself
)
39 EnterRenderTarget(render_target
);
40 else if (layer_iterator
.represents_target_render_surface
)
41 FinishedRenderTarget(render_target
);
43 prevent_occlusion_
= prevent_occlusion
;
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
;
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
);
58 prevent_occlusion_
= false;
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();
70 return gfx::ToEnclosingRect(MathUtil::ProjectClippedRect(
71 inverse_screen_space_transform
, screen_space_clip_rect
));
74 template <typename RenderSurfaceType
>
75 static Region
TransformSurfaceOpaqueRegion(const Region
& region
,
77 gfx::Rect clip_rect_in_new_target
,
78 const gfx::Transform
& transform
) {
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.
86 // TODO(danakj): Find a rect interior to each transformed quad.
87 if (!transform
.Preserves2dAxisAlignment())
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()) {
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.
101 transformed_rect
.Intersect(clip_rect_in_new_target
);
102 transformed_region
.Union(transformed_rect
);
104 return transformed_region
;
107 static inline bool LayerOpacityKnown(const Layer
* layer
) {
108 return !layer
->draw_opacity_is_animating();
110 static inline bool LayerOpacityKnown(const LayerImpl
* layer
) {
113 static inline bool LayerTransformsToTargetKnown(const Layer
* layer
) {
114 return !layer
->draw_transform_is_animating();
116 static inline bool LayerTransformsToTargetKnown(const LayerImpl
* layer
) {
120 static inline bool SurfaceOpacityKnown(const RenderSurface
* rs
) {
121 return !rs
->draw_opacity_is_animating();
123 static inline bool SurfaceOpacityKnown(const RenderSurfaceImpl
* rs
) {
126 static inline bool SurfaceTransformsToTargetKnown(const RenderSurface
* rs
) {
127 return !rs
->target_surface_transforms_are_animating();
129 static inline bool SurfaceTransformsToTargetKnown(const RenderSurfaceImpl
* rs
) {
132 static inline bool SurfaceTransformsToScreenKnown(const RenderSurface
* rs
) {
133 return !rs
->screen_space_transforms_are_animating();
135 static inline bool SurfaceTransformsToScreenKnown(const RenderSurfaceImpl
* rs
) {
139 static inline bool LayerIsInUnsorted3dRenderingContext(const Layer
* layer
) {
140 return layer
->parent() && layer
->parent()->preserves_3d();
142 static inline bool LayerIsInUnsorted3dRenderingContext(const LayerImpl
* layer
) {
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()));
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
)
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();
165 const RenderSurfaceType
* new_ancestor_that_moves_pixels
=
166 new_target
->render_surface()->nearest_ancestor_that_moves_pixels();
168 stack_
.push_back(StackObject(new_target
));
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.
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
179 bool entering_subtree_that_moves_pixels
=
180 new_ancestor_that_moves_pixels
&&
181 new_ancestor_that_moves_pixels
!= old_ancestor_that_moves_pixels
;
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
);
193 bool entering_root_target
= new_target
->parent() == NULL
;
195 bool copy_outside_occlusion_forward
=
197 !entering_subtree_that_moves_pixels
&&
198 have_transform_from_screen_to_new_target
&&
199 !entering_root_target
;
200 if (!copy_outside_occlusion_forward
)
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
,
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
,
218 old_target_to_new_target_transform
));
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
);
227 RenderSurfaceType
* surface
= finished_target
->render_surface();
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
);
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 // TODO(senorblanco): Make this smarter for SkImageFilter case: once
237 // SkImageFilters can report affectsOpacity(), call that.
238 if (finished_target
->mask_layer() ||
239 !SurfaceOpacityKnown(surface
) ||
240 surface
->draw_opacity() < 1 ||
241 target_is_only_for_copy_request
||
242 finished_target
->filters().HasFilterThatAffectsOpacity() ||
243 finished_target
->filter()) {
244 stack_
.back().occlusion_from_outside_target
.Clear();
245 stack_
.back().occlusion_from_inside_target
.Clear();
246 } else if (!SurfaceTransformsToTargetKnown(surface
)) {
247 stack_
.back().occlusion_from_inside_target
.Clear();
248 stack_
.back().occlusion_from_outside_target
.Clear();
252 template <typename LayerType
>
253 static void ReduceOcclusionBelowSurface(LayerType
* contributing_layer
,
254 gfx::Rect surface_rect
,
255 const gfx::Transform
& surface_transform
,
256 LayerType
* render_target
,
257 Region
* occlusion_from_inside_target
) {
258 if (surface_rect
.IsEmpty())
261 gfx::Rect affected_area_in_target
= gfx::ToEnclosingRect(
262 MathUtil::MapClippedRect(surface_transform
, gfx::RectF(surface_rect
)));
263 if (contributing_layer
->render_surface()->is_clipped()) {
264 affected_area_in_target
.Intersect(
265 contributing_layer
->render_surface()->clip_rect());
267 if (affected_area_in_target
.IsEmpty())
270 int outset_top
, outset_right
, outset_bottom
, outset_left
;
271 contributing_layer
->background_filters().GetOutsets(
272 &outset_top
, &outset_right
, &outset_bottom
, &outset_left
);
274 // The filter can move pixels from outside of the clip, so allow affected_area
275 // to expand outside the clip.
276 affected_area_in_target
.Inset(
277 -outset_left
, -outset_top
, -outset_right
, -outset_bottom
);
279 gfx::Rect
FilterOutsetsInTarget(-outset_left
,
281 outset_left
+ outset_right
,
282 outset_top
+ outset_bottom
);
284 Region affected_occlusion
= IntersectRegions(*occlusion_from_inside_target
,
285 affected_area_in_target
);
286 Region::Iterator
affected_occlusion_rects(affected_occlusion
);
288 occlusion_from_inside_target
->Subtract(affected_area_in_target
);
289 for (; affected_occlusion_rects
.has_rect(); affected_occlusion_rects
.next()) {
290 gfx::Rect occlusion_rect
= affected_occlusion_rects
.rect();
292 // Shrink the rect by expanding the non-opaque pixels outside the rect.
294 // The left outset of the filters moves pixels on the right side of
295 // the occlusion_rect into it, shrinking its right edge.
297 occlusion_rect
.x() == affected_area_in_target
.x() ? 0 : outset_right
;
299 occlusion_rect
.y() == affected_area_in_target
.y() ? 0 : outset_bottom
;
301 occlusion_rect
.right() == affected_area_in_target
.right() ?
304 occlusion_rect
.bottom() == affected_area_in_target
.bottom() ?
307 occlusion_rect
.Inset(shrink_left
, shrink_top
, shrink_right
, shrink_bottom
);
309 occlusion_from_inside_target
->Union(occlusion_rect
);
313 template <typename LayerType
, typename RenderSurfaceType
>
314 void OcclusionTrackerBase
<LayerType
, RenderSurfaceType
>::LeaveToRenderTarget(
315 const LayerType
* new_target
) {
316 int last_index
= stack_
.size() - 1;
317 bool surface_will_be_at_top_after_pop
=
318 stack_
.size() > 1 && stack_
[last_index
- 1].target
== new_target
;
320 // We merge the screen occlusion from the current RenderSurfaceImpl subtree
321 // out to its parent target RenderSurfaceImpl. The target occlusion can be
322 // merged out as well but needs to be transformed to the new target.
324 const LayerType
* old_target
= stack_
[last_index
].target
;
325 const RenderSurfaceType
* old_surface
= old_target
->render_surface();
327 Region old_occlusion_from_inside_target_in_new_target
=
328 TransformSurfaceOpaqueRegion
<RenderSurfaceType
>(
329 stack_
[last_index
].occlusion_from_inside_target
,
330 old_surface
->is_clipped(),
331 old_surface
->clip_rect(),
332 old_surface
->draw_transform());
333 if (old_target
->has_replica() && !old_target
->replica_has_mask()) {
334 old_occlusion_from_inside_target_in_new_target
.Union(
335 TransformSurfaceOpaqueRegion
<RenderSurfaceType
>(
336 stack_
[last_index
].occlusion_from_inside_target
,
337 old_surface
->is_clipped(),
338 old_surface
->clip_rect(),
339 old_surface
->replica_draw_transform()));
342 Region old_occlusion_from_outside_target_in_new_target
=
343 TransformSurfaceOpaqueRegion
<RenderSurfaceType
>(
344 stack_
[last_index
].occlusion_from_outside_target
,
347 old_surface
->draw_transform());
349 gfx::Rect unoccluded_surface_rect
;
350 gfx::Rect unoccluded_replica_rect
;
351 if (old_target
->background_filters().HasFilterThatMovesPixels()) {
352 unoccluded_surface_rect
= UnoccludedContributingSurfaceContentRect(
353 old_target
, false, old_surface
->content_rect(), NULL
);
354 if (old_target
->has_replica()) {
355 unoccluded_replica_rect
= UnoccludedContributingSurfaceContentRect(
356 old_target
, true, old_surface
->content_rect(), NULL
);
360 if (surface_will_be_at_top_after_pop
) {
361 // Merge the top of the stack down.
362 stack_
[last_index
- 1].occlusion_from_inside_target
.Union(
363 old_occlusion_from_inside_target_in_new_target
);
364 // TODO(danakj): Strictly this should subtract the inside target occlusion
366 if (new_target
->parent()) {
367 stack_
[last_index
- 1].occlusion_from_outside_target
.Union(
368 old_occlusion_from_outside_target_in_new_target
);
372 // Replace the top of the stack with the new pushed surface.
373 stack_
.back().target
= new_target
;
374 stack_
.back().occlusion_from_inside_target
=
375 old_occlusion_from_inside_target_in_new_target
;
376 if (new_target
->parent()) {
377 stack_
.back().occlusion_from_outside_target
=
378 old_occlusion_from_outside_target_in_new_target
;
380 stack_
.back().occlusion_from_outside_target
.Clear();
384 if (!old_target
->background_filters().HasFilterThatMovesPixels())
387 ReduceOcclusionBelowSurface(old_target
,
388 unoccluded_surface_rect
,
389 old_surface
->draw_transform(),
391 &stack_
.back().occlusion_from_inside_target
);
392 ReduceOcclusionBelowSurface(old_target
,
393 unoccluded_surface_rect
,
394 old_surface
->draw_transform(),
396 &stack_
.back().occlusion_from_outside_target
);
398 if (!old_target
->has_replica())
400 ReduceOcclusionBelowSurface(old_target
,
401 unoccluded_replica_rect
,
402 old_surface
->replica_draw_transform(),
404 &stack_
.back().occlusion_from_inside_target
);
405 ReduceOcclusionBelowSurface(old_target
,
406 unoccluded_replica_rect
,
407 old_surface
->replica_draw_transform(),
409 &stack_
.back().occlusion_from_outside_target
);
412 template <typename LayerType
, typename RenderSurfaceType
>
413 void OcclusionTrackerBase
<LayerType
, RenderSurfaceType
>::
414 MarkOccludedBehindLayer(const LayerType
* layer
) {
415 DCHECK(!stack_
.empty());
416 DCHECK_EQ(layer
->render_target(), stack_
.back().target
);
420 if (!LayerOpacityKnown(layer
) || layer
->draw_opacity() < 1)
423 if (LayerIsInUnsorted3dRenderingContext(layer
))
426 if (!LayerTransformsToTargetKnown(layer
))
429 Region opaque_contents
= layer
->VisibleContentOpaqueRegion();
430 if (opaque_contents
.IsEmpty())
433 DCHECK(layer
->visible_content_rect().Contains(opaque_contents
.bounds()));
435 // TODO(danakj): Find a rect interior to each transformed quad.
436 if (!layer
->draw_transform().Preserves2dAxisAlignment())
439 gfx::Rect clip_rect_in_target
= ScreenSpaceClipRectInTargetSurface(
440 layer
->render_target()->render_surface(), screen_space_clip_rect_
);
441 if (layer
->is_clipped()) {
442 clip_rect_in_target
.Intersect(layer
->clip_rect());
444 clip_rect_in_target
.Intersect(
445 layer
->render_target()->render_surface()->content_rect());
448 for (Region::Iterator
opaque_content_rects(opaque_contents
);
449 opaque_content_rects
.has_rect();
450 opaque_content_rects
.next()) {
452 gfx::QuadF transformed_quad
= MathUtil::MapQuad(
453 layer
->draw_transform(),
454 gfx::QuadF(opaque_content_rects
.rect()),
456 gfx::Rect transformed_rect
=
457 gfx::ToEnclosedRect(transformed_quad
.BoundingBox());
458 DCHECK(!clipped
); // We only map if the transform preserves axis alignment.
459 transformed_rect
.Intersect(clip_rect_in_target
);
460 if (transformed_rect
.width() < minimum_tracking_size_
.width() &&
461 transformed_rect
.height() < minimum_tracking_size_
.height())
463 stack_
.back().occlusion_from_inside_target
.Union(transformed_rect
);
465 if (!occluding_screen_space_rects_
)
468 // Save the occluding area in screen space for debug visualization.
469 gfx::QuadF screen_space_quad
= MathUtil::MapQuad(
470 layer
->render_target()->render_surface()->screen_space_transform(),
471 gfx::QuadF(transformed_rect
), &clipped
);
472 // TODO(danakj): Store the quad in the debug info instead of the bounding
474 gfx::Rect screen_space_rect
=
475 gfx::ToEnclosedRect(screen_space_quad
.BoundingBox());
476 occluding_screen_space_rects_
->push_back(screen_space_rect
);
479 if (!non_occluding_screen_space_rects_
)
482 Region non_opaque_contents
=
483 SubtractRegions(gfx::Rect(layer
->content_bounds()), opaque_contents
);
484 for (Region::Iterator
non_opaque_content_rects(non_opaque_contents
);
485 non_opaque_content_rects
.has_rect();
486 non_opaque_content_rects
.next()) {
487 // We've already checked for clipping in the MapQuad call above, these calls
488 // should not clip anything further.
489 gfx::Rect transformed_rect
= gfx::ToEnclosedRect(
490 MathUtil::MapClippedRect(layer
->draw_transform(),
491 gfx::RectF(non_opaque_content_rects
.rect())));
492 transformed_rect
.Intersect(clip_rect_in_target
);
493 if (transformed_rect
.IsEmpty())
497 gfx::QuadF screen_space_quad
= MathUtil::MapQuad(
498 layer
->render_target()->render_surface()->screen_space_transform(),
499 gfx::QuadF(transformed_rect
),
501 // TODO(danakj): Store the quad in the debug info instead of the bounding
503 gfx::Rect screen_space_rect
=
504 gfx::ToEnclosedRect(screen_space_quad
.BoundingBox());
505 non_occluding_screen_space_rects_
->push_back(screen_space_rect
);
509 template <typename LayerType
, typename RenderSurfaceType
>
510 bool OcclusionTrackerBase
<LayerType
, RenderSurfaceType
>::Occluded(
511 const LayerType
* render_target
,
512 gfx::Rect content_rect
,
513 const gfx::Transform
& draw_transform
,
514 bool impl_draw_transform_is_unknown
,
516 gfx::Rect clip_rect_in_target
,
517 bool* has_occlusion_from_outside_target_surface
) const {
518 if (has_occlusion_from_outside_target_surface
)
519 *has_occlusion_from_outside_target_surface
= false;
520 if (prevent_occlusion_
)
523 DCHECK(!stack_
.empty());
526 if (content_rect
.IsEmpty())
528 if (impl_draw_transform_is_unknown
)
531 // For tests with no render target.
535 DCHECK_EQ(render_target
->render_target(), render_target
);
536 DCHECK(render_target
->render_surface());
537 DCHECK_EQ(render_target
, stack_
.back().target
);
539 gfx::Transform
inverse_draw_transform(gfx::Transform::kSkipInitialization
);
540 if (!draw_transform
.GetInverse(&inverse_draw_transform
))
543 // Take the ToEnclosingRect at each step, as we want to contain any unoccluded
544 // partial pixels in the resulting Rect.
545 Region unoccluded_region_in_target_surface
= gfx::ToEnclosingRect(
546 MathUtil::MapClippedRect(draw_transform
, gfx::RectF(content_rect
)));
547 // Layers can't clip across surfaces, so count this as internal occlusion.
549 unoccluded_region_in_target_surface
.Intersect(clip_rect_in_target
);
550 unoccluded_region_in_target_surface
.Subtract(
551 stack_
.back().occlusion_from_inside_target
);
552 gfx::RectF unoccluded_rect_in_target_surface_without_outside_occlusion
=
553 unoccluded_region_in_target_surface
.bounds();
554 unoccluded_region_in_target_surface
.Subtract(
555 stack_
.back().occlusion_from_outside_target
);
557 // Treat other clipping as occlusion from outside the surface.
558 // TODO(danakj): Clip to visibleContentRect?
559 unoccluded_region_in_target_surface
.Intersect(
560 render_target
->render_surface()->content_rect());
561 unoccluded_region_in_target_surface
.Intersect(
562 ScreenSpaceClipRectInTargetSurface(render_target
->render_surface(),
563 screen_space_clip_rect_
));
565 gfx::RectF unoccluded_rect_in_target_surface
=
566 unoccluded_region_in_target_surface
.bounds();
568 if (has_occlusion_from_outside_target_surface
) {
569 // Check if the unoccluded rect shrank when applying outside occlusion.
570 *has_occlusion_from_outside_target_surface
= !gfx::SubtractRects(
571 unoccluded_rect_in_target_surface_without_outside_occlusion
,
572 unoccluded_rect_in_target_surface
).IsEmpty();
575 return unoccluded_rect_in_target_surface
.IsEmpty();
578 template <typename LayerType
, typename RenderSurfaceType
>
579 gfx::Rect OcclusionTrackerBase
<LayerType
, RenderSurfaceType
>::
580 UnoccludedContentRect(
581 const LayerType
* render_target
,
582 gfx::Rect content_rect
,
583 const gfx::Transform
& draw_transform
,
584 bool impl_draw_transform_is_unknown
,
586 gfx::Rect clip_rect_in_target
,
587 bool* has_occlusion_from_outside_target_surface
) const {
588 if (has_occlusion_from_outside_target_surface
)
589 *has_occlusion_from_outside_target_surface
= false;
590 if (prevent_occlusion_
)
593 DCHECK(!stack_
.empty());
596 if (content_rect
.IsEmpty())
598 if (impl_draw_transform_is_unknown
)
601 // For tests with no render target.
605 DCHECK_EQ(render_target
->render_target(), render_target
);
606 DCHECK(render_target
->render_surface());
607 DCHECK_EQ(render_target
, stack_
.back().target
);
609 gfx::Transform
inverse_draw_transform(gfx::Transform::kSkipInitialization
);
610 if (!draw_transform
.GetInverse(&inverse_draw_transform
))
613 // Take the ToEnclosingRect at each step, as we want to contain any unoccluded
614 // partial pixels in the resulting Rect.
615 Region unoccluded_region_in_target_surface
= gfx::ToEnclosingRect(
616 MathUtil::MapClippedRect(draw_transform
, gfx::RectF(content_rect
)));
617 // Layers can't clip across surfaces, so count this as internal occlusion.
619 unoccluded_region_in_target_surface
.Intersect(clip_rect_in_target
);
620 unoccluded_region_in_target_surface
.Subtract(
621 stack_
.back().occlusion_from_inside_target
);
622 gfx::RectF unoccluded_rect_in_target_surface_without_outside_occlusion
=
623 unoccluded_region_in_target_surface
.bounds();
624 unoccluded_region_in_target_surface
.Subtract(
625 stack_
.back().occlusion_from_outside_target
);
627 // Treat other clipping as occlusion from outside the surface.
628 // TODO(danakj): Clip to visibleContentRect?
629 unoccluded_region_in_target_surface
.Intersect(
630 render_target
->render_surface()->content_rect());
631 unoccluded_region_in_target_surface
.Intersect(
632 ScreenSpaceClipRectInTargetSurface(render_target
->render_surface(),
633 screen_space_clip_rect_
));
635 gfx::RectF unoccluded_rect_in_target_surface
=
636 unoccluded_region_in_target_surface
.bounds();
637 gfx::Rect unoccluded_rect
= gfx::ToEnclosingRect(
638 MathUtil::ProjectClippedRect(inverse_draw_transform
,
639 unoccluded_rect_in_target_surface
));
640 unoccluded_rect
.Intersect(content_rect
);
642 if (has_occlusion_from_outside_target_surface
) {
643 // Check if the unoccluded rect shrank when applying outside occlusion.
644 *has_occlusion_from_outside_target_surface
= !gfx::SubtractRects(
645 unoccluded_rect_in_target_surface_without_outside_occlusion
,
646 unoccluded_rect_in_target_surface
).IsEmpty();
649 return unoccluded_rect
;
652 template <typename LayerType
, typename RenderSurfaceType
>
653 gfx::Rect OcclusionTrackerBase
<LayerType
, RenderSurfaceType
>::
654 UnoccludedContributingSurfaceContentRect(
655 const LayerType
* layer
,
657 gfx::Rect content_rect
,
658 bool* has_occlusion_from_outside_target_surface
) const {
659 DCHECK(!stack_
.empty());
660 // The layer is a contributing render_target so it should have a surface.
661 DCHECK(layer
->render_surface());
662 // The layer is a contributing render_target so its target should be itself.
663 DCHECK_EQ(layer
->render_target(), layer
);
664 // The layer should not be the root, else what is is contributing to?
665 DCHECK(layer
->parent());
666 // This should be called while the layer is still considered the current
667 // target in the occlusion tracker.
668 DCHECK_EQ(layer
, stack_
.back().target
);
670 if (has_occlusion_from_outside_target_surface
)
671 *has_occlusion_from_outside_target_surface
= false;
672 if (prevent_occlusion_
)
675 if (content_rect
.IsEmpty())
678 const RenderSurfaceType
* surface
= layer
->render_surface();
679 const LayerType
* contributing_surface_render_target
=
680 layer
->parent()->render_target();
682 if (!SurfaceTransformsToTargetKnown(surface
))
685 gfx::Transform draw_transform
=
686 for_replica
? surface
->replica_draw_transform()
687 : surface
->draw_transform();
688 gfx::Transform
inverse_draw_transform(gfx::Transform::kSkipInitialization
);
689 if (!draw_transform
.GetInverse(&inverse_draw_transform
))
692 // A contributing surface doesn't get occluded by things inside its own
693 // surface, so only things outside the surface can occlude it. That occlusion
694 // is found just below the top of the stack (if it exists).
695 bool has_occlusion
= stack_
.size() > 1;
697 // Take the ToEnclosingRect at each step, as we want to contain any unoccluded
698 // partial pixels in the resulting Rect.
699 Region unoccluded_region_in_target_surface
= gfx::ToEnclosingRect(
700 MathUtil::MapClippedRect(draw_transform
, gfx::RectF(content_rect
)));
701 // Layers can't clip across surfaces, so count this as internal occlusion.
702 if (surface
->is_clipped())
703 unoccluded_region_in_target_surface
.Intersect(surface
->clip_rect());
705 const StackObject
& second_last
= stack_
[stack_
.size() - 2];
706 unoccluded_region_in_target_surface
.Subtract(
707 second_last
.occlusion_from_inside_target
);
709 gfx::RectF unoccluded_rect_in_target_surface_without_outside_occlusion
=
710 unoccluded_region_in_target_surface
.bounds();
712 const StackObject
& second_last
= stack_
[stack_
.size() - 2];
713 unoccluded_region_in_target_surface
.Subtract(
714 second_last
.occlusion_from_outside_target
);
717 // Treat other clipping as occlusion from outside the target surface.
718 unoccluded_region_in_target_surface
.Intersect(
719 contributing_surface_render_target
->render_surface()->content_rect());
720 unoccluded_region_in_target_surface
.Intersect(
721 ScreenSpaceClipRectInTargetSurface(
722 contributing_surface_render_target
->render_surface(),
723 screen_space_clip_rect_
));
725 gfx::RectF unoccluded_rect_in_target_surface
=
726 unoccluded_region_in_target_surface
.bounds();
727 gfx::Rect unoccluded_rect
= gfx::ToEnclosingRect(
728 MathUtil::ProjectClippedRect(inverse_draw_transform
,
729 unoccluded_rect_in_target_surface
));
730 unoccluded_rect
.Intersect(content_rect
);
732 if (has_occlusion_from_outside_target_surface
) {
733 // Check if the unoccluded rect shrank when applying outside occlusion.
734 *has_occlusion_from_outside_target_surface
= !gfx::SubtractRects(
735 unoccluded_rect_in_target_surface_without_outside_occlusion
,
736 unoccluded_rect_in_target_surface
).IsEmpty();
739 return unoccluded_rect
;
742 // Instantiate (and export) templates here for the linker.
743 template class OcclusionTrackerBase
<Layer
, RenderSurface
>;
744 template class OcclusionTrackerBase
<LayerImpl
, RenderSurfaceImpl
>;