Adding Peter Thatcher to the owners file.
[chromium-blink-merge.git] / cc / trees / occlusion_tracker.cc
blob0f0ce158400add99977bb525b0a1b334d0f782a9
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/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"
18 namespace cc {
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>
41 Occlusion
42 OcclusionTracker<LayerType>::GetCurrentOcclusionForContributingSurface(
43 const gfx::Transform& draw_transform) const {
44 DCHECK(!stack_.empty());
45 if (stack_.size() < 2)
46 return Occlusion();
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,
96 bool have_clip_rect,
97 const gfx::Rect& clip_rect_in_new_target,
98 const gfx::Transform& transform) {
99 if (region.IsEmpty())
100 return region;
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,
114 region.GetRect(i));
115 if (have_clip_rect)
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) {
126 return true;
128 static inline bool LayerTransformsToTargetKnown(const Layer* layer) {
129 return !layer->draw_transform_is_animating();
131 static inline bool LayerTransformsToTargetKnown(const LayerImpl* layer) {
132 return true;
135 static inline bool SurfaceOpacityKnown(const RenderSurface* rs) {
136 return !rs->draw_opacity_is_animating();
138 static inline bool SurfaceOpacityKnown(const RenderSurfaceImpl* rs) {
139 return true;
141 static inline bool SurfaceTransformsToTargetKnown(const RenderSurface* rs) {
142 return !rs->target_surface_transforms_are_animating();
144 static inline bool SurfaceTransformsToTargetKnown(const RenderSurfaceImpl* rs) {
145 return true;
147 static inline bool SurfaceTransformsToScreenKnown(const RenderSurface* rs) {
148 return !rs->screen_space_transforms_are_animating();
150 static inline bool SurfaceTransformsToScreenKnown(const RenderSurfaceImpl* rs) {
151 return true;
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)
171 return;
173 const LayerType* old_target = NULL;
174 const typename LayerType::RenderSurfaceType* old_occlusion_immune_ancestor =
175 NULL;
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 =
209 stack_.size() > 1 &&
210 !entering_unoccluded_subtree &&
211 have_transform_from_screen_to_new_target &&
212 !entering_root_target;
213 if (!copy_outside_occlusion_forward)
214 return;
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,
223 false,
224 gfx::Rect(),
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,
229 false,
230 gfx::Rect(),
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())
271 return;
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())
280 return;
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.
301 int shrink_left =
302 occlusion_rect.x() == affected_area_in_target.x() ? 0 : outset_right;
303 int shrink_top =
304 occlusion_rect.y() == affected_area_in_target.y() ? 0 : outset_bottom;
305 int shrink_right =
306 occlusion_rect.right() == affected_area_in_target.right() ?
307 0 : outset_left;
308 int shrink_bottom =
309 occlusion_rect.bottom() == affected_area_in_target.bottom() ?
310 0 : outset_top;
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,
351 false,
352 gfx::Rect(),
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
375 // before union.
376 if (new_target->parent()) {
377 stack_[last_index - 1].occlusion_from_outside_target.Union(
378 old_occlusion_from_outside_target_in_new_target);
380 stack_.pop_back();
381 } else {
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;
389 } else {
390 stack_.back().occlusion_from_outside_target.Clear();
394 if (!old_target->background_filters().HasFilterThatMovesPixels())
395 return;
397 ReduceOcclusionBelowSurface(old_target,
398 unoccluded_surface_rect,
399 old_surface->draw_transform(),
400 new_target,
401 &stack_.back().occlusion_from_inside_target);
402 ReduceOcclusionBelowSurface(old_target,
403 unoccluded_surface_rect,
404 old_surface->draw_transform(),
405 new_target,
406 &stack_.back().occlusion_from_outside_target);
408 if (!old_target->has_replica())
409 return;
410 ReduceOcclusionBelowSurface(old_target,
411 unoccluded_replica_rect,
412 old_surface->replica_draw_transform(),
413 new_target,
414 &stack_.back().occlusion_from_inside_target);
415 ReduceOcclusionBelowSurface(old_target,
416 unoccluded_replica_rect,
417 old_surface->replica_draw_transform(),
418 new_target,
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)
429 return;
431 if (!layer->uses_default_blend_mode())
432 return;
434 if (LayerIsInUnsorted3dRenderingContext(layer))
435 return;
437 if (!LayerTransformsToTargetKnown(layer))
438 return;
440 SimpleEnclosedRegion opaque_contents = layer->VisibleContentOpaqueRegion();
441 if (opaque_contents.IsEmpty())
442 return;
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())
448 return;
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());
454 } else {
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())
466 continue;
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>;
486 } // namespace cc