OLD | NEW |
---|---|
1 // Copyright 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "cc/resources/picture_pile.h" | 5 #include "cc/resources/picture_pile.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <limits> | 8 #include <limits> |
9 #include <vector> | 9 #include <vector> |
10 | 10 |
11 #include "cc/base/region.h" | 11 #include "cc/base/region.h" |
12 #include "cc/resources/picture_pile_impl.h" | 12 #include "cc/resources/picture_pile_impl.h" |
13 #include "skia/ext/analysis_canvas.h" | 13 #include "skia/ext/analysis_canvas.h" |
14 | 14 |
15 namespace { | 15 namespace { |
16 // Layout pixel buffer around the visible layer rect to record. Any base | 16 // Layout pixel buffer around the visible layer rect to record. Any base |
17 // picture that intersects the visible layer rect expanded by this distance | 17 // picture that intersects the visible layer rect expanded by this distance |
18 // will be recorded. | 18 // will be recorded. |
19 const int kPixelDistanceToRecord = 8000; | 19 const int kPixelDistanceToRecord = 8000; |
20 // We don't perform solid color analysis on images that have more than 10 skia | 20 // We don't perform solid color analysis on images that have more than 10 skia |
21 // operations. | 21 // operations. |
22 const int kOpCountThatIsOkToAnalyze = 10; | 22 const int kOpCountThatIsOkToAnalyze = 10; |
23 | 23 |
24 // Dimensions of the tiles in this picture pile as well as the dimensions of | 24 // Dimensions of the tiles in this picture pile as well as the dimensions of |
25 // the base picture in each tile. | 25 // the base picture in each tile. |
26 const int kBasePictureSize = 512; | 26 const int kBasePictureSize = 512; |
27 | 27 |
28 // Invalidation frequency settings. kInvalidationFrequencyThreshold is a value | |
29 // between 0 and 1 meaning invalidation frequency between 0% and 100% that | |
30 // indicates when to stop invalidating offscreen regions. | |
31 // kFrequentInvalidationDistanceThreshold defines what it means to be | |
32 // "offscreen" in terms of distance to visible in css pixels. | |
33 // TODO(vmpstr): Remove invalidation frequency after frequently invalidated | |
34 // content is not painted at a higher level. | |
35 const float kInvalidationFrequencyThreshold = 0.75f; | |
36 const int kFrequentInvalidationDistanceThreshold = 1024; | |
37 | |
38 // TODO(humper): The density threshold here is somewhat arbitrary; need a | 28 // TODO(humper): The density threshold here is somewhat arbitrary; need a |
39 // way to set // this from the command line so we can write a benchmark | 29 // way to set // this from the command line so we can write a benchmark |
40 // script and find a sweet spot. | 30 // script and find a sweet spot. |
41 const float kDensityThreshold = 0.5f; | 31 const float kDensityThreshold = 0.5f; |
42 | 32 |
43 bool rect_sort_y(const gfx::Rect& r1, const gfx::Rect& r2) { | 33 bool rect_sort_y(const gfx::Rect& r1, const gfx::Rect& r2) { |
44 return r1.y() < r2.y() || (r1.y() == r2.y() && r1.x() < r2.x()); | 34 return r1.y() < r2.y() || (r1.y() == r2.y() && r1.x() < r2.x()); |
45 } | 35 } |
46 | 36 |
47 bool rect_sort_x(const gfx::Rect& r1, const gfx::Rect& r2) { | 37 bool rect_sort_x(const gfx::Rect& r1, const gfx::Rect& r2) { |
(...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
209 | 199 |
210 CreatePictures(painter, recording_mode, record_rects); | 200 CreatePictures(painter, recording_mode, record_rects); |
211 | 201 |
212 DetermineIfSolidColor(); | 202 DetermineIfSolidColor(); |
213 | 203 |
214 has_any_recordings_ = true; | 204 has_any_recordings_ = true; |
215 DCHECK(CanRasterSlowTileCheck(recorded_viewport_)); | 205 DCHECK(CanRasterSlowTileCheck(recorded_viewport_)); |
216 return true; | 206 return true; |
217 } | 207 } |
218 | 208 |
219 void PicturePile::DidMoveToNewCompositor() { | |
220 for (auto& map_pair : picture_map_) | |
221 map_pair.second.ResetInvalidationHistory(); | |
222 } | |
223 | |
224 bool PicturePile::ApplyInvalidationAndResize(const gfx::Rect& interest_rect, | 209 bool PicturePile::ApplyInvalidationAndResize(const gfx::Rect& interest_rect, |
225 Region* invalidation, | 210 Region* invalidation, |
226 const gfx::Size& layer_size, | 211 const gfx::Size& layer_size, |
227 int frame_number) { | 212 int frame_number) { |
228 bool updated = false; | 213 bool updated = false; |
229 | 214 |
230 Region synthetic_invalidation; | 215 Region synthetic_invalidation; |
231 gfx::Size old_tiling_size = GetSize(); | 216 gfx::Size old_tiling_size = GetSize(); |
232 if (old_tiling_size != layer_size) { | 217 if (old_tiling_size != layer_size) { |
233 tiling_.SetTilingSize(layer_size); | 218 tiling_.SetTilingSize(layer_size); |
(...skipping 24 matching lines...) Expand all Loading... | |
258 tiling_.FirstBorderTileXIndexFromSrcCoord(min_tiling_size.width()); | 243 tiling_.FirstBorderTileXIndexFromSrcCoord(min_tiling_size.width()); |
259 } | 244 } |
260 int min_toss_y = tiling_.num_tiles_y(); | 245 int min_toss_y = tiling_.num_tiles_y(); |
261 if (max_tiling_size.height() > min_tiling_size.height()) { | 246 if (max_tiling_size.height() > min_tiling_size.height()) { |
262 min_toss_y = | 247 min_toss_y = |
263 tiling_.FirstBorderTileYIndexFromSrcCoord(min_tiling_size.height()); | 248 tiling_.FirstBorderTileYIndexFromSrcCoord(min_tiling_size.height()); |
264 } | 249 } |
265 for (const auto& key_picture_pair : picture_map_) { | 250 for (const auto& key_picture_pair : picture_map_) { |
266 const PictureMapKey& key = key_picture_pair.first; | 251 const PictureMapKey& key = key_picture_pair.first; |
267 if (key.first < min_toss_x && key.second < min_toss_y) { | 252 if (key.first < min_toss_x && key.second < min_toss_y) { |
268 has_any_recordings_ |= !!key_picture_pair.second.GetPicture(); | 253 has_any_recordings_ |= true; |
vmpstr
2015/04/07 17:43:14
nit: = true
| |
269 continue; | 254 continue; |
270 } | 255 } |
271 to_erase.push_back(key); | 256 to_erase.push_back(key); |
272 } | 257 } |
273 | 258 |
274 for (size_t i = 0; i < to_erase.size(); ++i) | 259 for (size_t i = 0; i < to_erase.size(); ++i) |
275 picture_map_.erase(to_erase[i]); | 260 picture_map_.erase(to_erase[i]); |
276 | 261 |
277 // If a recording is dropped and not re-recorded below, invalidate that | 262 // If a recording is dropped and not re-recorded below, invalidate that |
278 // full recording to cause any raster tiles that would use it to be | 263 // full recording to cause any raster tiles that would use it to be |
(...skipping 151 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
430 synthetic_invalidation.Union(top_rect); | 415 synthetic_invalidation.Union(top_rect); |
431 synthetic_invalidation.Union(bottom_rect); | 416 synthetic_invalidation.Union(bottom_rect); |
432 synthetic_invalidation.Union(exposed_rect); | 417 synthetic_invalidation.Union(exposed_rect); |
433 } | 418 } |
434 } | 419 } |
435 | 420 |
436 // Detect cases where the full pile is invalidated, in this situation we | 421 // Detect cases where the full pile is invalidated, in this situation we |
437 // can just drop/invalidate everything. | 422 // can just drop/invalidate everything. |
438 if (invalidation->Contains(gfx::Rect(old_tiling_size)) || | 423 if (invalidation->Contains(gfx::Rect(old_tiling_size)) || |
439 invalidation->Contains(gfx::Rect(GetSize()))) { | 424 invalidation->Contains(gfx::Rect(GetSize()))) { |
440 for (auto& it : picture_map_) | 425 updated = !picture_map_.empty(); |
441 updated = it.second.Invalidate(frame_number) || updated; | 426 picture_map_.clear(); |
442 } else { | 427 } else { |
443 // Expand invalidation that is on tiles that aren't in the interest rect and | 428 // Expand invalidation that is on tiles that aren't in the interest rect and |
444 // will not be re-recorded below. These tiles are no longer valid and should | 429 // will not be re-recorded below. These tiles are no longer valid and should |
445 // be considerered fully invalid, so we can know to not keep around raster | 430 // be considerered fully invalid, so we can know to not keep around raster |
446 // tiles that intersect with these recording tiles. | 431 // tiles that intersect with these recording tiles. |
447 Region invalidation_expanded_to_full_tiles; | 432 Region invalidation_expanded_to_full_tiles; |
448 | 433 |
449 for (Region::Iterator i(*invalidation); i.has_rect(); i.next()) { | 434 for (Region::Iterator i(*invalidation); i.has_rect(); i.next()) { |
450 gfx::Rect invalid_rect = i.rect(); | 435 gfx::Rect invalid_rect = i.rect(); |
451 | 436 |
(...skipping 19 matching lines...) Expand all Loading... | |
471 bool include_borders = true; | 456 bool include_borders = true; |
472 for (TilingData::Iterator iter(&tiling_, invalid_rect, include_borders); | 457 for (TilingData::Iterator iter(&tiling_, invalid_rect, include_borders); |
473 iter; | 458 iter; |
474 ++iter) { | 459 ++iter) { |
475 const PictureMapKey& key = iter.index(); | 460 const PictureMapKey& key = iter.index(); |
476 | 461 |
477 PictureMap::iterator picture_it = picture_map_.find(key); | 462 PictureMap::iterator picture_it = picture_map_.find(key); |
478 if (picture_it == picture_map_.end()) | 463 if (picture_it == picture_map_.end()) |
479 continue; | 464 continue; |
480 | 465 |
481 // Inform the grid cell that it has been invalidated in this frame. | 466 updated = true; |
482 updated = picture_it->second.Invalidate(frame_number) || updated; | 467 picture_map_.erase(key); |
468 | |
483 // Invalidate drops the picture so the whole tile better be invalidated | 469 // Invalidate drops the picture so the whole tile better be invalidated |
484 // if it won't be re-recorded below. | 470 // if it won't be re-recorded below. |
485 DCHECK_IMPLIES(!tiling_.TileBounds(key.first, key.second) | 471 DCHECK_IMPLIES(!tiling_.TileBounds(key.first, key.second) |
486 .Intersects(interest_rect_over_tiles), | 472 .Intersects(interest_rect_over_tiles), |
487 invalidation_expanded_to_full_tiles.Contains( | 473 invalidation_expanded_to_full_tiles.Contains( |
488 tiling_.TileBounds(key.first, key.second))); | 474 tiling_.TileBounds(key.first, key.second))); |
489 } | 475 } |
490 } | 476 } |
491 invalidation->Union(invalidation_expanded_to_full_tiles); | 477 invalidation->Union(invalidation_expanded_to_full_tiles); |
492 } | 478 } |
493 | 479 |
494 invalidation->Union(synthetic_invalidation); | 480 invalidation->Union(synthetic_invalidation); |
495 return updated; | 481 return updated; |
496 } | 482 } |
497 | 483 |
498 void PicturePile::GetInvalidTileRects(const gfx::Rect& interest_rect, | 484 void PicturePile::GetInvalidTileRects(const gfx::Rect& interest_rect, |
499 Region* invalidation, | 485 Region* invalidation, |
500 const gfx::Rect& visible_layer_rect, | 486 const gfx::Rect& visible_layer_rect, |
501 int frame_number, | 487 int frame_number, |
502 std::vector<gfx::Rect>* invalid_tiles) { | 488 std::vector<gfx::Rect>* invalid_tiles) { |
503 // Make a list of all invalid tiles; we will attempt to | 489 // Make a list of all invalid tiles; we will attempt to |
504 // cluster these into multiple invalidation regions. | 490 // cluster these into multiple invalidation regions. |
505 bool include_borders = true; | 491 bool include_borders = true; |
506 for (TilingData::Iterator it(&tiling_, interest_rect, include_borders); it; | 492 for (TilingData::Iterator it(&tiling_, interest_rect, include_borders); it; |
507 ++it) { | 493 ++it) { |
508 const PictureMapKey& key = it.index(); | 494 const PictureMapKey& key = it.index(); |
509 PictureInfo& info = picture_map_[key]; | 495 if (picture_map_.find(key) == picture_map_.end()) |
510 | 496 invalid_tiles->push_back(tiling_.TileBounds(key.first, key.second)); |
511 gfx::Rect rect = PaddedRect(key); | |
512 int distance_to_visible = | |
513 rect.ManhattanInternalDistance(visible_layer_rect); | |
514 | |
515 if (info.NeedsRecording(frame_number, distance_to_visible)) { | |
516 gfx::Rect tile = tiling_.TileBounds(key.first, key.second); | |
517 invalid_tiles->push_back(tile); | |
518 } else if (!info.GetPicture()) { | |
519 if (recorded_viewport_.Intersects(rect)) { | |
520 // Recorded viewport is just an optimization for a fully recorded | |
521 // interest rect. In this case, a tile in that rect has declined | |
522 // to be recorded (probably due to frequent invalidations). | |
523 // TODO(enne): Shrink the recorded_viewport_ rather than clearing. | |
524 recorded_viewport_ = gfx::Rect(); | |
525 } | |
526 | |
527 // If a tile in the interest rect is not recorded, the entire tile needs | |
528 // to be considered invalid, so that we know not to keep around raster | |
529 // tiles that intersect this recording tile. | |
530 invalidation->Union(tiling_.TileBounds(it.index_x(), it.index_y())); | |
531 } | |
532 } | 497 } |
533 } | 498 } |
534 | 499 |
535 void PicturePile::CreatePictures(ContentLayerClient* painter, | 500 void PicturePile::CreatePictures(ContentLayerClient* painter, |
536 RecordingSource::RecordingMode recording_mode, | 501 RecordingSource::RecordingMode recording_mode, |
537 const std::vector<gfx::Rect>& record_rects) { | 502 const std::vector<gfx::Rect>& record_rects) { |
538 for (const auto& record_rect : record_rects) { | 503 for (const auto& record_rect : record_rects) { |
539 gfx::Rect padded_record_rect = PadRect(record_rect); | 504 gfx::Rect padded_record_rect = PadRect(record_rect); |
540 | 505 |
541 int repeat_count = std::max(1, slow_down_raster_scale_factor_for_debug_); | 506 int repeat_count = std::max(1, slow_down_raster_scale_factor_for_debug_); |
(...skipping 21 matching lines...) Expand all Loading... | |
563 } | 528 } |
564 | 529 |
565 bool found_tile_for_recorded_picture = false; | 530 bool found_tile_for_recorded_picture = false; |
566 | 531 |
567 bool include_borders = true; | 532 bool include_borders = true; |
568 for (TilingData::Iterator it(&tiling_, padded_record_rect, include_borders); | 533 for (TilingData::Iterator it(&tiling_, padded_record_rect, include_borders); |
569 it; ++it) { | 534 it; ++it) { |
570 const PictureMapKey& key = it.index(); | 535 const PictureMapKey& key = it.index(); |
571 gfx::Rect tile = PaddedRect(key); | 536 gfx::Rect tile = PaddedRect(key); |
572 if (padded_record_rect.Contains(tile)) { | 537 if (padded_record_rect.Contains(tile)) { |
573 PictureInfo& info = picture_map_[key]; | 538 picture_map_[key] = picture; |
574 info.SetPicture(picture); | |
575 found_tile_for_recorded_picture = true; | 539 found_tile_for_recorded_picture = true; |
576 } | 540 } |
577 } | 541 } |
578 DCHECK(found_tile_for_recorded_picture); | 542 DCHECK(found_tile_for_recorded_picture); |
579 } | 543 } |
580 } | 544 } |
581 | 545 |
582 scoped_refptr<RasterSource> PicturePile::CreateRasterSource( | 546 scoped_refptr<RasterSource> PicturePile::CreateRasterSource( |
583 bool can_use_lcd_text) const { | 547 bool can_use_lcd_text) const { |
584 return scoped_refptr<RasterSource>( | 548 return scoped_refptr<RasterSource>( |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
650 return tile_grid_size_; | 614 return tile_grid_size_; |
651 } | 615 } |
652 | 616 |
653 bool PicturePile::CanRasterSlowTileCheck(const gfx::Rect& layer_rect) const { | 617 bool PicturePile::CanRasterSlowTileCheck(const gfx::Rect& layer_rect) const { |
654 bool include_borders = false; | 618 bool include_borders = false; |
655 for (TilingData::Iterator tile_iter(&tiling_, layer_rect, include_borders); | 619 for (TilingData::Iterator tile_iter(&tiling_, layer_rect, include_borders); |
656 tile_iter; ++tile_iter) { | 620 tile_iter; ++tile_iter) { |
657 PictureMap::const_iterator map_iter = picture_map_.find(tile_iter.index()); | 621 PictureMap::const_iterator map_iter = picture_map_.find(tile_iter.index()); |
658 if (map_iter == picture_map_.end()) | 622 if (map_iter == picture_map_.end()) |
659 return false; | 623 return false; |
660 if (!map_iter->second.GetPicture()) | |
661 return false; | |
662 } | 624 } |
663 return true; | 625 return true; |
664 } | 626 } |
665 | 627 |
666 void PicturePile::DetermineIfSolidColor() { | 628 void PicturePile::DetermineIfSolidColor() { |
667 is_solid_color_ = false; | 629 is_solid_color_ = false; |
668 solid_color_ = SK_ColorTRANSPARENT; | 630 solid_color_ = SK_ColorTRANSPARENT; |
669 | 631 |
670 if (picture_map_.empty()) { | 632 if (picture_map_.empty()) { |
671 return; | 633 return; |
672 } | 634 } |
673 | 635 |
674 PictureMap::const_iterator it = picture_map_.begin(); | 636 PictureMap::const_iterator it = picture_map_.begin(); |
675 const Picture* picture = it->second.GetPicture(); | 637 const Picture* picture = it->second.get(); |
676 | 638 |
677 // Missing recordings due to frequent invalidations or being too far away | 639 // Missing recordings due to frequent invalidations or being too far away |
678 // from the interest rect will cause the a null picture to exist. | 640 // from the interest rect will cause the a null picture to exist. |
679 if (!picture) | 641 if (!picture) |
680 return; | 642 return; |
681 | 643 |
682 // Don't bother doing more work if the first image is too complicated. | 644 // Don't bother doing more work if the first image is too complicated. |
683 if (picture->ApproximateOpCount() > kOpCountThatIsOkToAnalyze) | 645 if (picture->ApproximateOpCount() > kOpCountThatIsOkToAnalyze) |
684 return; | 646 return; |
685 | 647 |
686 // Make sure all of the mapped images point to the same picture. | 648 // Make sure all of the mapped images point to the same picture. |
687 for (++it; it != picture_map_.end(); ++it) { | 649 for (++it; it != picture_map_.end(); ++it) { |
688 if (it->second.GetPicture() != picture) | 650 if (it->second.get() != picture) |
689 return; | 651 return; |
690 } | 652 } |
691 | 653 |
692 gfx::Size layer_size = GetSize(); | 654 gfx::Size layer_size = GetSize(); |
693 skia::AnalysisCanvas canvas(layer_size.width(), layer_size.height()); | 655 skia::AnalysisCanvas canvas(layer_size.width(), layer_size.height()); |
694 | 656 |
695 picture->Raster(&canvas, nullptr, Region(), 1.0f); | 657 picture->Raster(&canvas, nullptr, Region(), 1.0f); |
696 is_solid_color_ = canvas.GetColorIfSolid(&solid_color_); | 658 is_solid_color_ = canvas.GetColorIfSolid(&solid_color_); |
697 } | 659 } |
698 | 660 |
699 gfx::Rect PicturePile::PaddedRect(const PictureMapKey& key) const { | 661 gfx::Rect PicturePile::PaddedRect(const PictureMapKey& key) const { |
700 gfx::Rect tile = tiling_.TileBounds(key.first, key.second); | 662 gfx::Rect tile = tiling_.TileBounds(key.first, key.second); |
701 return PadRect(tile); | 663 return PadRect(tile); |
702 } | 664 } |
703 | 665 |
704 gfx::Rect PicturePile::PadRect(const gfx::Rect& rect) const { | 666 gfx::Rect PicturePile::PadRect(const gfx::Rect& rect) const { |
705 gfx::Rect padded_rect = rect; | 667 gfx::Rect padded_rect = rect; |
706 padded_rect.Inset(-buffer_pixels(), -buffer_pixels(), -buffer_pixels(), | 668 padded_rect.Inset(-buffer_pixels(), -buffer_pixels(), -buffer_pixels(), |
707 -buffer_pixels()); | 669 -buffer_pixels()); |
708 return padded_rect; | 670 return padded_rect; |
709 } | 671 } |
710 | 672 |
711 void PicturePile::Clear() { | 673 void PicturePile::Clear() { |
712 picture_map_.clear(); | 674 picture_map_.clear(); |
713 recorded_viewport_ = gfx::Rect(); | 675 recorded_viewport_ = gfx::Rect(); |
714 has_any_recordings_ = false; | 676 has_any_recordings_ = false; |
715 is_solid_color_ = false; | 677 is_solid_color_ = false; |
716 } | 678 } |
717 | 679 |
718 PicturePile::PictureInfo::PictureInfo() : last_frame_number_(0) { | |
719 } | |
720 | |
721 PicturePile::PictureInfo::~PictureInfo() { | |
722 } | |
723 | |
724 void PicturePile::PictureInfo::AdvanceInvalidationHistory(int frame_number) { | |
725 DCHECK_GE(frame_number, last_frame_number_); | |
726 if (frame_number == last_frame_number_) | |
727 return; | |
728 | |
729 invalidation_history_ <<= (frame_number - last_frame_number_); | |
730 last_frame_number_ = frame_number; | |
731 } | |
732 | |
733 bool PicturePile::PictureInfo::Invalidate(int frame_number) { | |
734 AdvanceInvalidationHistory(frame_number); | |
735 invalidation_history_.set(0); | |
736 | |
737 bool did_invalidate = !!picture_.get(); | |
738 picture_ = NULL; | |
739 return did_invalidate; | |
740 } | |
741 | |
742 bool PicturePile::PictureInfo::NeedsRecording(int frame_number, | |
743 int distance_to_visible) { | |
744 AdvanceInvalidationHistory(frame_number); | |
745 | |
746 // We only need recording if we don't have a picture. Furthermore, we only | |
747 // need a recording if we're within frequent invalidation distance threshold | |
748 // or the invalidation is not frequent enough (below invalidation frequency | |
749 // threshold). | |
750 return !picture_.get() && | |
751 ((distance_to_visible <= kFrequentInvalidationDistanceThreshold) || | |
752 (GetInvalidationFrequency() < kInvalidationFrequencyThreshold)); | |
753 } | |
754 | |
755 void PicturePile::PictureInfo::ResetInvalidationHistory() { | |
756 invalidation_history_.reset(); | |
757 last_frame_number_ = 0; | |
758 } | |
759 | |
760 void PicturePile::SetBufferPixels(int new_buffer_pixels) { | 680 void PicturePile::SetBufferPixels(int new_buffer_pixels) { |
761 if (new_buffer_pixels == buffer_pixels()) | 681 if (new_buffer_pixels == buffer_pixels()) |
762 return; | 682 return; |
763 | 683 |
764 Clear(); | 684 Clear(); |
765 tiling_.SetBorderTexels(new_buffer_pixels); | 685 tiling_.SetBorderTexels(new_buffer_pixels); |
766 } | 686 } |
767 | 687 |
768 void PicturePile::PictureInfo::SetPicture(scoped_refptr<Picture> picture) { | |
769 picture_ = picture; | |
770 } | |
771 | |
772 const Picture* PicturePile::PictureInfo::GetPicture() const { | |
773 return picture_.get(); | |
774 } | |
775 | |
776 float PicturePile::PictureInfo::GetInvalidationFrequency() const { | |
777 return invalidation_history_.count() / | |
778 static_cast<float>(INVALIDATION_FRAMES_TRACKED); | |
779 } | |
780 | |
781 } // namespace cc | 688 } // namespace cc |
OLD | NEW |