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/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" 17 18 namespace cc { 19 20 template <typename LayerType, typename RenderSurfaceType> 21 OcclusionTrackerBase<LayerType, RenderSurfaceType>::OcclusionTrackerBase( 22 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 prevent_occlusion_(false), 26 occluding_screen_space_rects_(NULL), 27 non_occluding_screen_space_rects_(NULL) {} 28 29 template <typename LayerType, typename RenderSurfaceType> 30 OcclusionTrackerBase<LayerType, RenderSurfaceType>::~OcclusionTrackerBase() {} 31 32 template <typename LayerType, typename RenderSurfaceType> 33 void OcclusionTrackerBase<LayerType, RenderSurfaceType>::EnterLayer( 34 const LayerIteratorPosition<LayerType>& layer_iterator, 35 bool prevent_occlusion) { 36 LayerType* render_target = layer_iterator.target_render_surface_layer; 37 38 if (layer_iterator.represents_itself) 39 EnterRenderTarget(render_target); 40 else if (layer_iterator.represents_target_render_surface) 41 FinishedRenderTarget(render_target); 42 43 prevent_occlusion_ = prevent_occlusion; 44 } 45 46 template <typename LayerType, typename RenderSurfaceType> 47 void OcclusionTrackerBase<LayerType, RenderSurfaceType>::LeaveLayer( 48 const LayerIteratorPosition<LayerType>& layer_iterator) { 49 LayerType* render_target = layer_iterator.target_render_surface_layer; 50 51 if (layer_iterator.represents_itself) 52 MarkOccludedBehindLayer(layer_iterator.current_layer); 53 // TODO(danakj): This should be done when entering the contributing surface, 54 // but in a way that the surface's own occlusion won't occlude itself. 55 else if (layer_iterator.represents_contributing_render_surface) 56 LeaveToRenderTarget(render_target); 57 58 prevent_occlusion_ = false; 59 } 60 61 template <typename RenderSurfaceType> 62 static gfx::Rect ScreenSpaceClipRectInTargetSurface( 63 const RenderSurfaceType* target_surface, gfx::Rect screen_space_clip_rect) { 64 gfx::Transform inverse_screen_space_transform( 65 gfx::Transform::kSkipInitialization); 66 if (!target_surface->screen_space_transform().GetInverse( 67 &inverse_screen_space_transform)) 68 return target_surface->content_rect(); 69 70 return gfx::ToEnclosingRect(MathUtil::ProjectClippedRect( 71 inverse_screen_space_transform, screen_space_clip_rect)); 72 } 73 74 template <typename RenderSurfaceType> 75 static Region TransformSurfaceOpaqueRegion(const Region& region, 76 bool have_clip_rect, 77 gfx::Rect clip_rect_in_new_target, 78 const gfx::Transform& transform) { 79 if (region.IsEmpty()) 80 return Region(); 81 82 // Verify that rects within the |surface| will remain rects in its target 83 // surface after applying |transform|. If this is true, then apply |transform| 84 // to each rect within |region| in order to transform the entire Region. 85 86 // TODO(danakj): Find a rect interior to each transformed quad. 87 if (!transform.Preserves2dAxisAlignment()) 88 return Region(); 89 90 // TODO(danakj): If the Region is too complex, degrade gracefully here by 91 // skipping rects in it. 92 Region transformed_region; 93 for (Region::Iterator rects(region); rects.has_rect(); rects.next()) { 94 bool clipped; 95 gfx::QuadF transformed_quad = 96 MathUtil::MapQuad(transform, gfx::QuadF(rects.rect()), &clipped); 97 gfx::Rect transformed_rect = 98 gfx::ToEnclosedRect(transformed_quad.BoundingBox()); 99 DCHECK(!clipped); // We only map if the transform preserves axis alignment. 100 if (have_clip_rect) 101 transformed_rect.Intersect(clip_rect_in_new_target); 102 transformed_region.Union(transformed_rect); 103 } 104 return transformed_region; 105 } 106 107 static inline bool LayerOpacityKnown(const Layer* layer) { 108 return !layer->draw_opacity_is_animating(); 109 } 110 static inline bool LayerOpacityKnown(const LayerImpl* layer) { 111 return true; 112 } 113 static inline bool LayerTransformsToTargetKnown(const Layer* layer) { 114 return !layer->draw_transform_is_animating(); 115 } 116 static inline bool LayerTransformsToTargetKnown(const LayerImpl* layer) { 117 return true; 118 } 119 120 static inline bool SurfaceOpacityKnown(const RenderSurface* rs) { 121 return !rs->draw_opacity_is_animating(); 122 } 123 static inline bool SurfaceOpacityKnown(const RenderSurfaceImpl* rs) { 124 return true; 125 } 126 static inline bool SurfaceTransformsToTargetKnown(const RenderSurface* rs) { 127 return !rs->target_surface_transforms_are_animating(); 128 } 129 static inline bool SurfaceTransformsToTargetKnown(const RenderSurfaceImpl* rs) { 130 return true; 131 } 132 static inline bool SurfaceTransformsToScreenKnown(const RenderSurface* rs) { 133 return !rs->screen_space_transforms_are_animating(); 134 } 135 static inline bool SurfaceTransformsToScreenKnown(const RenderSurfaceImpl* rs) { 136 return true; 137 } 138 139 static inline bool LayerIsInUnsorted3dRenderingContext(const Layer* layer) { 140 return layer->parent() && layer->parent()->preserves_3d(); 141 } 142 static inline bool LayerIsInUnsorted3dRenderingContext(const LayerImpl* layer) { 143 return false; 144 } 145 146 template <typename LayerType> 147 static inline bool LayerIsHidden(const LayerType* layer) { 148 return layer->hide_layer_and_subtree() || 149 (layer->parent() && LayerIsHidden(layer->parent())); 150 } 151 152 template <typename LayerType, typename RenderSurfaceType> 153 void OcclusionTrackerBase<LayerType, RenderSurfaceType>::EnterRenderTarget( 154 const LayerType* new_target) { 155 if (!stack_.empty() && stack_.back().target == new_target) 156 return; 157 158 const LayerType* old_target = NULL; 159 const RenderSurfaceType* old_ancestor_that_moves_pixels = NULL; 160 if (!stack_.empty()) { 161 old_target = stack_.back().target; 162 old_ancestor_that_moves_pixels = 163 old_target->render_surface()->nearest_ancestor_that_moves_pixels(); 164 } 165 const RenderSurfaceType* new_ancestor_that_moves_pixels = 166 new_target->render_surface()->nearest_ancestor_that_moves_pixels(); 167 168 stack_.push_back(StackObject(new_target)); 169 170 // We copy the screen occlusion into the new RenderSurface subtree, but we 171 // never copy in the occlusion from inside the target, since we are looking 172 // at a new RenderSurface target. 173 174 // If we are entering a subtree that is going to move pixels around, then the 175 // occlusion we've computed so far won't apply to the pixels we're drawing 176 // here in the same way. We discard the occlusion thus far to be safe, and 177 // ensure we don't cull any pixels that are moved such that they become 178 // visible. 179 bool entering_subtree_that_moves_pixels = 180 new_ancestor_that_moves_pixels && 181 new_ancestor_that_moves_pixels != old_ancestor_that_moves_pixels; 182 183 bool have_transform_from_screen_to_new_target = false; 184 gfx::Transform inverse_new_target_screen_space_transform( 185 // Note carefully, not used if screen space transform is uninvertible. 186 gfx::Transform::kSkipInitialization); 187 if (SurfaceTransformsToScreenKnown(new_target->render_surface())) { 188 have_transform_from_screen_to_new_target = 189 new_target->render_surface()->screen_space_transform().GetInverse( 190 &inverse_new_target_screen_space_transform); 191 } 192 193 bool entering_root_target = new_target->parent() == NULL; 194 195 bool copy_outside_occlusion_forward = 196 stack_.size() > 1 && 197 !entering_subtree_that_moves_pixels && 198 have_transform_from_screen_to_new_target && 199 !entering_root_target; 200 if (!copy_outside_occlusion_forward) 201 return; 202 203 int last_index = stack_.size() - 1; 204 gfx::Transform old_target_to_new_target_transform( 205 inverse_new_target_screen_space_transform, 206 old_target->render_surface()->screen_space_transform()); 207 stack_[last_index].occlusion_from_outside_target = 208 TransformSurfaceOpaqueRegion<RenderSurfaceType>( 209 stack_[last_index - 1].occlusion_from_outside_target, 210 false, 211 gfx::Rect(), 212 old_target_to_new_target_transform); 213 stack_[last_index].occlusion_from_outside_target.Union( 214 TransformSurfaceOpaqueRegion<RenderSurfaceType>( 215 stack_[last_index - 1].occlusion_from_inside_target, 216 false, 217 gfx::Rect(), 218 old_target_to_new_target_transform)); 219 } 220 221 template <typename LayerType, typename RenderSurfaceType> 222 void OcclusionTrackerBase<LayerType, RenderSurfaceType>::FinishedRenderTarget( 223 const LayerType* finished_target) { 224 // Make sure we know about the target surface. 225 EnterRenderTarget(finished_target); 226 227 RenderSurfaceType* surface = finished_target->render_surface(); 228 229 // Readbacks always happen on render targets so we only need to check 230 // for readbacks here. 231 bool target_is_only_for_copy_request = 232 finished_target->HasCopyRequest() && LayerIsHidden(finished_target); 233 234 // If the occlusion within the surface can not be applied to things outside of 235 // the surface's subtree, then clear the occlusion here so it won't be used. 236 // TODO(senorblanco): Make this smarter for SkImageFilter case: once 237 // SkImageFilters can report affectsOpacity(), call that. 238 if (finished_target->mask_layer() || 239 !SurfaceOpacityKnown(surface) || 240 surface->draw_opacity() < 1 || 241 target_is_only_for_copy_request || 242 finished_target->filters().HasFilterThatAffectsOpacity() || 243 finished_target->filter()) { 244 stack_.back().occlusion_from_outside_target.Clear(); 245 stack_.back().occlusion_from_inside_target.Clear(); 246 } else if (!SurfaceTransformsToTargetKnown(surface)) { 247 stack_.back().occlusion_from_inside_target.Clear(); 248 stack_.back().occlusion_from_outside_target.Clear(); 249 } 250 } 251 252 template <typename LayerType> 253 static void ReduceOcclusionBelowSurface(LayerType* contributing_layer, 254 gfx::Rect surface_rect, 255 const gfx::Transform& surface_transform, 256 LayerType* render_target, 257 Region* occlusion_from_inside_target) { 258 if (surface_rect.IsEmpty()) 259 return; 260 261 gfx::Rect affected_area_in_target = gfx::ToEnclosingRect( 262 MathUtil::MapClippedRect(surface_transform, gfx::RectF(surface_rect))); 263 if (contributing_layer->render_surface()->is_clipped()) { 264 affected_area_in_target.Intersect( 265 contributing_layer->render_surface()->clip_rect()); 266 } 267 if (affected_area_in_target.IsEmpty()) 268 return; 269 270 int outset_top, outset_right, outset_bottom, outset_left; 271 contributing_layer->background_filters().GetOutsets( 272 &outset_top, &outset_right, &outset_bottom, &outset_left); 273 274 // The filter can move pixels from outside of the clip, so allow affected_area 275 // to expand outside the clip. 276 affected_area_in_target.Inset( 277 -outset_left, -outset_top, -outset_right, -outset_bottom); 278 279 gfx::Rect FilterOutsetsInTarget(-outset_left, 280 -outset_top, 281 outset_left + outset_right, 282 outset_top + outset_bottom); 283 284 Region affected_occlusion = IntersectRegions(*occlusion_from_inside_target, 285 affected_area_in_target); 286 Region::Iterator affected_occlusion_rects(affected_occlusion); 287 288 occlusion_from_inside_target->Subtract(affected_area_in_target); 289 for (; affected_occlusion_rects.has_rect(); affected_occlusion_rects.next()) { 290 gfx::Rect occlusion_rect = affected_occlusion_rects.rect(); 291 292 // Shrink the rect by expanding the non-opaque pixels outside the rect. 293 294 // The left outset of the filters moves pixels on the right side of 295 // the occlusion_rect into it, shrinking its right edge. 296 int shrink_left = 297 occlusion_rect.x() == affected_area_in_target.x() ? 0 : outset_right; 298 int shrink_top = 299 occlusion_rect.y() == affected_area_in_target.y() ? 0 : outset_bottom; 300 int shrink_right = 301 occlusion_rect.right() == affected_area_in_target.right() ? 302 0 : outset_left; 303 int shrink_bottom = 304 occlusion_rect.bottom() == affected_area_in_target.bottom() ? 305 0 : outset_top; 306 307 occlusion_rect.Inset(shrink_left, shrink_top, shrink_right, shrink_bottom); 308 309 occlusion_from_inside_target->Union(occlusion_rect); 310 } 311 } 312 313 template <typename LayerType, typename RenderSurfaceType> 314 void OcclusionTrackerBase<LayerType, RenderSurfaceType>::LeaveToRenderTarget( 315 const LayerType* new_target) { 316 int last_index = stack_.size() - 1; 317 bool surface_will_be_at_top_after_pop = 318 stack_.size() > 1 && stack_[last_index - 1].target == new_target; 319 320 // We merge the screen occlusion from the current RenderSurfaceImpl subtree 321 // out to its parent target RenderSurfaceImpl. The target occlusion can be 322 // merged out as well but needs to be transformed to the new target. 323 324 const LayerType* old_target = stack_[last_index].target; 325 const RenderSurfaceType* old_surface = old_target->render_surface(); 326 327 Region old_occlusion_from_inside_target_in_new_target = 328 TransformSurfaceOpaqueRegion<RenderSurfaceType>( 329 stack_[last_index].occlusion_from_inside_target, 330 old_surface->is_clipped(), 331 old_surface->clip_rect(), 332 old_surface->draw_transform()); 333 if (old_target->has_replica() && !old_target->replica_has_mask()) { 334 old_occlusion_from_inside_target_in_new_target.Union( 335 TransformSurfaceOpaqueRegion<RenderSurfaceType>( 336 stack_[last_index].occlusion_from_inside_target, 337 old_surface->is_clipped(), 338 old_surface->clip_rect(), 339 old_surface->replica_draw_transform())); 340 } 341 342 Region old_occlusion_from_outside_target_in_new_target = 343 TransformSurfaceOpaqueRegion<RenderSurfaceType>( 344 stack_[last_index].occlusion_from_outside_target, 345 false, 346 gfx::Rect(), 347 old_surface->draw_transform()); 348 349 gfx::Rect unoccluded_surface_rect; 350 gfx::Rect unoccluded_replica_rect; 351 if (old_target->background_filters().HasFilterThatMovesPixels()) { 352 unoccluded_surface_rect = UnoccludedContributingSurfaceContentRect( 353 old_target, false, old_surface->content_rect(), NULL); 354 if (old_target->has_replica()) { 355 unoccluded_replica_rect = UnoccludedContributingSurfaceContentRect( 356 old_target, true, old_surface->content_rect(), NULL); 357 } 358 } 359 360 if (surface_will_be_at_top_after_pop) { 361 // Merge the top of the stack down. 362 stack_[last_index - 1].occlusion_from_inside_target.Union( 363 old_occlusion_from_inside_target_in_new_target); 364 // TODO(danakj): Strictly this should subtract the inside target occlusion 365 // before union. 366 if (new_target->parent()) { 367 stack_[last_index - 1].occlusion_from_outside_target.Union( 368 old_occlusion_from_outside_target_in_new_target); 369 } 370 stack_.pop_back(); 371 } else { 372 // Replace the top of the stack with the new pushed surface. 373 stack_.back().target = new_target; 374 stack_.back().occlusion_from_inside_target = 375 old_occlusion_from_inside_target_in_new_target; 376 if (new_target->parent()) { 377 stack_.back().occlusion_from_outside_target = 378 old_occlusion_from_outside_target_in_new_target; 379 } else { 380 stack_.back().occlusion_from_outside_target.Clear(); 381 } 382 } 383 384 if (!old_target->background_filters().HasFilterThatMovesPixels()) 385 return; 386 387 ReduceOcclusionBelowSurface(old_target, 388 unoccluded_surface_rect, 389 old_surface->draw_transform(), 390 new_target, 391 &stack_.back().occlusion_from_inside_target); 392 ReduceOcclusionBelowSurface(old_target, 393 unoccluded_surface_rect, 394 old_surface->draw_transform(), 395 new_target, 396 &stack_.back().occlusion_from_outside_target); 397 398 if (!old_target->has_replica()) 399 return; 400 ReduceOcclusionBelowSurface(old_target, 401 unoccluded_replica_rect, 402 old_surface->replica_draw_transform(), 403 new_target, 404 &stack_.back().occlusion_from_inside_target); 405 ReduceOcclusionBelowSurface(old_target, 406 unoccluded_replica_rect, 407 old_surface->replica_draw_transform(), 408 new_target, 409 &stack_.back().occlusion_from_outside_target); 410 } 411 412 template <typename LayerType, typename RenderSurfaceType> 413 void OcclusionTrackerBase<LayerType, RenderSurfaceType>:: 414 MarkOccludedBehindLayer(const LayerType* layer) { 415 DCHECK(!stack_.empty()); 416 DCHECK_EQ(layer->render_target(), stack_.back().target); 417 if (stack_.empty()) 418 return; 419 420 if (!LayerOpacityKnown(layer) || layer->draw_opacity() < 1) 421 return; 422 423 if (LayerIsInUnsorted3dRenderingContext(layer)) 424 return; 425 426 if (!LayerTransformsToTargetKnown(layer)) 427 return; 428 429 Region opaque_contents = layer->VisibleContentOpaqueRegion(); 430 if (opaque_contents.IsEmpty()) 431 return; 432 433 DCHECK(layer->visible_content_rect().Contains(opaque_contents.bounds())); 434 435 // TODO(danakj): Find a rect interior to each transformed quad. 436 if (!layer->draw_transform().Preserves2dAxisAlignment()) 437 return; 438 439 gfx::Rect clip_rect_in_target = ScreenSpaceClipRectInTargetSurface( 440 layer->render_target()->render_surface(), screen_space_clip_rect_); 441 if (layer->is_clipped()) { 442 clip_rect_in_target.Intersect(layer->clip_rect()); 443 } else { 444 clip_rect_in_target.Intersect( 445 layer->render_target()->render_surface()->content_rect()); 446 } 447 448 for (Region::Iterator opaque_content_rects(opaque_contents); 449 opaque_content_rects.has_rect(); 450 opaque_content_rects.next()) { 451 bool clipped; 452 gfx::QuadF transformed_quad = MathUtil::MapQuad( 453 layer->draw_transform(), 454 gfx::QuadF(opaque_content_rects.rect()), 455 &clipped); 456 gfx::Rect transformed_rect = 457 gfx::ToEnclosedRect(transformed_quad.BoundingBox()); 458 DCHECK(!clipped); // We only map if the transform preserves axis alignment. 459 transformed_rect.Intersect(clip_rect_in_target); 460 if (transformed_rect.width() < minimum_tracking_size_.width() && 461 transformed_rect.height() < minimum_tracking_size_.height()) 462 continue; 463 stack_.back().occlusion_from_inside_target.Union(transformed_rect); 464 465 if (!occluding_screen_space_rects_) 466 continue; 467 468 // Save the occluding area in screen space for debug visualization. 469 gfx::QuadF screen_space_quad = MathUtil::MapQuad( 470 layer->render_target()->render_surface()->screen_space_transform(), 471 gfx::QuadF(transformed_rect), &clipped); 472 // TODO(danakj): Store the quad in the debug info instead of the bounding 473 // box. 474 gfx::Rect screen_space_rect = 475 gfx::ToEnclosedRect(screen_space_quad.BoundingBox()); 476 occluding_screen_space_rects_->push_back(screen_space_rect); 477 } 478 479 if (!non_occluding_screen_space_rects_) 480 return; 481 482 Region non_opaque_contents = 483 SubtractRegions(gfx::Rect(layer->content_bounds()), opaque_contents); 484 for (Region::Iterator non_opaque_content_rects(non_opaque_contents); 485 non_opaque_content_rects.has_rect(); 486 non_opaque_content_rects.next()) { 487 // We've already checked for clipping in the MapQuad call above, these calls 488 // should not clip anything further. 489 gfx::Rect transformed_rect = gfx::ToEnclosedRect( 490 MathUtil::MapClippedRect(layer->draw_transform(), 491 gfx::RectF(non_opaque_content_rects.rect()))); 492 transformed_rect.Intersect(clip_rect_in_target); 493 if (transformed_rect.IsEmpty()) 494 continue; 495 496 bool clipped; 497 gfx::QuadF screen_space_quad = MathUtil::MapQuad( 498 layer->render_target()->render_surface()->screen_space_transform(), 499 gfx::QuadF(transformed_rect), 500 &clipped); 501 // TODO(danakj): Store the quad in the debug info instead of the bounding 502 // box. 503 gfx::Rect screen_space_rect = 504 gfx::ToEnclosedRect(screen_space_quad.BoundingBox()); 505 non_occluding_screen_space_rects_->push_back(screen_space_rect); 506 } 507 } 508 509 template <typename LayerType, typename RenderSurfaceType> 510 bool OcclusionTrackerBase<LayerType, RenderSurfaceType>::Occluded( 511 const LayerType* render_target, 512 gfx::Rect content_rect, 513 const gfx::Transform& draw_transform, 514 bool impl_draw_transform_is_unknown, 515 bool is_clipped, 516 gfx::Rect clip_rect_in_target, 517 bool* has_occlusion_from_outside_target_surface) const { 518 if (has_occlusion_from_outside_target_surface) 519 *has_occlusion_from_outside_target_surface = false; 520 if (prevent_occlusion_) 521 return false; 522 523 DCHECK(!stack_.empty()); 524 if (stack_.empty()) 525 return false; 526 if (content_rect.IsEmpty()) 527 return true; 528 if (impl_draw_transform_is_unknown) 529 return false; 530 531 // For tests with no render target. 532 if (!render_target) 533 return false; 534 535 DCHECK_EQ(render_target->render_target(), render_target); 536 DCHECK(render_target->render_surface()); 537 DCHECK_EQ(render_target, stack_.back().target); 538 539 gfx::Transform inverse_draw_transform(gfx::Transform::kSkipInitialization); 540 if (!draw_transform.GetInverse(&inverse_draw_transform)) 541 return false; 542 543 // Take the ToEnclosingRect at each step, as we want to contain any unoccluded 544 // partial pixels in the resulting Rect. 545 Region unoccluded_region_in_target_surface = gfx::ToEnclosingRect( 546 MathUtil::MapClippedRect(draw_transform, gfx::RectF(content_rect))); 547 // Layers can't clip across surfaces, so count this as internal occlusion. 548 if (is_clipped) 549 unoccluded_region_in_target_surface.Intersect(clip_rect_in_target); 550 unoccluded_region_in_target_surface.Subtract( 551 stack_.back().occlusion_from_inside_target); 552 gfx::RectF unoccluded_rect_in_target_surface_without_outside_occlusion = 553 unoccluded_region_in_target_surface.bounds(); 554 unoccluded_region_in_target_surface.Subtract( 555 stack_.back().occlusion_from_outside_target); 556 557 // Treat other clipping as occlusion from outside the surface. 558 // TODO(danakj): Clip to visibleContentRect? 559 unoccluded_region_in_target_surface.Intersect( 560 render_target->render_surface()->content_rect()); 561 unoccluded_region_in_target_surface.Intersect( 562 ScreenSpaceClipRectInTargetSurface(render_target->render_surface(), 563 screen_space_clip_rect_)); 564 565 gfx::RectF unoccluded_rect_in_target_surface = 566 unoccluded_region_in_target_surface.bounds(); 567 568 if (has_occlusion_from_outside_target_surface) { 569 // Check if the unoccluded rect shrank when applying outside occlusion. 570 *has_occlusion_from_outside_target_surface = !gfx::SubtractRects( 571 unoccluded_rect_in_target_surface_without_outside_occlusion, 572 unoccluded_rect_in_target_surface).IsEmpty(); 573 } 574 575 return unoccluded_rect_in_target_surface.IsEmpty(); 576 } 577 578 template <typename LayerType, typename RenderSurfaceType> 579 gfx::Rect OcclusionTrackerBase<LayerType, RenderSurfaceType>:: 580 UnoccludedContentRect( 581 const LayerType* render_target, 582 gfx::Rect content_rect, 583 const gfx::Transform& draw_transform, 584 bool impl_draw_transform_is_unknown, 585 bool is_clipped, 586 gfx::Rect clip_rect_in_target, 587 bool* has_occlusion_from_outside_target_surface) const { 588 if (has_occlusion_from_outside_target_surface) 589 *has_occlusion_from_outside_target_surface = false; 590 if (prevent_occlusion_) 591 return content_rect; 592 593 DCHECK(!stack_.empty()); 594 if (stack_.empty()) 595 return content_rect; 596 if (content_rect.IsEmpty()) 597 return content_rect; 598 if (impl_draw_transform_is_unknown) 599 return content_rect; 600 601 // For tests with no render target. 602 if (!render_target) 603 return content_rect; 604 605 DCHECK_EQ(render_target->render_target(), render_target); 606 DCHECK(render_target->render_surface()); 607 DCHECK_EQ(render_target, stack_.back().target); 608 609 gfx::Transform inverse_draw_transform(gfx::Transform::kSkipInitialization); 610 if (!draw_transform.GetInverse(&inverse_draw_transform)) 611 return content_rect; 612 613 // Take the ToEnclosingRect at each step, as we want to contain any unoccluded 614 // partial pixels in the resulting Rect. 615 Region unoccluded_region_in_target_surface = gfx::ToEnclosingRect( 616 MathUtil::MapClippedRect(draw_transform, gfx::RectF(content_rect))); 617 // Layers can't clip across surfaces, so count this as internal occlusion. 618 if (is_clipped) 619 unoccluded_region_in_target_surface.Intersect(clip_rect_in_target); 620 unoccluded_region_in_target_surface.Subtract( 621 stack_.back().occlusion_from_inside_target); 622 gfx::RectF unoccluded_rect_in_target_surface_without_outside_occlusion = 623 unoccluded_region_in_target_surface.bounds(); 624 unoccluded_region_in_target_surface.Subtract( 625 stack_.back().occlusion_from_outside_target); 626 627 // Treat other clipping as occlusion from outside the surface. 628 // TODO(danakj): Clip to visibleContentRect? 629 unoccluded_region_in_target_surface.Intersect( 630 render_target->render_surface()->content_rect()); 631 unoccluded_region_in_target_surface.Intersect( 632 ScreenSpaceClipRectInTargetSurface(render_target->render_surface(), 633 screen_space_clip_rect_)); 634 635 gfx::RectF unoccluded_rect_in_target_surface = 636 unoccluded_region_in_target_surface.bounds(); 637 gfx::Rect unoccluded_rect = gfx::ToEnclosingRect( 638 MathUtil::ProjectClippedRect(inverse_draw_transform, 639 unoccluded_rect_in_target_surface)); 640 unoccluded_rect.Intersect(content_rect); 641 642 if (has_occlusion_from_outside_target_surface) { 643 // Check if the unoccluded rect shrank when applying outside occlusion. 644 *has_occlusion_from_outside_target_surface = !gfx::SubtractRects( 645 unoccluded_rect_in_target_surface_without_outside_occlusion, 646 unoccluded_rect_in_target_surface).IsEmpty(); 647 } 648 649 return unoccluded_rect; 650 } 651 652 template <typename LayerType, typename RenderSurfaceType> 653 gfx::Rect OcclusionTrackerBase<LayerType, RenderSurfaceType>:: 654 UnoccludedContributingSurfaceContentRect( 655 const LayerType* layer, 656 bool for_replica, 657 gfx::Rect content_rect, 658 bool* has_occlusion_from_outside_target_surface) const { 659 DCHECK(!stack_.empty()); 660 // The layer is a contributing render_target so it should have a surface. 661 DCHECK(layer->render_surface()); 662 // The layer is a contributing render_target so its target should be itself. 663 DCHECK_EQ(layer->render_target(), layer); 664 // The layer should not be the root, else what is is contributing to? 665 DCHECK(layer->parent()); 666 // This should be called while the layer is still considered the current 667 // target in the occlusion tracker. 668 DCHECK_EQ(layer, stack_.back().target); 669 670 if (has_occlusion_from_outside_target_surface) 671 *has_occlusion_from_outside_target_surface = false; 672 if (prevent_occlusion_) 673 return content_rect; 674 675 if (content_rect.IsEmpty()) 676 return content_rect; 677 678 const RenderSurfaceType* surface = layer->render_surface(); 679 const LayerType* contributing_surface_render_target = 680 layer->parent()->render_target(); 681 682 if (!SurfaceTransformsToTargetKnown(surface)) 683 return content_rect; 684 685 gfx::Transform draw_transform = 686 for_replica ? surface->replica_draw_transform() 687 : surface->draw_transform(); 688 gfx::Transform inverse_draw_transform(gfx::Transform::kSkipInitialization); 689 if (!draw_transform.GetInverse(&inverse_draw_transform)) 690 return content_rect; 691 692 // A contributing surface doesn't get occluded by things inside its own 693 // surface, so only things outside the surface can occlude it. That occlusion 694 // is found just below the top of the stack (if it exists). 695 bool has_occlusion = stack_.size() > 1; 696 697 // Take the ToEnclosingRect at each step, as we want to contain any unoccluded 698 // partial pixels in the resulting Rect. 699 Region unoccluded_region_in_target_surface = gfx::ToEnclosingRect( 700 MathUtil::MapClippedRect(draw_transform, gfx::RectF(content_rect))); 701 // Layers can't clip across surfaces, so count this as internal occlusion. 702 if (surface->is_clipped()) 703 unoccluded_region_in_target_surface.Intersect(surface->clip_rect()); 704 if (has_occlusion) { 705 const StackObject& second_last = stack_[stack_.size() - 2]; 706 unoccluded_region_in_target_surface.Subtract( 707 second_last.occlusion_from_inside_target); 708 } 709 gfx::RectF unoccluded_rect_in_target_surface_without_outside_occlusion = 710 unoccluded_region_in_target_surface.bounds(); 711 if (has_occlusion) { 712 const StackObject& second_last = stack_[stack_.size() - 2]; 713 unoccluded_region_in_target_surface.Subtract( 714 second_last.occlusion_from_outside_target); 715 } 716 717 // Treat other clipping as occlusion from outside the target surface. 718 unoccluded_region_in_target_surface.Intersect( 719 contributing_surface_render_target->render_surface()->content_rect()); 720 unoccluded_region_in_target_surface.Intersect( 721 ScreenSpaceClipRectInTargetSurface( 722 contributing_surface_render_target->render_surface(), 723 screen_space_clip_rect_)); 724 725 gfx::RectF unoccluded_rect_in_target_surface = 726 unoccluded_region_in_target_surface.bounds(); 727 gfx::Rect unoccluded_rect = gfx::ToEnclosingRect( 728 MathUtil::ProjectClippedRect(inverse_draw_transform, 729 unoccluded_rect_in_target_surface)); 730 unoccluded_rect.Intersect(content_rect); 731 732 if (has_occlusion_from_outside_target_surface) { 733 // Check if the unoccluded rect shrank when applying outside occlusion. 734 *has_occlusion_from_outside_target_surface = !gfx::SubtractRects( 735 unoccluded_rect_in_target_surface_without_outside_occlusion, 736 unoccluded_rect_in_target_surface).IsEmpty(); 737 } 738 739 return unoccluded_rect; 740 } 741 742 // Instantiate (and export) templates here for the linker. 743 template class OcclusionTrackerBase<Layer, RenderSurface>; 744 template class OcclusionTrackerBase<LayerImpl, RenderSurfaceImpl>; 745 746 } // namespace cc 747