Move prefs::kLastPolicyStatisticsUpdate to the policy component.
[chromium-blink-merge.git] / cc / trees / occlusion_tracker.cc
blob9fc26da566700ce9b46cb88974730ba722a6b155
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"
7 #include <algorithm>
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"
18 namespace cc {
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,
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();
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())
88 return Region();
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);
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) {
111 return true;
113 static inline bool LayerTransformsToTargetKnown(const Layer* layer) {
114 return !layer->draw_transform_is_animating();
116 static inline bool LayerTransformsToTargetKnown(const LayerImpl* layer) {
117 return true;
120 static inline bool SurfaceOpacityKnown(const RenderSurface* rs) {
121 return !rs->draw_opacity_is_animating();
123 static inline bool SurfaceOpacityKnown(const RenderSurfaceImpl* rs) {
124 return true;
126 static inline bool SurfaceTransformsToTargetKnown(const RenderSurface* rs) {
127 return !rs->target_surface_transforms_are_animating();
129 static inline bool SurfaceTransformsToTargetKnown(const RenderSurfaceImpl* rs) {
130 return true;
132 static inline bool SurfaceTransformsToScreenKnown(const RenderSurface* rs) {
133 return !rs->screen_space_transforms_are_animating();
135 static inline bool SurfaceTransformsToScreenKnown(const RenderSurfaceImpl* rs) {
136 return true;
139 static inline bool LayerIsInUnsorted3dRenderingContext(const Layer* layer) {
140 return layer->parent() && layer->parent()->preserves_3d();
142 static inline bool LayerIsInUnsorted3dRenderingContext(const LayerImpl* layer) {
143 return false;
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)
156 return;
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
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;
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 =
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;
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));
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())
259 return;
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())
268 return;
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,
280 -outset_top,
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.
296 int shrink_left =
297 occlusion_rect.x() == affected_area_in_target.x() ? 0 : outset_right;
298 int shrink_top =
299 occlusion_rect.y() == affected_area_in_target.y() ? 0 : outset_bottom;
300 int shrink_right =
301 occlusion_rect.right() == affected_area_in_target.right() ?
302 0 : outset_left;
303 int shrink_bottom =
304 occlusion_rect.bottom() == affected_area_in_target.bottom() ?
305 0 : outset_top;
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,
345 false,
346 gfx::Rect(),
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
365 // before union.
366 if (new_target->parent()) {
367 stack_[last_index - 1].occlusion_from_outside_target.Union(
368 old_occlusion_from_outside_target_in_new_target);
370 stack_.pop_back();
371 } else {
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;
379 } else {
380 stack_.back().occlusion_from_outside_target.Clear();
384 if (!old_target->background_filters().HasFilterThatMovesPixels())
385 return;
387 ReduceOcclusionBelowSurface(old_target,
388 unoccluded_surface_rect,
389 old_surface->draw_transform(),
390 new_target,
391 &stack_.back().occlusion_from_inside_target);
392 ReduceOcclusionBelowSurface(old_target,
393 unoccluded_surface_rect,
394 old_surface->draw_transform(),
395 new_target,
396 &stack_.back().occlusion_from_outside_target);
398 if (!old_target->has_replica())
399 return;
400 ReduceOcclusionBelowSurface(old_target,
401 unoccluded_replica_rect,
402 old_surface->replica_draw_transform(),
403 new_target,
404 &stack_.back().occlusion_from_inside_target);
405 ReduceOcclusionBelowSurface(old_target,
406 unoccluded_replica_rect,
407 old_surface->replica_draw_transform(),
408 new_target,
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);
417 if (stack_.empty())
418 return;
420 if (!LayerOpacityKnown(layer) || layer->draw_opacity() < 1)
421 return;
423 if (LayerIsInUnsorted3dRenderingContext(layer))
424 return;
426 if (!LayerTransformsToTargetKnown(layer))
427 return;
429 Region opaque_contents = layer->VisibleContentOpaqueRegion();
430 if (opaque_contents.IsEmpty())
431 return;
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())
437 return;
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());
443 } else {
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()) {
451 bool clipped;
452 gfx::QuadF transformed_quad = MathUtil::MapQuad(
453 layer->draw_transform(),
454 gfx::QuadF(opaque_content_rects.rect()),
455 &clipped);
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())
462 continue;
463 stack_.back().occlusion_from_inside_target.Union(transformed_rect);
465 if (!occluding_screen_space_rects_)
466 continue;
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
473 // box.
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_)
480 return;
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())
494 continue;
496 bool clipped;
497 gfx::QuadF screen_space_quad = MathUtil::MapQuad(
498 layer->render_target()->render_surface()->screen_space_transform(),
499 gfx::QuadF(transformed_rect),
500 &clipped);
501 // TODO(danakj): Store the quad in the debug info instead of the bounding
502 // box.
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,
515 bool is_clipped,
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_)
521 return false;
523 DCHECK(!stack_.empty());
524 if (stack_.empty())
525 return false;
526 if (content_rect.IsEmpty())
527 return true;
528 if (impl_draw_transform_is_unknown)
529 return false;
531 // For tests with no render target.
532 if (!render_target)
533 return false;
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))
541 return false;
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.
548 if (is_clipped)
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,
585 bool is_clipped,
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_)
591 return content_rect;
593 DCHECK(!stack_.empty());
594 if (stack_.empty())
595 return content_rect;
596 if (content_rect.IsEmpty())
597 return content_rect;
598 if (impl_draw_transform_is_unknown)
599 return content_rect;
601 // For tests with no render target.
602 if (!render_target)
603 return content_rect;
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))
611 return content_rect;
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.
618 if (is_clipped)
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,
656 bool for_replica,
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_)
673 return content_rect;
675 if (content_rect.IsEmpty())
676 return content_rect;
678 const RenderSurfaceType* surface = layer->render_surface();
679 const LayerType* contributing_surface_render_target =
680 layer->parent()->render_target();
682 if (!SurfaceTransformsToTargetKnown(surface))
683 return content_rect;
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))
690 return content_rect;
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());
704 if (has_occlusion) {
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();
711 if (has_occlusion) {
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>;
746 } // namespace cc