OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "cc/trees/occlusion_tracker.h" | |
6 | |
7 #include <algorithm> | |
8 | |
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" | |
17 | |
18 namespace cc { | |
19 | |
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) { | |
24 } | |
25 | |
26 template <typename LayerType> | |
27 OcclusionTracker<LayerType>::~OcclusionTracker() { | |
28 } | |
29 | |
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); | |
38 } | |
39 | |
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); | |
53 } | |
54 | |
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; | |
59 | |
60 if (layer_iterator.represents_itself) | |
61 EnterRenderTarget(render_target); | |
62 else if (layer_iterator.represents_target_render_surface) | |
63 FinishedRenderTarget(render_target); | |
64 } | |
65 | |
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; | |
70 | |
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); | |
77 } | |
78 | |
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(); | |
88 | |
89 return MathUtil::ProjectEnclosingClippedRect(inverse_screen_space_transform, | |
90 screen_space_clip_rect); | |
91 } | |
92 | |
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; | |
101 | |
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. | |
105 | |
106 // TODO(danakj): Find a rect interior to each transformed quad. | |
107 if (!transform.Preserves2dAxisAlignment()) | |
108 return SimpleEnclosedRegion(); | |
109 | |
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); | |
118 } | |
119 return transformed_region; | |
120 } | |
121 | |
122 static inline bool LayerOpacityKnown(const Layer* layer) { | |
123 return !layer->draw_opacity_is_animating(); | |
124 } | |
125 static inline bool LayerOpacityKnown(const LayerImpl* layer) { | |
126 return true; | |
127 } | |
128 static inline bool LayerTransformsToTargetKnown(const Layer* layer) { | |
129 return !layer->draw_transform_is_animating(); | |
130 } | |
131 static inline bool LayerTransformsToTargetKnown(const LayerImpl* layer) { | |
132 return true; | |
133 } | |
134 | |
135 static inline bool SurfaceOpacityKnown(const RenderSurface* rs) { | |
136 return !rs->draw_opacity_is_animating(); | |
137 } | |
138 static inline bool SurfaceOpacityKnown(const RenderSurfaceImpl* rs) { | |
139 return true; | |
140 } | |
141 static inline bool SurfaceTransformsToTargetKnown(const RenderSurface* rs) { | |
142 return !rs->target_surface_transforms_are_animating(); | |
143 } | |
144 static inline bool SurfaceTransformsToTargetKnown(const RenderSurfaceImpl* rs) { | |
145 return true; | |
146 } | |
147 static inline bool SurfaceTransformsToScreenKnown(const RenderSurface* rs) { | |
148 return !rs->screen_space_transforms_are_animating(); | |
149 } | |
150 static inline bool SurfaceTransformsToScreenKnown(const RenderSurfaceImpl* rs) { | |
151 return true; | |
152 } | |
153 | |
154 static inline bool LayerIsInUnsorted3dRenderingContext(const Layer* layer) { | |
155 return layer->Is3dSorted(); | |
156 } | |
157 static inline bool LayerIsInUnsorted3dRenderingContext(const LayerImpl* layer) { | |
158 return layer->Is3dSorted(); | |
159 } | |
160 | |
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())); | |
165 } | |
166 | |
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; | |
172 | |
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(); | |
180 } | |
181 const typename LayerType::RenderSurfaceType* new_occlusion_immune_ancestor = | |
182 new_target->render_surface()->nearest_occlusion_immune_ancestor(); | |
183 | |
184 stack_.push_back(StackObject(new_target)); | |
185 | |
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. | |
189 | |
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; | |
195 | |
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); | |
204 } | |
205 | |
206 bool entering_root_target = new_target->parent() == NULL; | |
207 | |
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; | |
215 | |
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)); | |
232 } | |
233 | |
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); | |
239 | |
240 typename LayerType::RenderSurfaceType* surface = | |
241 finished_target->render_surface(); | |
242 | |
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); | |
247 | |
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(); | |
260 } | |
261 } | |
262 | |
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; | |
272 | |
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()); | |
278 } | |
279 if (affected_area_in_target.IsEmpty()) | |
280 return; | |
281 | |
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); | |
285 | |
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); | |
292 | |
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); | |
296 | |
297 // Shrink the rect by expanding the non-opaque pixels outside the rect. | |
298 | |
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; | |
311 | |
312 occlusion_rect.Inset(shrink_left, shrink_top, shrink_right, shrink_bottom); | |
313 | |
314 occlusion_from_inside_target->Union(occlusion_rect); | |
315 } | |
316 } | |
317 | |
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; | |
324 | |
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. | |
328 | |
329 const LayerType* old_target = stack_[last_index].target; | |
330 const typename LayerType::RenderSurfaceType* old_surface = | |
331 old_target->render_surface(); | |
332 | |
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())); | |
346 } | |
347 | |
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()); | |
354 | |
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()); | |
367 } | |
368 } | |
369 | |
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); | |
379 } | |
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(); | |
391 } | |
392 } | |
393 | |
394 if (!old_target->background_filters().HasFilterThatMovesPixels()) | |
395 return; | |
396 | |
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); | |
407 | |
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); | |
420 } | |
421 | |
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); | |
427 | |
428 if (!LayerOpacityKnown(layer) || layer->draw_opacity() < 1) | |
429 return; | |
430 | |
431 if (!layer->uses_default_blend_mode()) | |
432 return; | |
433 | |
434 if (LayerIsInUnsorted3dRenderingContext(layer)) | |
435 return; | |
436 | |
437 if (!LayerTransformsToTargetKnown(layer)) | |
438 return; | |
439 | |
440 SimpleEnclosedRegion opaque_contents = layer->VisibleContentOpaqueRegion(); | |
441 if (opaque_contents.IsEmpty()) | |
442 return; | |
443 | |
444 DCHECK(layer->visible_content_rect().Contains(opaque_contents.bounds())); | |
445 | |
446 // TODO(danakj): Find a rect interior to each transformed quad. | |
447 if (!layer->draw_transform().Preserves2dAxisAlignment()) | |
448 return; | |
449 | |
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()); | |
457 } | |
458 | |
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); | |
468 } | |
469 } | |
470 | |
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; | |
480 } | |
481 | |
482 // Instantiate (and export) templates here for the linker. | |
483 template class OcclusionTracker<Layer>; | |
484 template class OcclusionTracker<LayerImpl>; | |
485 | |
486 } // namespace cc | |
OLD | NEW |