[safe browsing] Push missing download BINHASH histograms.
[chromium-blink-merge.git] / cc / trees / occlusion_tracker.cc
blob11982a8e6ed7959e93811cf15331ce59c1e8a480
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 const 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,
58 const gfx::Rect& screen_space_clip_rect) {
59 gfx::Transform inverse_screen_space_transform(
60 gfx::Transform::kSkipInitialization);
61 if (!target_surface->screen_space_transform().GetInverse(
62 &inverse_screen_space_transform))
63 return target_surface->content_rect();
65 return MathUtil::ProjectEnclosingClippedRect(inverse_screen_space_transform,
66 screen_space_clip_rect);
69 template <typename RenderSurfaceType>
70 static Region TransformSurfaceOpaqueRegion(
71 const Region& region,
72 bool have_clip_rect,
73 const gfx::Rect& clip_rect_in_new_target,
74 const gfx::Transform& transform) {
75 if (region.IsEmpty())
76 return Region();
78 // Verify that rects within the |surface| will remain rects in its target
79 // surface after applying |transform|. If this is true, then apply |transform|
80 // to each rect within |region| in order to transform the entire Region.
82 // TODO(danakj): Find a rect interior to each transformed quad.
83 if (!transform.Preserves2dAxisAlignment())
84 return Region();
86 // TODO(danakj): If the Region is too complex, degrade gracefully here by
87 // skipping rects in it.
88 Region transformed_region;
89 for (Region::Iterator rects(region); rects.has_rect(); rects.next()) {
90 bool clipped;
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.
96 if (have_clip_rect)
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) {
107 return true;
109 static inline bool LayerTransformsToTargetKnown(const Layer* layer) {
110 return !layer->draw_transform_is_animating();
112 static inline bool LayerTransformsToTargetKnown(const LayerImpl* layer) {
113 return true;
116 static inline bool SurfaceOpacityKnown(const RenderSurface* rs) {
117 return !rs->draw_opacity_is_animating();
119 static inline bool SurfaceOpacityKnown(const RenderSurfaceImpl* rs) {
120 return true;
122 static inline bool SurfaceTransformsToTargetKnown(const RenderSurface* rs) {
123 return !rs->target_surface_transforms_are_animating();
125 static inline bool SurfaceTransformsToTargetKnown(const RenderSurfaceImpl* rs) {
126 return true;
128 static inline bool SurfaceTransformsToScreenKnown(const RenderSurface* rs) {
129 return !rs->screen_space_transforms_are_animating();
131 static inline bool SurfaceTransformsToScreenKnown(const RenderSurfaceImpl* rs) {
132 return true;
135 static inline bool LayerIsInUnsorted3dRenderingContext(const Layer* layer) {
136 return layer->is_3d_sorted();
138 static inline bool LayerIsInUnsorted3dRenderingContext(const LayerImpl* layer) {
139 return false;
142 template <typename LayerType>
143 static inline bool LayerIsHidden(const LayerType* layer) {
144 return layer->hide_layer_and_subtree() ||
145 (layer->parent() && LayerIsHidden(layer->parent()));
148 template <typename LayerType, typename RenderSurfaceType>
149 void OcclusionTrackerBase<LayerType, RenderSurfaceType>::EnterRenderTarget(
150 const LayerType* new_target) {
151 if (!stack_.empty() && stack_.back().target == new_target)
152 return;
154 const LayerType* old_target = NULL;
155 const RenderSurfaceType* old_occlusion_immune_ancestor = NULL;
156 if (!stack_.empty()) {
157 old_target = stack_.back().target;
158 old_occlusion_immune_ancestor =
159 old_target->render_surface()->nearest_occlusion_immune_ancestor();
161 const RenderSurfaceType* new_occlusion_immune_ancestor =
162 new_target->render_surface()->nearest_occlusion_immune_ancestor();
164 stack_.push_back(StackObject(new_target));
166 // We copy the screen occlusion into the new RenderSurface subtree, but we
167 // never copy in the occlusion from inside the target, since we are looking
168 // at a new RenderSurface target.
170 // If entering an unoccluded subtree, do not carry forward the outside
171 // occlusion calculated so far.
172 bool entering_unoccluded_subtree =
173 new_occlusion_immune_ancestor &&
174 new_occlusion_immune_ancestor != old_occlusion_immune_ancestor;
176 bool have_transform_from_screen_to_new_target = false;
177 gfx::Transform inverse_new_target_screen_space_transform(
178 // Note carefully, not used if screen space transform is uninvertible.
179 gfx::Transform::kSkipInitialization);
180 if (SurfaceTransformsToScreenKnown(new_target->render_surface())) {
181 have_transform_from_screen_to_new_target =
182 new_target->render_surface()->screen_space_transform().GetInverse(
183 &inverse_new_target_screen_space_transform);
186 bool entering_root_target = new_target->parent() == NULL;
188 bool copy_outside_occlusion_forward =
189 stack_.size() > 1 &&
190 !entering_unoccluded_subtree &&
191 have_transform_from_screen_to_new_target &&
192 !entering_root_target;
193 if (!copy_outside_occlusion_forward)
194 return;
196 int last_index = stack_.size() - 1;
197 gfx::Transform old_target_to_new_target_transform(
198 inverse_new_target_screen_space_transform,
199 old_target->render_surface()->screen_space_transform());
200 stack_[last_index].occlusion_from_outside_target =
201 TransformSurfaceOpaqueRegion<RenderSurfaceType>(
202 stack_[last_index - 1].occlusion_from_outside_target,
203 false,
204 gfx::Rect(),
205 old_target_to_new_target_transform);
206 stack_[last_index].occlusion_from_outside_target.Union(
207 TransformSurfaceOpaqueRegion<RenderSurfaceType>(
208 stack_[last_index - 1].occlusion_from_inside_target,
209 false,
210 gfx::Rect(),
211 old_target_to_new_target_transform));
214 template <typename LayerType, typename RenderSurfaceType>
215 void OcclusionTrackerBase<LayerType, RenderSurfaceType>::FinishedRenderTarget(
216 const LayerType* finished_target) {
217 // Make sure we know about the target surface.
218 EnterRenderTarget(finished_target);
220 RenderSurfaceType* surface = finished_target->render_surface();
222 // Readbacks always happen on render targets so we only need to check
223 // for readbacks here.
224 bool target_is_only_for_copy_request =
225 finished_target->HasCopyRequest() && LayerIsHidden(finished_target);
227 // If the occlusion within the surface can not be applied to things outside of
228 // the surface's subtree, then clear the occlusion here so it won't be used.
229 if (finished_target->mask_layer() || !SurfaceOpacityKnown(surface) ||
230 surface->draw_opacity() < 1 ||
231 !finished_target->uses_default_blend_mode() ||
232 target_is_only_for_copy_request ||
233 finished_target->filters().HasFilterThatAffectsOpacity()) {
234 stack_.back().occlusion_from_outside_target.Clear();
235 stack_.back().occlusion_from_inside_target.Clear();
236 } else if (!SurfaceTransformsToTargetKnown(surface)) {
237 stack_.back().occlusion_from_inside_target.Clear();
238 stack_.back().occlusion_from_outside_target.Clear();
242 template <typename LayerType>
243 static void ReduceOcclusionBelowSurface(LayerType* contributing_layer,
244 const gfx::Rect& surface_rect,
245 const gfx::Transform& surface_transform,
246 LayerType* render_target,
247 Region* occlusion_from_inside_target) {
248 if (surface_rect.IsEmpty())
249 return;
251 gfx::Rect affected_area_in_target =
252 MathUtil::MapEnclosingClippedRect(surface_transform, surface_rect);
253 if (contributing_layer->render_surface()->is_clipped()) {
254 affected_area_in_target.Intersect(
255 contributing_layer->render_surface()->clip_rect());
257 if (affected_area_in_target.IsEmpty())
258 return;
260 int outset_top, outset_right, outset_bottom, outset_left;
261 contributing_layer->background_filters().GetOutsets(
262 &outset_top, &outset_right, &outset_bottom, &outset_left);
264 // The filter can move pixels from outside of the clip, so allow affected_area
265 // to expand outside the clip.
266 affected_area_in_target.Inset(
267 -outset_left, -outset_top, -outset_right, -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.
280 int shrink_left =
281 occlusion_rect.x() == affected_area_in_target.x() ? 0 : outset_right;
282 int shrink_top =
283 occlusion_rect.y() == affected_area_in_target.y() ? 0 : outset_bottom;
284 int shrink_right =
285 occlusion_rect.right() == affected_area_in_target.right() ?
286 0 : outset_left;
287 int shrink_bottom =
288 occlusion_rect.bottom() == affected_area_in_target.bottom() ?
289 0 : outset_top;
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,
329 false,
330 gfx::Rect(),
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());
338 if (old_target->has_replica()) {
339 unoccluded_replica_rect = UnoccludedContributingSurfaceContentRect(
340 old_target, true, old_surface->content_rect());
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
349 // before union.
350 if (new_target->parent()) {
351 stack_[last_index - 1].occlusion_from_outside_target.Union(
352 old_occlusion_from_outside_target_in_new_target);
354 stack_.pop_back();
355 } else {
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;
363 } else {
364 stack_.back().occlusion_from_outside_target.Clear();
368 if (!old_target->background_filters().HasFilterThatMovesPixels())
369 return;
371 ReduceOcclusionBelowSurface(old_target,
372 unoccluded_surface_rect,
373 old_surface->draw_transform(),
374 new_target,
375 &stack_.back().occlusion_from_inside_target);
376 ReduceOcclusionBelowSurface(old_target,
377 unoccluded_surface_rect,
378 old_surface->draw_transform(),
379 new_target,
380 &stack_.back().occlusion_from_outside_target);
382 if (!old_target->has_replica())
383 return;
384 ReduceOcclusionBelowSurface(old_target,
385 unoccluded_replica_rect,
386 old_surface->replica_draw_transform(),
387 new_target,
388 &stack_.back().occlusion_from_inside_target);
389 ReduceOcclusionBelowSurface(old_target,
390 unoccluded_replica_rect,
391 old_surface->replica_draw_transform(),
392 new_target,
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);
401 if (stack_.empty())
402 return;
404 if (!layer->DrawsContent())
405 return;
407 if (!LayerOpacityKnown(layer) || layer->draw_opacity() < 1)
408 return;
410 if (!layer->uses_default_blend_mode())
411 return;
413 if (LayerIsInUnsorted3dRenderingContext(layer))
414 return;
416 if (!LayerTransformsToTargetKnown(layer))
417 return;
419 Region opaque_contents = layer->VisibleContentOpaqueRegion();
420 if (opaque_contents.IsEmpty())
421 return;
423 DCHECK(layer->visible_content_rect().Contains(opaque_contents.bounds()));
425 // TODO(danakj): Find a rect interior to each transformed quad.
426 if (!layer->draw_transform().Preserves2dAxisAlignment())
427 return;
429 gfx::Rect clip_rect_in_target = ScreenSpaceClipRectInTargetSurface(
430 layer->render_target()->render_surface(), screen_space_clip_rect_);
431 if (layer->is_clipped()) {
432 clip_rect_in_target.Intersect(layer->clip_rect());
433 } else {
434 clip_rect_in_target.Intersect(
435 layer->render_target()->render_surface()->content_rect());
438 for (Region::Iterator opaque_content_rects(opaque_contents);
439 opaque_content_rects.has_rect();
440 opaque_content_rects.next()) {
441 bool clipped;
442 gfx::QuadF transformed_quad = MathUtil::MapQuad(
443 layer->draw_transform(),
444 gfx::QuadF(opaque_content_rects.rect()),
445 &clipped);
446 gfx::Rect transformed_rect =
447 gfx::ToEnclosedRect(transformed_quad.BoundingBox());
448 DCHECK(!clipped); // We only map if the transform preserves axis alignment.
449 transformed_rect.Intersect(clip_rect_in_target);
450 if (transformed_rect.width() < minimum_tracking_size_.width() &&
451 transformed_rect.height() < minimum_tracking_size_.height())
452 continue;
453 stack_.back().occlusion_from_inside_target.Union(transformed_rect);
455 if (!occluding_screen_space_rects_)
456 continue;
458 // Save the occluding area in screen space for debug visualization.
459 gfx::QuadF screen_space_quad = MathUtil::MapQuad(
460 layer->render_target()->render_surface()->screen_space_transform(),
461 gfx::QuadF(transformed_rect), &clipped);
462 // TODO(danakj): Store the quad in the debug info instead of the bounding
463 // box.
464 gfx::Rect screen_space_rect =
465 gfx::ToEnclosedRect(screen_space_quad.BoundingBox());
466 occluding_screen_space_rects_->push_back(screen_space_rect);
469 if (!non_occluding_screen_space_rects_)
470 return;
472 Region non_opaque_contents =
473 SubtractRegions(gfx::Rect(layer->content_bounds()), opaque_contents);
474 for (Region::Iterator non_opaque_content_rects(non_opaque_contents);
475 non_opaque_content_rects.has_rect();
476 non_opaque_content_rects.next()) {
477 // We've already checked for clipping in the MapQuad call above, these calls
478 // should not clip anything further.
479 gfx::Rect transformed_rect = gfx::ToEnclosedRect(
480 MathUtil::MapClippedRect(layer->draw_transform(),
481 gfx::RectF(non_opaque_content_rects.rect())));
482 transformed_rect.Intersect(clip_rect_in_target);
483 if (transformed_rect.IsEmpty())
484 continue;
486 bool clipped;
487 gfx::QuadF screen_space_quad = MathUtil::MapQuad(
488 layer->render_target()->render_surface()->screen_space_transform(),
489 gfx::QuadF(transformed_rect),
490 &clipped);
491 // TODO(danakj): Store the quad in the debug info instead of the bounding
492 // box.
493 gfx::Rect screen_space_rect =
494 gfx::ToEnclosedRect(screen_space_quad.BoundingBox());
495 non_occluding_screen_space_rects_->push_back(screen_space_rect);
499 template <typename LayerType, typename RenderSurfaceType>
500 bool OcclusionTrackerBase<LayerType, RenderSurfaceType>::Occluded(
501 const LayerType* render_target,
502 const gfx::Rect& content_rect,
503 const gfx::Transform& draw_transform,
504 bool impl_draw_transform_is_unknown) const {
505 DCHECK(!stack_.empty());
506 if (stack_.empty())
507 return false;
508 if (content_rect.IsEmpty())
509 return true;
510 if (impl_draw_transform_is_unknown)
511 return false;
513 // For tests with no render target.
514 if (!render_target)
515 return false;
517 DCHECK_EQ(render_target->render_target(), render_target);
518 DCHECK(render_target->render_surface());
519 DCHECK_EQ(render_target, stack_.back().target);
521 if (stack_.back().occlusion_from_inside_target.IsEmpty() &&
522 stack_.back().occlusion_from_outside_target.IsEmpty()) {
523 return false;
526 gfx::Transform inverse_draw_transform(gfx::Transform::kSkipInitialization);
527 if (!draw_transform.GetInverse(&inverse_draw_transform))
528 return false;
530 // Take the ToEnclosingRect at each step, as we want to contain any unoccluded
531 // partial pixels in the resulting Rect.
532 Region unoccluded_region_in_target_surface =
533 MathUtil::MapEnclosingClippedRect(draw_transform, content_rect);
534 unoccluded_region_in_target_surface.Subtract(
535 stack_.back().occlusion_from_inside_target);
536 gfx::RectF unoccluded_rect_in_target_surface_without_outside_occlusion =
537 unoccluded_region_in_target_surface.bounds();
538 unoccluded_region_in_target_surface.Subtract(
539 stack_.back().occlusion_from_outside_target);
541 gfx::RectF unoccluded_rect_in_target_surface =
542 unoccluded_region_in_target_surface.bounds();
544 return unoccluded_rect_in_target_surface.IsEmpty();
547 template <typename LayerType, typename RenderSurfaceType>
548 gfx::Rect OcclusionTrackerBase<LayerType, RenderSurfaceType>::
549 UnoccludedContentRect(
550 const LayerType* render_target,
551 const gfx::Rect& content_rect,
552 const gfx::Transform& draw_transform,
553 bool impl_draw_transform_is_unknown) const {
554 DCHECK(!stack_.empty());
555 if (stack_.empty())
556 return content_rect;
557 if (content_rect.IsEmpty())
558 return content_rect;
559 if (impl_draw_transform_is_unknown)
560 return content_rect;
562 // For tests with no render target.
563 if (!render_target)
564 return content_rect;
566 DCHECK_EQ(render_target->render_target(), render_target);
567 DCHECK(render_target->render_surface());
568 DCHECK_EQ(render_target, stack_.back().target);
570 if (stack_.back().occlusion_from_inside_target.IsEmpty() &&
571 stack_.back().occlusion_from_outside_target.IsEmpty()) {
572 return content_rect;
575 gfx::Transform inverse_draw_transform(gfx::Transform::kSkipInitialization);
576 if (!draw_transform.GetInverse(&inverse_draw_transform))
577 return content_rect;
579 // Take the ToEnclosingRect at each step, as we want to contain any unoccluded
580 // partial pixels in the resulting Rect.
581 Region unoccluded_region_in_target_surface =
582 MathUtil::MapEnclosingClippedRect(draw_transform, content_rect);
583 unoccluded_region_in_target_surface.Subtract(
584 stack_.back().occlusion_from_inside_target);
585 unoccluded_region_in_target_surface.Subtract(
586 stack_.back().occlusion_from_outside_target);
588 gfx::Rect unoccluded_rect_in_target_surface =
589 unoccluded_region_in_target_surface.bounds();
590 gfx::Rect unoccluded_rect = MathUtil::ProjectEnclosingClippedRect(
591 inverse_draw_transform, unoccluded_rect_in_target_surface);
592 unoccluded_rect.Intersect(content_rect);
594 return unoccluded_rect;
597 template <typename LayerType, typename RenderSurfaceType>
598 gfx::Rect OcclusionTrackerBase<LayerType, RenderSurfaceType>::
599 UnoccludedContributingSurfaceContentRect(
600 const LayerType* layer,
601 bool for_replica,
602 const gfx::Rect& content_rect) const {
603 DCHECK(!stack_.empty());
604 // The layer is a contributing render_target so it should have a surface.
605 DCHECK(layer->render_surface());
606 // The layer is a contributing render_target so its target should be itself.
607 DCHECK_EQ(layer->render_target(), layer);
608 // The layer should not be the root, else what is is contributing to?
609 DCHECK(layer->parent());
610 // This should be called while the layer is still considered the current
611 // target in the occlusion tracker.
612 DCHECK_EQ(layer, stack_.back().target);
614 if (content_rect.IsEmpty())
615 return content_rect;
617 const RenderSurfaceType* surface = layer->render_surface();
618 const LayerType* contributing_surface_render_target =
619 layer->parent()->render_target();
621 if (!SurfaceTransformsToTargetKnown(surface))
622 return content_rect;
624 gfx::Transform draw_transform =
625 for_replica ? surface->replica_draw_transform()
626 : surface->draw_transform();
627 gfx::Transform inverse_draw_transform(gfx::Transform::kSkipInitialization);
628 if (!draw_transform.GetInverse(&inverse_draw_transform))
629 return content_rect;
631 // A contributing surface doesn't get occluded by things inside its own
632 // surface, so only things outside the surface can occlude it. That occlusion
633 // is found just below the top of the stack (if it exists).
634 bool has_occlusion = stack_.size() > 1;
636 // Take the ToEnclosingRect at each step, as we want to contain any unoccluded
637 // partial pixels in the resulting Rect.
638 Region unoccluded_region_in_target_surface =
639 MathUtil::MapEnclosingClippedRect(draw_transform, content_rect);
640 // Layers can't clip across surfaces, so count this as internal occlusion.
641 if (surface->is_clipped())
642 unoccluded_region_in_target_surface.Intersect(surface->clip_rect());
643 if (has_occlusion) {
644 const StackObject& second_last = stack_[stack_.size() - 2];
645 unoccluded_region_in_target_surface.Subtract(
646 second_last.occlusion_from_inside_target);
647 unoccluded_region_in_target_surface.Subtract(
648 second_last.occlusion_from_outside_target);
651 // Treat other clipping as occlusion from outside the target surface.
652 unoccluded_region_in_target_surface.Intersect(
653 contributing_surface_render_target->render_surface()->content_rect());
654 unoccluded_region_in_target_surface.Intersect(
655 ScreenSpaceClipRectInTargetSurface(
656 contributing_surface_render_target->render_surface(),
657 screen_space_clip_rect_));
659 gfx::Rect unoccluded_rect_in_target_surface =
660 unoccluded_region_in_target_surface.bounds();
661 gfx::Rect unoccluded_rect = MathUtil::ProjectEnclosingClippedRect(
662 inverse_draw_transform, unoccluded_rect_in_target_surface);
663 unoccluded_rect.Intersect(content_rect);
665 return unoccluded_rect;
668 // Instantiate (and export) templates here for the linker.
669 template class OcclusionTrackerBase<Layer, RenderSurface>;
670 template class OcclusionTrackerBase<LayerImpl, RenderSurfaceImpl>;
672 } // namespace cc