| 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 |