Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / cc / trees / occlusion_tracker.cc
blobb86f8547db84b4adec7a9623368db3dd46dfd244
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_impl.h"
14 #include "ui/gfx/geometry/quad_f.h"
15 #include "ui/gfx/geometry/rect_conversions.h"
17 namespace cc {
19 OcclusionTracker::OcclusionTracker(const gfx::Rect& screen_space_clip_rect)
20 : screen_space_clip_rect_(screen_space_clip_rect) {
23 OcclusionTracker::~OcclusionTracker() {
26 Occlusion OcclusionTracker::GetCurrentOcclusionForLayer(
27 const gfx::Transform& draw_transform) const {
28 DCHECK(!stack_.empty());
29 const StackObject& back = stack_.back();
30 return Occlusion(draw_transform,
31 back.occlusion_from_outside_target,
32 back.occlusion_from_inside_target);
35 Occlusion OcclusionTracker::GetCurrentOcclusionForContributingSurface(
36 const gfx::Transform& draw_transform) const {
37 DCHECK(!stack_.empty());
38 if (stack_.size() < 2)
39 return Occlusion();
40 // A contributing surface doesn't get occluded by things inside its own
41 // surface, so only things outside the surface can occlude it. That occlusion
42 // is found just below the top of the stack (if it exists).
43 const StackObject& second_last = stack_[stack_.size() - 2];
44 return Occlusion(draw_transform, second_last.occlusion_from_outside_target,
45 second_last.occlusion_from_inside_target);
48 void OcclusionTracker::EnterLayer(const LayerIteratorPosition& layer_iterator) {
49 LayerImpl* render_target = layer_iterator.target_render_surface_layer;
51 if (layer_iterator.represents_itself)
52 EnterRenderTarget(render_target);
53 else if (layer_iterator.represents_target_render_surface)
54 FinishedRenderTarget(render_target);
57 void OcclusionTracker::LeaveLayer(const LayerIteratorPosition& layer_iterator) {
58 LayerImpl* render_target = layer_iterator.target_render_surface_layer;
60 if (layer_iterator.represents_itself)
61 MarkOccludedBehindLayer(layer_iterator.current_layer);
62 // TODO(danakj): This should be done when entering the contributing surface,
63 // but in a way that the surface's own occlusion won't occlude itself.
64 else if (layer_iterator.represents_contributing_render_surface)
65 LeaveToRenderTarget(render_target);
68 static gfx::Rect ScreenSpaceClipRectInTargetSurface(
69 const RenderSurfaceImpl* target_surface,
70 const gfx::Rect& screen_space_clip_rect) {
71 gfx::Transform inverse_screen_space_transform(
72 gfx::Transform::kSkipInitialization);
73 if (!target_surface->screen_space_transform().GetInverse(
74 &inverse_screen_space_transform))
75 return target_surface->content_rect();
77 return MathUtil::ProjectEnclosingClippedRect(inverse_screen_space_transform,
78 screen_space_clip_rect);
81 static SimpleEnclosedRegion TransformSurfaceOpaqueRegion(
82 const SimpleEnclosedRegion& region,
83 bool have_clip_rect,
84 const gfx::Rect& clip_rect_in_new_target,
85 const gfx::Transform& transform) {
86 if (region.IsEmpty())
87 return region;
89 // Verify that rects within the |surface| will remain rects in its target
90 // surface after applying |transform|. If this is true, then apply |transform|
91 // to each rect within |region| in order to transform the entire Region.
93 // TODO(danakj): Find a rect interior to each transformed quad.
94 if (!transform.Preserves2dAxisAlignment())
95 return SimpleEnclosedRegion();
97 SimpleEnclosedRegion transformed_region;
98 for (size_t i = 0; i < region.GetRegionComplexity(); ++i) {
99 gfx::Rect transformed_rect =
100 MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(transform,
101 region.GetRect(i));
102 if (have_clip_rect)
103 transformed_rect.Intersect(clip_rect_in_new_target);
104 transformed_region.Union(transformed_rect);
106 return transformed_region;
109 void OcclusionTracker::EnterRenderTarget(const LayerImpl* new_target) {
110 if (!stack_.empty() && stack_.back().target == new_target)
111 return;
113 const LayerImpl* old_target = NULL;
114 const RenderSurfaceImpl* old_occlusion_immune_ancestor = NULL;
115 if (!stack_.empty()) {
116 old_target = stack_.back().target;
117 old_occlusion_immune_ancestor =
118 old_target->render_surface()->nearest_occlusion_immune_ancestor();
120 const RenderSurfaceImpl* new_occlusion_immune_ancestor =
121 new_target->render_surface()->nearest_occlusion_immune_ancestor();
123 stack_.push_back(StackObject(new_target));
125 // We copy the screen occlusion into the new RenderSurfaceImpl subtree, but we
126 // never copy in the occlusion from inside the target, since we are looking
127 // at a new RenderSurfaceImpl target.
129 // If entering an unoccluded subtree, do not carry forward the outside
130 // occlusion calculated so far.
131 bool entering_unoccluded_subtree =
132 new_occlusion_immune_ancestor &&
133 new_occlusion_immune_ancestor != old_occlusion_immune_ancestor;
135 gfx::Transform inverse_new_target_screen_space_transform(
136 // Note carefully, not used if screen space transform is uninvertible.
137 gfx::Transform::kSkipInitialization);
138 bool have_transform_from_screen_to_new_target =
139 new_target->render_surface()->screen_space_transform().GetInverse(
140 &inverse_new_target_screen_space_transform);
142 bool entering_root_target = new_target->parent() == NULL;
144 bool copy_outside_occlusion_forward =
145 stack_.size() > 1 &&
146 !entering_unoccluded_subtree &&
147 have_transform_from_screen_to_new_target &&
148 !entering_root_target;
149 if (!copy_outside_occlusion_forward)
150 return;
152 size_t last_index = stack_.size() - 1;
153 gfx::Transform old_target_to_new_target_transform(
154 inverse_new_target_screen_space_transform,
155 old_target->render_surface()->screen_space_transform());
156 stack_[last_index].occlusion_from_outside_target =
157 TransformSurfaceOpaqueRegion(
158 stack_[last_index - 1].occlusion_from_outside_target, false,
159 gfx::Rect(), old_target_to_new_target_transform);
160 stack_[last_index].occlusion_from_outside_target.Union(
161 TransformSurfaceOpaqueRegion(
162 stack_[last_index - 1].occlusion_from_inside_target, false,
163 gfx::Rect(), old_target_to_new_target_transform));
166 static bool LayerIsHidden(const LayerImpl* layer) {
167 return layer->hide_layer_and_subtree() ||
168 (layer->parent() && LayerIsHidden(layer->parent()));
171 void OcclusionTracker::FinishedRenderTarget(const LayerImpl* finished_target) {
172 // Make sure we know about the target surface.
173 EnterRenderTarget(finished_target);
175 RenderSurfaceImpl* surface = finished_target->render_surface();
177 // Readbacks always happen on render targets so we only need to check
178 // for readbacks here.
179 bool target_is_only_for_copy_request =
180 finished_target->HasCopyRequest() && LayerIsHidden(finished_target);
182 // If the occlusion within the surface can not be applied to things outside of
183 // the surface's subtree, then clear the occlusion here so it won't be used.
184 if (finished_target->mask_layer() || surface->draw_opacity() < 1 ||
185 !finished_target->uses_default_blend_mode() ||
186 target_is_only_for_copy_request ||
187 finished_target->filters().HasFilterThatAffectsOpacity()) {
188 stack_.back().occlusion_from_outside_target.Clear();
189 stack_.back().occlusion_from_inside_target.Clear();
193 static void ReduceOcclusionBelowSurface(
194 const LayerImpl* contributing_layer,
195 const gfx::Rect& surface_rect,
196 const gfx::Transform& surface_transform,
197 const LayerImpl* render_target,
198 SimpleEnclosedRegion* occlusion_from_inside_target) {
199 if (surface_rect.IsEmpty())
200 return;
202 gfx::Rect affected_area_in_target =
203 MathUtil::MapEnclosingClippedRect(surface_transform, surface_rect);
204 if (contributing_layer->render_surface()->is_clipped()) {
205 affected_area_in_target.Intersect(
206 contributing_layer->render_surface()->clip_rect());
208 if (affected_area_in_target.IsEmpty())
209 return;
211 int outset_top, outset_right, outset_bottom, outset_left;
212 contributing_layer->background_filters().GetOutsets(
213 &outset_top, &outset_right, &outset_bottom, &outset_left);
215 // The filter can move pixels from outside of the clip, so allow affected_area
216 // to expand outside the clip.
217 affected_area_in_target.Inset(
218 -outset_left, -outset_top, -outset_right, -outset_bottom);
219 SimpleEnclosedRegion affected_occlusion = *occlusion_from_inside_target;
220 affected_occlusion.Intersect(affected_area_in_target);
222 occlusion_from_inside_target->Subtract(affected_area_in_target);
223 for (size_t i = 0; i < affected_occlusion.GetRegionComplexity(); ++i) {
224 gfx::Rect occlusion_rect = affected_occlusion.GetRect(i);
226 // Shrink the rect by expanding the non-opaque pixels outside the rect.
228 // The left outset of the filters moves pixels on the right side of
229 // the occlusion_rect into it, shrinking its right edge.
230 int shrink_left =
231 occlusion_rect.x() == affected_area_in_target.x() ? 0 : outset_right;
232 int shrink_top =
233 occlusion_rect.y() == affected_area_in_target.y() ? 0 : outset_bottom;
234 int shrink_right =
235 occlusion_rect.right() == affected_area_in_target.right() ?
236 0 : outset_left;
237 int shrink_bottom =
238 occlusion_rect.bottom() == affected_area_in_target.bottom() ?
239 0 : outset_top;
241 occlusion_rect.Inset(shrink_left, shrink_top, shrink_right, shrink_bottom);
243 occlusion_from_inside_target->Union(occlusion_rect);
247 void OcclusionTracker::LeaveToRenderTarget(const LayerImpl* new_target) {
248 DCHECK(!stack_.empty());
249 size_t last_index = stack_.size() - 1;
250 bool surface_will_be_at_top_after_pop =
251 stack_.size() > 1 && stack_[last_index - 1].target == new_target;
253 // We merge the screen occlusion from the current RenderSurfaceImpl subtree
254 // out to its parent target RenderSurfaceImpl. The target occlusion can be
255 // merged out as well but needs to be transformed to the new target.
257 const LayerImpl* old_target = stack_[last_index].target;
258 const RenderSurfaceImpl* old_surface = old_target->render_surface();
260 SimpleEnclosedRegion old_occlusion_from_inside_target_in_new_target =
261 TransformSurfaceOpaqueRegion(
262 stack_[last_index].occlusion_from_inside_target,
263 old_surface->is_clipped(), old_surface->clip_rect(),
264 old_surface->draw_transform());
265 if (old_target->has_replica() && !old_target->replica_has_mask()) {
266 old_occlusion_from_inside_target_in_new_target.Union(
267 TransformSurfaceOpaqueRegion(
268 stack_[last_index].occlusion_from_inside_target,
269 old_surface->is_clipped(), old_surface->clip_rect(),
270 old_surface->replica_draw_transform()));
273 SimpleEnclosedRegion old_occlusion_from_outside_target_in_new_target =
274 TransformSurfaceOpaqueRegion(
275 stack_[last_index].occlusion_from_outside_target, false, gfx::Rect(),
276 old_surface->draw_transform());
278 gfx::Rect unoccluded_surface_rect;
279 gfx::Rect unoccluded_replica_rect;
280 if (old_target->background_filters().HasFilterThatMovesPixels()) {
281 Occlusion surface_occlusion = GetCurrentOcclusionForContributingSurface(
282 old_surface->draw_transform());
283 unoccluded_surface_rect =
284 surface_occlusion.GetUnoccludedContentRect(old_surface->content_rect());
285 if (old_target->has_replica()) {
286 Occlusion replica_occlusion = GetCurrentOcclusionForContributingSurface(
287 old_surface->replica_draw_transform());
288 unoccluded_replica_rect = replica_occlusion.GetUnoccludedContentRect(
289 old_surface->content_rect());
293 if (surface_will_be_at_top_after_pop) {
294 // Merge the top of the stack down.
295 stack_[last_index - 1].occlusion_from_inside_target.Union(
296 old_occlusion_from_inside_target_in_new_target);
297 // TODO(danakj): Strictly this should subtract the inside target occlusion
298 // before union.
299 if (new_target->parent()) {
300 stack_[last_index - 1].occlusion_from_outside_target.Union(
301 old_occlusion_from_outside_target_in_new_target);
303 stack_.pop_back();
304 } else {
305 // Replace the top of the stack with the new pushed surface.
306 stack_.back().target = new_target;
307 stack_.back().occlusion_from_inside_target =
308 old_occlusion_from_inside_target_in_new_target;
309 if (new_target->parent()) {
310 stack_.back().occlusion_from_outside_target =
311 old_occlusion_from_outside_target_in_new_target;
312 } else {
313 stack_.back().occlusion_from_outside_target.Clear();
317 if (!old_target->background_filters().HasFilterThatMovesPixels())
318 return;
320 ReduceOcclusionBelowSurface(old_target,
321 unoccluded_surface_rect,
322 old_surface->draw_transform(),
323 new_target,
324 &stack_.back().occlusion_from_inside_target);
325 ReduceOcclusionBelowSurface(old_target,
326 unoccluded_surface_rect,
327 old_surface->draw_transform(),
328 new_target,
329 &stack_.back().occlusion_from_outside_target);
331 if (!old_target->has_replica())
332 return;
333 ReduceOcclusionBelowSurface(old_target,
334 unoccluded_replica_rect,
335 old_surface->replica_draw_transform(),
336 new_target,
337 &stack_.back().occlusion_from_inside_target);
338 ReduceOcclusionBelowSurface(old_target,
339 unoccluded_replica_rect,
340 old_surface->replica_draw_transform(),
341 new_target,
342 &stack_.back().occlusion_from_outside_target);
345 void OcclusionTracker::MarkOccludedBehindLayer(const LayerImpl* layer) {
346 DCHECK(!stack_.empty());
347 DCHECK_EQ(layer->render_target(), stack_.back().target);
349 if (layer->draw_opacity() < 1)
350 return;
352 if (!layer->uses_default_blend_mode())
353 return;
355 if (layer->Is3dSorted())
356 return;
358 SimpleEnclosedRegion opaque_layer_region = layer->VisibleOpaqueRegion();
359 if (opaque_layer_region.IsEmpty())
360 return;
362 DCHECK(layer->visible_layer_rect().Contains(opaque_layer_region.bounds()));
364 // TODO(danakj): Find a rect interior to each transformed quad.
365 if (!layer->draw_transform().Preserves2dAxisAlignment())
366 return;
368 gfx::Rect clip_rect_in_target = ScreenSpaceClipRectInTargetSurface(
369 layer->render_target()->render_surface(), screen_space_clip_rect_);
370 if (layer->is_clipped()) {
371 clip_rect_in_target.Intersect(layer->clip_rect());
372 } else {
373 clip_rect_in_target.Intersect(
374 layer->render_target()->render_surface()->content_rect());
377 for (size_t i = 0; i < opaque_layer_region.GetRegionComplexity(); ++i) {
378 gfx::Rect transformed_rect =
379 MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(
380 layer->draw_transform(), opaque_layer_region.GetRect(i));
381 transformed_rect.Intersect(clip_rect_in_target);
382 if (transformed_rect.width() < minimum_tracking_size_.width() &&
383 transformed_rect.height() < minimum_tracking_size_.height())
384 continue;
385 stack_.back().occlusion_from_inside_target.Union(transformed_rect);
389 Region OcclusionTracker::ComputeVisibleRegionInScreen() const {
390 DCHECK(!stack_.back().target->parent());
391 const SimpleEnclosedRegion& occluded =
392 stack_.back().occlusion_from_inside_target;
393 Region visible_region(screen_space_clip_rect_);
394 for (size_t i = 0; i < occluded.GetRegionComplexity(); ++i)
395 visible_region.Subtract(occluded.GetRect(i));
396 return visible_region;
399 } // namespace cc