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