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/base/region.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/geometry/quad_f.h"
16 #include "ui/gfx/geometry/rect_conversions.h"
20 template <typename LayerType
>
21 OcclusionTracker
<LayerType
>::OcclusionTracker(
22 const gfx::Rect
& screen_space_clip_rect
)
23 : screen_space_clip_rect_(screen_space_clip_rect
) {
26 template <typename LayerType
>
27 OcclusionTracker
<LayerType
>::~OcclusionTracker() {
30 template <typename LayerType
>
31 Occlusion OcclusionTracker
<LayerType
>::GetCurrentOcclusionForLayer(
32 const gfx::Transform
& draw_transform
) const {
33 DCHECK(!stack_
.empty());
34 const StackObject
& back
= stack_
.back();
35 return Occlusion(draw_transform
,
36 back
.occlusion_from_outside_target
,
37 back
.occlusion_from_inside_target
);
40 template <typename LayerType
>
42 OcclusionTracker
<LayerType
>::GetCurrentOcclusionForContributingSurface(
43 const gfx::Transform
& draw_transform
) const {
44 DCHECK(!stack_
.empty());
45 if (stack_
.size() < 2)
47 // A contributing surface doesn't get occluded by things inside its own
48 // surface, so only things outside the surface can occlude it. That occlusion
49 // is found just below the top of the stack (if it exists).
50 const StackObject
& second_last
= stack_
[stack_
.size() - 2];
51 return Occlusion(draw_transform
, second_last
.occlusion_from_outside_target
,
52 second_last
.occlusion_from_inside_target
);
55 template <typename LayerType
>
56 void OcclusionTracker
<LayerType
>::EnterLayer(
57 const LayerIteratorPosition
<LayerType
>& layer_iterator
) {
58 LayerType
* render_target
= layer_iterator
.target_render_surface_layer
;
60 if (layer_iterator
.represents_itself
)
61 EnterRenderTarget(render_target
);
62 else if (layer_iterator
.represents_target_render_surface
)
63 FinishedRenderTarget(render_target
);
66 template <typename LayerType
>
67 void OcclusionTracker
<LayerType
>::LeaveLayer(
68 const LayerIteratorPosition
<LayerType
>& layer_iterator
) {
69 LayerType
* render_target
= layer_iterator
.target_render_surface_layer
;
71 if (layer_iterator
.represents_itself
)
72 MarkOccludedBehindLayer(layer_iterator
.current_layer
);
73 // TODO(danakj): This should be done when entering the contributing surface,
74 // but in a way that the surface's own occlusion won't occlude itself.
75 else if (layer_iterator
.represents_contributing_render_surface
)
76 LeaveToRenderTarget(render_target
);
79 template <typename RenderSurfaceType
>
80 static gfx::Rect
ScreenSpaceClipRectInTargetSurface(
81 const RenderSurfaceType
* target_surface
,
82 const gfx::Rect
& screen_space_clip_rect
) {
83 gfx::Transform
inverse_screen_space_transform(
84 gfx::Transform::kSkipInitialization
);
85 if (!target_surface
->screen_space_transform().GetInverse(
86 &inverse_screen_space_transform
))
87 return target_surface
->content_rect();
89 return MathUtil::ProjectEnclosingClippedRect(inverse_screen_space_transform
,
90 screen_space_clip_rect
);
93 template <typename RenderSurfaceType
>
94 static SimpleEnclosedRegion
TransformSurfaceOpaqueRegion(
95 const SimpleEnclosedRegion
& region
,
97 const gfx::Rect
& clip_rect_in_new_target
,
98 const gfx::Transform
& transform
) {
102 // Verify that rects within the |surface| will remain rects in its target
103 // surface after applying |transform|. If this is true, then apply |transform|
104 // to each rect within |region| in order to transform the entire Region.
106 // TODO(danakj): Find a rect interior to each transformed quad.
107 if (!transform
.Preserves2dAxisAlignment())
108 return SimpleEnclosedRegion();
110 SimpleEnclosedRegion transformed_region
;
111 for (size_t i
= 0; i
< region
.GetRegionComplexity(); ++i
) {
112 gfx::Rect transformed_rect
=
113 MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(transform
,
116 transformed_rect
.Intersect(clip_rect_in_new_target
);
117 transformed_region
.Union(transformed_rect
);
119 return transformed_region
;
122 static inline bool LayerOpacityKnown(const Layer
* layer
) {
123 return !layer
->draw_opacity_is_animating();
125 static inline bool LayerOpacityKnown(const LayerImpl
* layer
) {
128 static inline bool LayerTransformsToTargetKnown(const Layer
* layer
) {
129 return !layer
->draw_transform_is_animating();
131 static inline bool LayerTransformsToTargetKnown(const LayerImpl
* layer
) {
135 static inline bool SurfaceOpacityKnown(const RenderSurface
* rs
) {
136 return !rs
->draw_opacity_is_animating();
138 static inline bool SurfaceOpacityKnown(const RenderSurfaceImpl
* rs
) {
141 static inline bool SurfaceTransformsToTargetKnown(const RenderSurface
* rs
) {
142 return !rs
->target_surface_transforms_are_animating();
144 static inline bool SurfaceTransformsToTargetKnown(const RenderSurfaceImpl
* rs
) {
147 static inline bool SurfaceTransformsToScreenKnown(const RenderSurface
* rs
) {
148 return !rs
->screen_space_transforms_are_animating();
150 static inline bool SurfaceTransformsToScreenKnown(const RenderSurfaceImpl
* rs
) {
154 static inline bool LayerIsInUnsorted3dRenderingContext(const Layer
* layer
) {
155 return layer
->Is3dSorted();
157 static inline bool LayerIsInUnsorted3dRenderingContext(const LayerImpl
* layer
) {
158 return layer
->Is3dSorted();
161 template <typename LayerType
>
162 static inline bool LayerIsHidden(const LayerType
* layer
) {
163 return layer
->hide_layer_and_subtree() ||
164 (layer
->parent() && LayerIsHidden(layer
->parent()));
167 template <typename LayerType
>
168 void OcclusionTracker
<LayerType
>::EnterRenderTarget(
169 const LayerType
* new_target
) {
170 if (!stack_
.empty() && stack_
.back().target
== new_target
)
173 const LayerType
* old_target
= NULL
;
174 const typename
LayerType::RenderSurfaceType
* old_occlusion_immune_ancestor
=
176 if (!stack_
.empty()) {
177 old_target
= stack_
.back().target
;
178 old_occlusion_immune_ancestor
=
179 old_target
->render_surface()->nearest_occlusion_immune_ancestor();
181 const typename
LayerType::RenderSurfaceType
* new_occlusion_immune_ancestor
=
182 new_target
->render_surface()->nearest_occlusion_immune_ancestor();
184 stack_
.push_back(StackObject(new_target
));
186 // We copy the screen occlusion into the new RenderSurface subtree, but we
187 // never copy in the occlusion from inside the target, since we are looking
188 // at a new RenderSurface target.
190 // If entering an unoccluded subtree, do not carry forward the outside
191 // occlusion calculated so far.
192 bool entering_unoccluded_subtree
=
193 new_occlusion_immune_ancestor
&&
194 new_occlusion_immune_ancestor
!= old_occlusion_immune_ancestor
;
196 bool have_transform_from_screen_to_new_target
= false;
197 gfx::Transform
inverse_new_target_screen_space_transform(
198 // Note carefully, not used if screen space transform is uninvertible.
199 gfx::Transform::kSkipInitialization
);
200 if (SurfaceTransformsToScreenKnown(new_target
->render_surface())) {
201 have_transform_from_screen_to_new_target
=
202 new_target
->render_surface()->screen_space_transform().GetInverse(
203 &inverse_new_target_screen_space_transform
);
206 bool entering_root_target
= new_target
->parent() == NULL
;
208 bool copy_outside_occlusion_forward
=
210 !entering_unoccluded_subtree
&&
211 have_transform_from_screen_to_new_target
&&
212 !entering_root_target
;
213 if (!copy_outside_occlusion_forward
)
216 int last_index
= stack_
.size() - 1;
217 gfx::Transform
old_target_to_new_target_transform(
218 inverse_new_target_screen_space_transform
,
219 old_target
->render_surface()->screen_space_transform());
220 stack_
[last_index
].occlusion_from_outside_target
=
221 TransformSurfaceOpaqueRegion
<typename
LayerType::RenderSurfaceType
>(
222 stack_
[last_index
- 1].occlusion_from_outside_target
,
225 old_target_to_new_target_transform
);
226 stack_
[last_index
].occlusion_from_outside_target
.Union(
227 TransformSurfaceOpaqueRegion
<typename
LayerType::RenderSurfaceType
>(
228 stack_
[last_index
- 1].occlusion_from_inside_target
,
231 old_target_to_new_target_transform
));
234 template <typename LayerType
>
235 void OcclusionTracker
<LayerType
>::FinishedRenderTarget(
236 const LayerType
* finished_target
) {
237 // Make sure we know about the target surface.
238 EnterRenderTarget(finished_target
);
240 typename
LayerType::RenderSurfaceType
* surface
=
241 finished_target
->render_surface();
243 // Readbacks always happen on render targets so we only need to check
244 // for readbacks here.
245 bool target_is_only_for_copy_request
=
246 finished_target
->HasCopyRequest() && LayerIsHidden(finished_target
);
248 // If the occlusion within the surface can not be applied to things outside of
249 // the surface's subtree, then clear the occlusion here so it won't be used.
250 if (finished_target
->mask_layer() || !SurfaceOpacityKnown(surface
) ||
251 surface
->draw_opacity() < 1 ||
252 !finished_target
->uses_default_blend_mode() ||
253 target_is_only_for_copy_request
||
254 finished_target
->filters().HasFilterThatAffectsOpacity()) {
255 stack_
.back().occlusion_from_outside_target
.Clear();
256 stack_
.back().occlusion_from_inside_target
.Clear();
257 } else if (!SurfaceTransformsToTargetKnown(surface
)) {
258 stack_
.back().occlusion_from_inside_target
.Clear();
259 stack_
.back().occlusion_from_outside_target
.Clear();
263 template <typename LayerType
>
264 static void ReduceOcclusionBelowSurface(
265 LayerType
* contributing_layer
,
266 const gfx::Rect
& surface_rect
,
267 const gfx::Transform
& surface_transform
,
268 LayerType
* render_target
,
269 SimpleEnclosedRegion
* occlusion_from_inside_target
) {
270 if (surface_rect
.IsEmpty())
273 gfx::Rect affected_area_in_target
=
274 MathUtil::MapEnclosingClippedRect(surface_transform
, surface_rect
);
275 if (contributing_layer
->render_surface()->is_clipped()) {
276 affected_area_in_target
.Intersect(
277 contributing_layer
->render_surface()->clip_rect());
279 if (affected_area_in_target
.IsEmpty())
282 int outset_top
, outset_right
, outset_bottom
, outset_left
;
283 contributing_layer
->background_filters().GetOutsets(
284 &outset_top
, &outset_right
, &outset_bottom
, &outset_left
);
286 // The filter can move pixels from outside of the clip, so allow affected_area
287 // to expand outside the clip.
288 affected_area_in_target
.Inset(
289 -outset_left
, -outset_top
, -outset_right
, -outset_bottom
);
290 SimpleEnclosedRegion affected_occlusion
= *occlusion_from_inside_target
;
291 affected_occlusion
.Intersect(affected_area_in_target
);
293 occlusion_from_inside_target
->Subtract(affected_area_in_target
);
294 for (size_t i
= 0; i
< affected_occlusion
.GetRegionComplexity(); ++i
) {
295 gfx::Rect occlusion_rect
= affected_occlusion
.GetRect(i
);
297 // Shrink the rect by expanding the non-opaque pixels outside the rect.
299 // The left outset of the filters moves pixels on the right side of
300 // the occlusion_rect into it, shrinking its right edge.
302 occlusion_rect
.x() == affected_area_in_target
.x() ? 0 : outset_right
;
304 occlusion_rect
.y() == affected_area_in_target
.y() ? 0 : outset_bottom
;
306 occlusion_rect
.right() == affected_area_in_target
.right() ?
309 occlusion_rect
.bottom() == affected_area_in_target
.bottom() ?
312 occlusion_rect
.Inset(shrink_left
, shrink_top
, shrink_right
, shrink_bottom
);
314 occlusion_from_inside_target
->Union(occlusion_rect
);
318 template <typename LayerType
>
319 void OcclusionTracker
<LayerType
>::LeaveToRenderTarget(
320 const LayerType
* new_target
) {
321 int last_index
= stack_
.size() - 1;
322 bool surface_will_be_at_top_after_pop
=
323 stack_
.size() > 1 && stack_
[last_index
- 1].target
== new_target
;
325 // We merge the screen occlusion from the current RenderSurfaceImpl subtree
326 // out to its parent target RenderSurfaceImpl. The target occlusion can be
327 // merged out as well but needs to be transformed to the new target.
329 const LayerType
* old_target
= stack_
[last_index
].target
;
330 const typename
LayerType::RenderSurfaceType
* old_surface
=
331 old_target
->render_surface();
333 SimpleEnclosedRegion old_occlusion_from_inside_target_in_new_target
=
334 TransformSurfaceOpaqueRegion
<typename
LayerType::RenderSurfaceType
>(
335 stack_
[last_index
].occlusion_from_inside_target
,
336 old_surface
->is_clipped(),
337 old_surface
->clip_rect(),
338 old_surface
->draw_transform());
339 if (old_target
->has_replica() && !old_target
->replica_has_mask()) {
340 old_occlusion_from_inside_target_in_new_target
.Union(
341 TransformSurfaceOpaqueRegion
<typename
LayerType::RenderSurfaceType
>(
342 stack_
[last_index
].occlusion_from_inside_target
,
343 old_surface
->is_clipped(),
344 old_surface
->clip_rect(),
345 old_surface
->replica_draw_transform()));
348 SimpleEnclosedRegion old_occlusion_from_outside_target_in_new_target
=
349 TransformSurfaceOpaqueRegion
<typename
LayerType::RenderSurfaceType
>(
350 stack_
[last_index
].occlusion_from_outside_target
,
353 old_surface
->draw_transform());
355 gfx::Rect unoccluded_surface_rect
;
356 gfx::Rect unoccluded_replica_rect
;
357 if (old_target
->background_filters().HasFilterThatMovesPixels()) {
358 Occlusion surface_occlusion
= GetCurrentOcclusionForContributingSurface(
359 old_surface
->draw_transform());
360 unoccluded_surface_rect
=
361 surface_occlusion
.GetUnoccludedContentRect(old_surface
->content_rect());
362 if (old_target
->has_replica()) {
363 Occlusion replica_occlusion
= GetCurrentOcclusionForContributingSurface(
364 old_surface
->replica_draw_transform());
365 unoccluded_replica_rect
= replica_occlusion
.GetUnoccludedContentRect(
366 old_surface
->content_rect());
370 if (surface_will_be_at_top_after_pop
) {
371 // Merge the top of the stack down.
372 stack_
[last_index
- 1].occlusion_from_inside_target
.Union(
373 old_occlusion_from_inside_target_in_new_target
);
374 // TODO(danakj): Strictly this should subtract the inside target occlusion
376 if (new_target
->parent()) {
377 stack_
[last_index
- 1].occlusion_from_outside_target
.Union(
378 old_occlusion_from_outside_target_in_new_target
);
382 // Replace the top of the stack with the new pushed surface.
383 stack_
.back().target
= new_target
;
384 stack_
.back().occlusion_from_inside_target
=
385 old_occlusion_from_inside_target_in_new_target
;
386 if (new_target
->parent()) {
387 stack_
.back().occlusion_from_outside_target
=
388 old_occlusion_from_outside_target_in_new_target
;
390 stack_
.back().occlusion_from_outside_target
.Clear();
394 if (!old_target
->background_filters().HasFilterThatMovesPixels())
397 ReduceOcclusionBelowSurface(old_target
,
398 unoccluded_surface_rect
,
399 old_surface
->draw_transform(),
401 &stack_
.back().occlusion_from_inside_target
);
402 ReduceOcclusionBelowSurface(old_target
,
403 unoccluded_surface_rect
,
404 old_surface
->draw_transform(),
406 &stack_
.back().occlusion_from_outside_target
);
408 if (!old_target
->has_replica())
410 ReduceOcclusionBelowSurface(old_target
,
411 unoccluded_replica_rect
,
412 old_surface
->replica_draw_transform(),
414 &stack_
.back().occlusion_from_inside_target
);
415 ReduceOcclusionBelowSurface(old_target
,
416 unoccluded_replica_rect
,
417 old_surface
->replica_draw_transform(),
419 &stack_
.back().occlusion_from_outside_target
);
422 template <typename LayerType
>
423 void OcclusionTracker
<LayerType
>::MarkOccludedBehindLayer(
424 const LayerType
* layer
) {
425 DCHECK(!stack_
.empty());
426 DCHECK_EQ(layer
->render_target(), stack_
.back().target
);
428 if (!LayerOpacityKnown(layer
) || layer
->draw_opacity() < 1)
431 if (!layer
->uses_default_blend_mode())
434 if (LayerIsInUnsorted3dRenderingContext(layer
))
437 if (!LayerTransformsToTargetKnown(layer
))
440 SimpleEnclosedRegion opaque_contents
= layer
->VisibleContentOpaqueRegion();
441 if (opaque_contents
.IsEmpty())
444 DCHECK(layer
->visible_content_rect().Contains(opaque_contents
.bounds()));
446 // TODO(danakj): Find a rect interior to each transformed quad.
447 if (!layer
->draw_transform().Preserves2dAxisAlignment())
450 gfx::Rect clip_rect_in_target
= ScreenSpaceClipRectInTargetSurface(
451 layer
->render_target()->render_surface(), screen_space_clip_rect_
);
452 if (layer
->is_clipped()) {
453 clip_rect_in_target
.Intersect(layer
->clip_rect());
455 clip_rect_in_target
.Intersect(
456 layer
->render_target()->render_surface()->content_rect());
459 for (size_t i
= 0; i
< opaque_contents
.GetRegionComplexity(); ++i
) {
460 gfx::Rect transformed_rect
=
461 MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(
462 layer
->draw_transform(), opaque_contents
.GetRect(i
));
463 transformed_rect
.Intersect(clip_rect_in_target
);
464 if (transformed_rect
.width() < minimum_tracking_size_
.width() &&
465 transformed_rect
.height() < minimum_tracking_size_
.height())
467 stack_
.back().occlusion_from_inside_target
.Union(transformed_rect
);
471 template <typename LayerType
>
472 Region OcclusionTracker
<LayerType
>::ComputeVisibleRegionInScreen() const {
473 DCHECK(!stack_
.back().target
->parent());
474 const SimpleEnclosedRegion
& occluded
=
475 stack_
.back().occlusion_from_inside_target
;
476 Region
visible_region(screen_space_clip_rect_
);
477 for (size_t i
= 0; i
< occluded
.GetRegionComplexity(); ++i
)
478 visible_region
.Subtract(occluded
.GetRect(i
));
479 return visible_region
;
482 // Instantiate (and export) templates here for the linker.
483 template class OcclusionTracker
<Layer
>;
484 template class OcclusionTracker
<LayerImpl
>;