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