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.
5 #include "cc/resources/tile_manager.h"
11 #include "base/bind.h"
12 #include "base/json/json_writer.h"
13 #include "base/logging.h"
14 #include "base/metrics/histogram.h"
15 #include "base/trace_event/trace_event_argument.h"
16 #include "cc/debug/devtools_instrumentation.h"
17 #include "cc/debug/frame_viewer_instrumentation.h"
18 #include "cc/debug/traced_value.h"
19 #include "cc/layers/picture_layer_impl.h"
20 #include "cc/resources/raster_buffer.h"
21 #include "cc/resources/rasterizer.h"
22 #include "cc/resources/tile.h"
23 #include "cc/resources/tile_task_runner.h"
24 #include "ui/gfx/geometry/rect_conversions.h"
29 // Flag to indicate whether we should try and detect that
30 // a tile is of solid color.
31 const bool kUseColorEstimator
= true;
33 class RasterTaskImpl
: public RasterTask
{
36 const Resource
* resource
,
37 RasterSource
* raster_source
,
38 const gfx::Rect
& content_rect
,
40 TileResolution tile_resolution
,
43 int source_frame_number
,
45 const base::Callback
<void(const RasterSource::SolidColorAnalysis
&, bool)>&
47 ImageDecodeTask::Vector
* dependencies
)
48 : RasterTask(resource
, dependencies
),
49 raster_source_(raster_source
),
50 content_rect_(content_rect
),
51 contents_scale_(contents_scale
),
52 tile_resolution_(tile_resolution
),
55 source_frame_number_(source_frame_number
),
56 analyze_picture_(analyze_picture
),
59 // Overridden from Task:
60 void RunOnWorkerThread() override
{
61 TRACE_EVENT0("cc", "RasterizerTaskImpl::RunOnWorkerThread");
63 DCHECK(raster_source_
.get());
64 DCHECK(raster_buffer_
);
66 if (analyze_picture_
) {
67 Analyze(raster_source_
.get());
68 if (analysis_
.is_solid_color
)
72 Raster(raster_source_
.get());
75 // Overridden from TileTask:
76 void ScheduleOnOriginThread(TileTaskClient
* client
) override
{
77 DCHECK(!raster_buffer_
);
78 raster_buffer_
= client
->AcquireBufferForRaster(resource());
80 void CompleteOnOriginThread(TileTaskClient
* client
) override
{
81 client
->ReleaseBufferForRaster(raster_buffer_
.Pass());
83 void RunReplyOnOriginThread() override
{
84 DCHECK(!raster_buffer_
);
85 reply_
.Run(analysis_
, !HasFinishedRunning());
89 ~RasterTaskImpl() override
{ DCHECK(!raster_buffer_
); }
92 void Analyze(const RasterSource
* raster_source
) {
93 frame_viewer_instrumentation::ScopedAnalyzeTask
analyze_task(
94 tile_id_
, tile_resolution_
, source_frame_number_
, layer_id_
);
96 DCHECK(raster_source
);
98 raster_source
->PerformSolidColorAnalysis(content_rect_
, contents_scale_
,
101 // Record the solid color prediction.
102 UMA_HISTOGRAM_BOOLEAN("Renderer4.SolidColorTilesAnalyzed",
103 analysis_
.is_solid_color
);
105 // Clear the flag if we're not using the estimator.
106 analysis_
.is_solid_color
&= kUseColorEstimator
;
109 void Raster(const RasterSource
* raster_source
) {
110 frame_viewer_instrumentation::ScopedRasterTask
raster_task(
111 tile_id_
, tile_resolution_
, source_frame_number_
, layer_id_
);
113 DCHECK(raster_source
);
115 raster_buffer_
->Playback(raster_source_
.get(), content_rect_
,
119 RasterSource::SolidColorAnalysis analysis_
;
120 scoped_refptr
<RasterSource
> raster_source_
;
121 gfx::Rect content_rect_
;
122 float contents_scale_
;
123 TileResolution tile_resolution_
;
125 const void* tile_id_
;
126 int source_frame_number_
;
127 bool analyze_picture_
;
128 const base::Callback
<void(const RasterSource::SolidColorAnalysis
&, bool)>
130 scoped_ptr
<RasterBuffer
> raster_buffer_
;
132 DISALLOW_COPY_AND_ASSIGN(RasterTaskImpl
);
135 class ImageDecodeTaskImpl
: public ImageDecodeTask
{
137 ImageDecodeTaskImpl(SkPixelRef
* pixel_ref
,
138 const base::Callback
<void(bool was_canceled
)>& reply
)
139 : pixel_ref_(skia::SharePtr(pixel_ref
)),
142 // Overridden from Task:
143 void RunOnWorkerThread() override
{
144 TRACE_EVENT0("cc", "ImageDecodeTaskImpl::RunOnWorkerThread");
146 devtools_instrumentation::ScopedImageDecodeTask
image_decode_task(
148 // This will cause the image referred to by pixel ref to be decoded.
149 pixel_ref_
->lockPixels();
150 pixel_ref_
->unlockPixels();
153 // Overridden from TileTask:
154 void ScheduleOnOriginThread(TileTaskClient
* client
) override
{}
155 void CompleteOnOriginThread(TileTaskClient
* client
) override
{}
156 void RunReplyOnOriginThread() override
{ reply_
.Run(!HasFinishedRunning()); }
159 ~ImageDecodeTaskImpl() override
{}
162 skia::RefPtr
<SkPixelRef
> pixel_ref_
;
163 const base::Callback
<void(bool was_canceled
)> reply_
;
165 DISALLOW_COPY_AND_ASSIGN(ImageDecodeTaskImpl
);
168 const char* TaskSetName(TaskSet task_set
) {
170 case TileManager::ALL
:
172 case TileManager::REQUIRED_FOR_ACTIVATION
:
173 return "REQUIRED_FOR_ACTIVATION";
174 case TileManager::REQUIRED_FOR_DRAW
:
175 return "REQUIRED_FOR_DRAW";
179 return "Invalid TaskSet";
184 RasterTaskCompletionStats::RasterTaskCompletionStats()
185 : completed_count(0u), canceled_count(0u) {}
187 scoped_refptr
<base::trace_event::ConvertableToTraceFormat
>
188 RasterTaskCompletionStatsAsValue(const RasterTaskCompletionStats
& stats
) {
189 scoped_refptr
<base::trace_event::TracedValue
> state
=
190 new base::trace_event::TracedValue();
191 state
->SetInteger("completed_count", stats
.completed_count
);
192 state
->SetInteger("canceled_count", stats
.canceled_count
);
197 scoped_ptr
<TileManager
> TileManager::Create(
198 TileManagerClient
* client
,
199 base::SequencedTaskRunner
* task_runner
,
200 ResourcePool
* resource_pool
,
201 TileTaskRunner
* tile_task_runner
,
202 Rasterizer
* rasterizer
,
203 size_t scheduled_raster_task_limit
) {
204 return make_scoped_ptr(new TileManager(client
, task_runner
, resource_pool
,
205 tile_task_runner
, rasterizer
,
206 scheduled_raster_task_limit
));
209 TileManager::TileManager(
210 TileManagerClient
* client
,
211 const scoped_refptr
<base::SequencedTaskRunner
>& task_runner
,
212 ResourcePool
* resource_pool
,
213 TileTaskRunner
* tile_task_runner
,
214 Rasterizer
* rasterizer
,
215 size_t scheduled_raster_task_limit
)
217 task_runner_(task_runner
),
218 resource_pool_(resource_pool
),
219 tile_task_runner_(tile_task_runner
),
220 rasterizer_(rasterizer
),
221 scheduled_raster_task_limit_(scheduled_raster_task_limit
),
222 all_tiles_that_need_to_be_rasterized_are_scheduled_(true),
223 did_check_for_completed_tasks_since_last_schedule_tasks_(true),
224 did_oom_on_last_assign_(false),
225 ready_to_activate_notifier_(
227 base::Bind(&TileManager::NotifyReadyToActivate
,
228 base::Unretained(this))),
229 ready_to_draw_notifier_(
231 base::Bind(&TileManager::NotifyReadyToDraw
, base::Unretained(this))),
232 ready_to_activate_check_notifier_(
234 base::Bind(&TileManager::CheckIfReadyToActivate
,
235 base::Unretained(this))),
236 ready_to_draw_check_notifier_(
238 base::Bind(&TileManager::CheckIfReadyToDraw
, base::Unretained(this))),
239 more_tiles_need_prepare_check_notifier_(
241 base::Bind(&TileManager::CheckIfMoreTilesNeedToBePrepared
,
242 base::Unretained(this))),
243 did_notify_ready_to_activate_(false),
244 did_notify_ready_to_draw_(false) {
245 tile_task_runner_
->SetClient(this);
248 TileManager::~TileManager() {
249 // Reset global state and manage. This should cause
250 // our memory usage to drop to zero.
251 global_state_
= GlobalStateThatImpactsTilePriority();
254 tile_task_runner_
->ScheduleTasks(&empty
);
255 orphan_raster_tasks_
.clear();
257 // This should finish all pending tasks and release any uninitialized
259 tile_task_runner_
->Shutdown();
260 tile_task_runner_
->CheckForCompletedTasks();
262 FreeResourcesForReleasedTiles();
263 CleanUpReleasedTiles();
266 void TileManager::Release(Tile
* tile
) {
267 released_tiles_
.push_back(tile
);
270 TaskSetCollection
TileManager::TasksThatShouldBeForcedToComplete() const {
271 TaskSetCollection tasks_that_should_be_forced_to_complete
;
272 if (global_state_
.tree_priority
!= SMOOTHNESS_TAKES_PRIORITY
)
273 tasks_that_should_be_forced_to_complete
[REQUIRED_FOR_ACTIVATION
] = true;
274 return tasks_that_should_be_forced_to_complete
;
277 void TileManager::FreeResourcesForReleasedTiles() {
278 for (std::vector
<Tile
*>::iterator it
= released_tiles_
.begin();
279 it
!= released_tiles_
.end();
282 FreeResourcesForTile(tile
);
286 void TileManager::CleanUpReleasedTiles() {
287 std::vector
<Tile
*>::iterator it
= released_tiles_
.begin();
288 while (it
!= released_tiles_
.end()) {
291 if (tile
->HasRasterTask()) {
296 DCHECK(!tile
->HasResource());
297 DCHECK(tiles_
.find(tile
->id()) != tiles_
.end());
298 tiles_
.erase(tile
->id());
300 LayerCountMap::iterator layer_it
=
301 used_layer_counts_
.find(tile
->layer_id());
302 DCHECK_GT(layer_it
->second
, 0);
303 if (--layer_it
->second
== 0) {
304 used_layer_counts_
.erase(layer_it
);
305 image_decode_tasks_
.erase(tile
->layer_id());
309 it
= released_tiles_
.erase(it
);
313 void TileManager::DidFinishRunningTileTasks(TaskSet task_set
) {
314 TRACE_EVENT1("cc", "TileManager::DidFinishRunningTileTasks", "task_set",
315 TaskSetName(task_set
));
319 bool memory_usage_above_limit
=
320 resource_pool_
->total_memory_usage_bytes() >
321 global_state_
.soft_memory_limit_in_bytes
;
323 if (all_tiles_that_need_to_be_rasterized_are_scheduled_
&&
324 !memory_usage_above_limit
)
327 more_tiles_need_prepare_check_notifier_
.Schedule();
330 case REQUIRED_FOR_ACTIVATION
:
331 ready_to_activate_check_notifier_
.Schedule();
333 case REQUIRED_FOR_DRAW
:
334 ready_to_draw_check_notifier_
.Schedule();
341 void TileManager::PrepareTiles(
342 const GlobalStateThatImpactsTilePriority
& state
) {
343 TRACE_EVENT0("cc", "TileManager::PrepareTiles");
345 global_state_
= state
;
347 PrepareTilesMode prepare_tiles_mode
= rasterizer_
->GetPrepareTilesMode();
349 // TODO(hendrikw): Consider moving some of this code to the rasterizer.
350 if (prepare_tiles_mode
!= PrepareTilesMode::PREPARE_NONE
) {
351 // We need to call CheckForCompletedTasks() once in-between each call
352 // to ScheduleTasks() to prevent canceled tasks from being scheduled.
353 if (!did_check_for_completed_tasks_since_last_schedule_tasks_
) {
354 tile_task_runner_
->CheckForCompletedTasks();
355 did_check_for_completed_tasks_since_last_schedule_tasks_
= true;
358 FreeResourcesForReleasedTiles();
359 CleanUpReleasedTiles();
361 TileVector tiles_that_need_to_be_rasterized
;
362 scoped_ptr
<RasterTilePriorityQueue
> raster_priority_queue(
363 client_
->BuildRasterQueue(global_state_
.tree_priority
,
364 RasterTilePriorityQueue::Type::ALL
));
365 // Inform the client that will likely require a draw if the top tile is
366 // required for draw.
367 client_
->SetIsLikelyToRequireADraw(
368 !raster_priority_queue
->IsEmpty() &&
369 raster_priority_queue
->Top()->required_for_draw());
370 AssignGpuMemoryToTiles(raster_priority_queue
.get(),
371 scheduled_raster_task_limit_
,
372 &tiles_that_need_to_be_rasterized
);
374 // Schedule tile tasks.
375 ScheduleTasks(tiles_that_need_to_be_rasterized
);
377 did_notify_ready_to_activate_
= false;
378 did_notify_ready_to_draw_
= false;
380 if (global_state_
.hard_memory_limit_in_bytes
== 0) {
381 resource_pool_
->CheckBusyResources(false);
382 MemoryUsage
memory_limit(0, 0);
383 MemoryUsage
memory_usage(resource_pool_
->acquired_memory_usage_bytes(),
384 resource_pool_
->acquired_resource_count());
385 FreeTileResourcesUntilUsageIsWithinLimit(nullptr, memory_limit
,
389 did_notify_ready_to_activate_
= false;
390 did_notify_ready_to_draw_
= false;
391 ready_to_activate_notifier_
.Schedule();
392 ready_to_draw_notifier_
.Schedule();
395 TRACE_EVENT_INSTANT1("cc", "DidPrepareTiles", TRACE_EVENT_SCOPE_THREAD
,
396 "state", BasicStateAsValue());
398 TRACE_COUNTER_ID1("cc", "unused_memory_bytes", this,
399 resource_pool_
->total_memory_usage_bytes() -
400 resource_pool_
->acquired_memory_usage_bytes());
403 void TileManager::SynchronouslyRasterizeTiles(
404 const GlobalStateThatImpactsTilePriority
& state
) {
405 TRACE_EVENT0("cc", "TileManager::SynchronouslyRasterizeTiles");
407 DCHECK(rasterizer_
->GetPrepareTilesMode() !=
408 PrepareTilesMode::RASTERIZE_PRIORITIZED_TILES
);
410 global_state_
= state
;
412 FreeResourcesForReleasedTiles();
413 CleanUpReleasedTiles();
415 scoped_ptr
<RasterTilePriorityQueue
> required_for_draw_queue(
416 client_
->BuildRasterQueue(
417 global_state_
.tree_priority
,
418 RasterTilePriorityQueue::Type::REQUIRED_FOR_DRAW
));
419 TileVector tiles_that_need_to_be_rasterized
;
420 AssignGpuMemoryToTiles(required_for_draw_queue
.get(),
421 std::numeric_limits
<size_t>::max(),
422 &tiles_that_need_to_be_rasterized
);
424 // We must reduce the amount of unused resources before calling
425 // RunTasks to prevent usage from rising above limits.
426 resource_pool_
->ReduceResourceUsage();
428 // Run and complete all raster task synchronously.
429 rasterizer_
->RasterizeTiles(
430 tiles_that_need_to_be_rasterized
, resource_pool_
,
431 tile_task_runner_
->GetResourceFormat(),
432 base::Bind(&TileManager::UpdateTileDrawInfo
, base::Unretained(this)));
434 // Use on-demand raster for any required-for-draw tiles that have not been
435 // assigned memory after reaching a steady memory state.
436 // TODO(hendrikw): Figure out why this would improve jank on some tests - See
438 required_for_draw_queue
= client_
->BuildRasterQueue(
439 global_state_
.tree_priority
,
440 RasterTilePriorityQueue::Type::REQUIRED_FOR_DRAW
);
442 // Change to OOM mode for any tiles that have not been been assigned memory.
443 // This ensures that we draw even when OOM.
444 for (; !required_for_draw_queue
->IsEmpty(); required_for_draw_queue
->Pop()) {
445 Tile
* tile
= required_for_draw_queue
->Top();
446 tile
->draw_info().set_oom();
447 client_
->NotifyTileStateChanged(tile
);
450 TRACE_EVENT_INSTANT1("cc", "DidRasterize", TRACE_EVENT_SCOPE_THREAD
, "state",
451 BasicStateAsValue());
453 TRACE_COUNTER_ID1("cc", "unused_memory_bytes", this,
454 resource_pool_
->total_memory_usage_bytes() -
455 resource_pool_
->acquired_memory_usage_bytes());
458 void TileManager::UpdateVisibleTiles(
459 const GlobalStateThatImpactsTilePriority
& state
) {
460 TRACE_EVENT0("cc", "TileManager::UpdateVisibleTiles");
462 tile_task_runner_
->CheckForCompletedTasks();
465 PrepareTilesMode prepare_tiles_mode
= rasterizer_
->GetPrepareTilesMode();
466 if (prepare_tiles_mode
!= PrepareTilesMode::RASTERIZE_PRIORITIZED_TILES
)
467 SynchronouslyRasterizeTiles(state
);
469 did_check_for_completed_tasks_since_last_schedule_tasks_
= true;
471 TRACE_EVENT_INSTANT1(
473 "DidUpdateVisibleTiles",
474 TRACE_EVENT_SCOPE_THREAD
,
476 RasterTaskCompletionStatsAsValue(update_visible_tiles_stats_
));
477 update_visible_tiles_stats_
= RasterTaskCompletionStats();
480 scoped_refptr
<base::trace_event::ConvertableToTraceFormat
>
481 TileManager::BasicStateAsValue() const {
482 scoped_refptr
<base::trace_event::TracedValue
> value
=
483 new base::trace_event::TracedValue();
484 BasicStateAsValueInto(value
.get());
488 void TileManager::BasicStateAsValueInto(
489 base::trace_event::TracedValue
* state
) const {
490 state
->SetInteger("tile_count", tiles_
.size());
491 state
->SetBoolean("did_oom_on_last_assign", did_oom_on_last_assign_
);
492 state
->BeginDictionary("global_state");
493 global_state_
.AsValueInto(state
);
494 state
->EndDictionary();
497 scoped_ptr
<EvictionTilePriorityQueue
>
498 TileManager::FreeTileResourcesUntilUsageIsWithinLimit(
499 scoped_ptr
<EvictionTilePriorityQueue
> eviction_priority_queue
,
500 const MemoryUsage
& limit
,
501 MemoryUsage
* usage
) {
502 while (usage
->Exceeds(limit
)) {
503 if (!eviction_priority_queue
) {
504 eviction_priority_queue
=
505 client_
->BuildEvictionQueue(global_state_
.tree_priority
);
507 if (eviction_priority_queue
->IsEmpty())
510 Tile
* tile
= eviction_priority_queue
->Top();
511 *usage
-= MemoryUsage::FromTile(tile
);
512 FreeResourcesForTileAndNotifyClientIfTileWasReadyToDraw(tile
);
513 eviction_priority_queue
->Pop();
515 return eviction_priority_queue
;
518 scoped_ptr
<EvictionTilePriorityQueue
>
519 TileManager::FreeTileResourcesWithLowerPriorityUntilUsageIsWithinLimit(
520 scoped_ptr
<EvictionTilePriorityQueue
> eviction_priority_queue
,
521 const MemoryUsage
& limit
,
522 const TilePriority
& other_priority
,
523 MemoryUsage
* usage
) {
524 while (usage
->Exceeds(limit
)) {
525 if (!eviction_priority_queue
) {
526 eviction_priority_queue
=
527 client_
->BuildEvictionQueue(global_state_
.tree_priority
);
529 if (eviction_priority_queue
->IsEmpty())
532 Tile
* tile
= eviction_priority_queue
->Top();
533 if (!other_priority
.IsHigherPriorityThan(tile
->combined_priority()))
536 *usage
-= MemoryUsage::FromTile(tile
);
537 FreeResourcesForTileAndNotifyClientIfTileWasReadyToDraw(tile
);
538 eviction_priority_queue
->Pop();
540 return eviction_priority_queue
;
543 bool TileManager::TilePriorityViolatesMemoryPolicy(
544 const TilePriority
& priority
) {
545 switch (global_state_
.memory_limit_policy
) {
548 case ALLOW_ABSOLUTE_MINIMUM
:
549 return priority
.priority_bin
> TilePriority::NOW
;
550 case ALLOW_PREPAINT_ONLY
:
551 return priority
.priority_bin
> TilePriority::SOON
;
553 return priority
.distance_to_visible
==
554 std::numeric_limits
<float>::infinity();
560 void TileManager::AssignGpuMemoryToTiles(
561 RasterTilePriorityQueue
* raster_priority_queue
,
562 size_t scheduled_raster_task_limit
,
563 TileVector
* tiles_that_need_to_be_rasterized
) {
564 TRACE_EVENT_BEGIN0("cc", "TileManager::AssignGpuMemoryToTiles");
566 // Maintain the list of released resources that can potentially be re-used
567 // or deleted. If this operation becomes expensive too, only do this after
568 // some resource(s) was returned. Note that in that case, one also need to
569 // invalidate when releasing some resource from the pool.
570 resource_pool_
->CheckBusyResources(false);
572 // Now give memory out to the tiles until we're out, and build
573 // the needs-to-be-rasterized queue.
574 unsigned schedule_priority
= 1u;
575 all_tiles_that_need_to_be_rasterized_are_scheduled_
= true;
576 bool had_enough_memory_to_schedule_tiles_needed_now
= true;
578 MemoryUsage
hard_memory_limit(global_state_
.hard_memory_limit_in_bytes
,
579 global_state_
.num_resources_limit
);
580 MemoryUsage
soft_memory_limit(global_state_
.soft_memory_limit_in_bytes
,
581 global_state_
.num_resources_limit
);
582 MemoryUsage
memory_usage(resource_pool_
->acquired_memory_usage_bytes(),
583 resource_pool_
->acquired_resource_count());
585 scoped_ptr
<EvictionTilePriorityQueue
> eviction_priority_queue
;
586 for (; !raster_priority_queue
->IsEmpty(); raster_priority_queue
->Pop()) {
587 Tile
* tile
= raster_priority_queue
->Top();
588 TilePriority priority
= tile
->combined_priority();
590 if (TilePriorityViolatesMemoryPolicy(priority
)) {
591 TRACE_EVENT_INSTANT0(
592 "cc", "TileManager::AssignGpuMemory tile violates memory policy",
593 TRACE_EVENT_SCOPE_THREAD
);
597 // We won't be able to schedule this tile, so break out early.
598 if (tiles_that_need_to_be_rasterized
->size() >=
599 scheduled_raster_task_limit
) {
600 all_tiles_that_need_to_be_rasterized_are_scheduled_
= false;
604 TileDrawInfo
& draw_info
= tile
->draw_info();
605 tile
->scheduled_priority_
= schedule_priority
++;
607 DCHECK_IMPLIES(draw_info
.mode() != TileDrawInfo::OOM_MODE
,
608 !draw_info
.IsReadyToDraw());
610 // If the tile already has a raster_task, then the memory used by it is
611 // already accounted for in memory_usage. Otherwise, we'll have to acquire
612 // more memory to create a raster task.
613 MemoryUsage memory_required_by_tile_to_be_scheduled
;
614 if (!tile
->raster_task_
.get()) {
615 memory_required_by_tile_to_be_scheduled
= MemoryUsage::FromConfig(
616 tile
->desired_texture_size(), tile_task_runner_
->GetResourceFormat());
619 bool tile_is_needed_now
= priority
.priority_bin
== TilePriority::NOW
;
621 // This is the memory limit that will be used by this tile. Depending on
622 // the tile priority, it will be one of hard_memory_limit or
623 // soft_memory_limit.
624 MemoryUsage
& tile_memory_limit
=
625 tile_is_needed_now
? hard_memory_limit
: soft_memory_limit
;
627 const MemoryUsage
& scheduled_tile_memory_limit
=
628 tile_memory_limit
- memory_required_by_tile_to_be_scheduled
;
629 eviction_priority_queue
=
630 FreeTileResourcesWithLowerPriorityUntilUsageIsWithinLimit(
631 eviction_priority_queue
.Pass(), scheduled_tile_memory_limit
,
632 priority
, &memory_usage
);
633 bool memory_usage_is_within_limit
=
634 !memory_usage
.Exceeds(scheduled_tile_memory_limit
);
636 // If we couldn't fit the tile into our current memory limit, then we're
638 if (!memory_usage_is_within_limit
) {
639 if (tile_is_needed_now
)
640 had_enough_memory_to_schedule_tiles_needed_now
= false;
641 all_tiles_that_need_to_be_rasterized_are_scheduled_
= false;
645 memory_usage
+= memory_required_by_tile_to_be_scheduled
;
646 tiles_that_need_to_be_rasterized
->push_back(tile
);
649 // Note that we should try and further reduce memory in case the above loop
650 // didn't reduce memory. This ensures that we always release as many resources
651 // as possible to stay within the memory limit.
652 eviction_priority_queue
= FreeTileResourcesUntilUsageIsWithinLimit(
653 eviction_priority_queue
.Pass(), hard_memory_limit
, &memory_usage
);
655 UMA_HISTOGRAM_BOOLEAN("TileManager.ExceededMemoryBudget",
656 !had_enough_memory_to_schedule_tiles_needed_now
);
657 did_oom_on_last_assign_
= !had_enough_memory_to_schedule_tiles_needed_now
;
659 memory_stats_from_last_assign_
.total_budget_in_bytes
=
660 global_state_
.hard_memory_limit_in_bytes
;
661 memory_stats_from_last_assign_
.total_bytes_used
= memory_usage
.memory_bytes();
662 memory_stats_from_last_assign_
.had_enough_memory
=
663 had_enough_memory_to_schedule_tiles_needed_now
;
665 TRACE_EVENT_END2("cc", "TileManager::AssignGpuMemoryToTiles",
666 "all_tiles_that_need_to_be_rasterized_are_scheduled",
667 all_tiles_that_need_to_be_rasterized_are_scheduled_
,
668 "had_enough_memory_to_schedule_tiles_needed_now",
669 had_enough_memory_to_schedule_tiles_needed_now
);
672 void TileManager::FreeResourcesForTile(Tile
* tile
) {
673 TileDrawInfo
& draw_info
= tile
->draw_info();
674 if (draw_info
.resource_
)
675 resource_pool_
->ReleaseResource(draw_info
.resource_
.Pass());
678 void TileManager::FreeResourcesForTileAndNotifyClientIfTileWasReadyToDraw(
680 bool was_ready_to_draw
= tile
->IsReadyToDraw();
681 FreeResourcesForTile(tile
);
682 if (was_ready_to_draw
)
683 client_
->NotifyTileStateChanged(tile
);
686 void TileManager::ScheduleTasks(
687 const TileVector
& tiles_that_need_to_be_rasterized
) {
689 "TileManager::ScheduleTasks",
691 tiles_that_need_to_be_rasterized
.size());
693 DCHECK(did_check_for_completed_tasks_since_last_schedule_tasks_
);
695 raster_queue_
.Reset();
697 // Build a new task queue containing all task currently needed. Tasks
698 // are added in order of priority, highest priority task first.
699 for (TileVector::const_iterator it
= tiles_that_need_to_be_rasterized
.begin();
700 it
!= tiles_that_need_to_be_rasterized
.end();
703 TileDrawInfo
& draw_info
= tile
->draw_info();
705 DCHECK(draw_info
.requires_resource());
706 DCHECK(!draw_info
.resource_
);
708 if (!tile
->raster_task_
.get())
709 tile
->raster_task_
= CreateRasterTask(tile
);
711 TaskSetCollection task_sets
;
712 if (tile
->required_for_activation())
713 task_sets
.set(REQUIRED_FOR_ACTIVATION
);
714 if (tile
->required_for_draw())
715 task_sets
.set(REQUIRED_FOR_DRAW
);
717 raster_queue_
.items
.push_back(
718 TileTaskQueue::Item(tile
->raster_task_
.get(), task_sets
));
721 // We must reduce the amount of unused resoruces before calling
722 // ScheduleTasks to prevent usage from rising above limits.
723 resource_pool_
->ReduceResourceUsage();
725 // Schedule running of |raster_queue_|. This replaces any previously
726 // scheduled tasks and effectively cancels all tasks not present
727 // in |raster_queue_|.
728 tile_task_runner_
->ScheduleTasks(&raster_queue_
);
730 // It's now safe to clean up orphan tasks as raster worker pool is not
731 // allowed to keep around unreferenced raster tasks after ScheduleTasks() has
733 orphan_raster_tasks_
.clear();
735 did_check_for_completed_tasks_since_last_schedule_tasks_
= false;
738 scoped_refptr
<ImageDecodeTask
> TileManager::CreateImageDecodeTask(
740 SkPixelRef
* pixel_ref
) {
741 return make_scoped_refptr(new ImageDecodeTaskImpl(
743 base::Bind(&TileManager::OnImageDecodeTaskCompleted
,
744 base::Unretained(this),
746 base::Unretained(pixel_ref
))));
749 scoped_refptr
<RasterTask
> TileManager::CreateRasterTask(Tile
* tile
) {
750 scoped_ptr
<ScopedResource
> resource
=
751 resource_pool_
->AcquireResource(tile
->desired_texture_size(),
752 tile_task_runner_
->GetResourceFormat());
753 const ScopedResource
* const_resource
= resource
.get();
755 // Create and queue all image decode tasks that this tile depends on.
756 ImageDecodeTask::Vector decode_tasks
;
757 PixelRefTaskMap
& existing_pixel_refs
= image_decode_tasks_
[tile
->layer_id()];
758 std::vector
<SkPixelRef
*> pixel_refs
;
759 tile
->raster_source()->GatherPixelRefs(
760 tile
->content_rect(), tile
->contents_scale(), &pixel_refs
);
761 for (SkPixelRef
* pixel_ref
: pixel_refs
) {
762 uint32_t id
= pixel_ref
->getGenerationID();
764 // Append existing image decode task if available.
765 PixelRefTaskMap::iterator decode_task_it
= existing_pixel_refs
.find(id
);
766 if (decode_task_it
!= existing_pixel_refs
.end()) {
767 decode_tasks
.push_back(decode_task_it
->second
);
771 // Create and append new image decode task for this pixel ref.
772 scoped_refptr
<ImageDecodeTask
> decode_task
=
773 CreateImageDecodeTask(tile
, pixel_ref
);
774 decode_tasks
.push_back(decode_task
);
775 existing_pixel_refs
[id
] = decode_task
;
778 return make_scoped_refptr(new RasterTaskImpl(
779 const_resource
, tile
->raster_source(), tile
->content_rect(),
780 tile
->contents_scale(), tile
->combined_priority().resolution
,
781 tile
->layer_id(), static_cast<const void*>(tile
),
782 tile
->source_frame_number(), tile
->use_picture_analysis(),
783 base::Bind(&TileManager::OnRasterTaskCompleted
, base::Unretained(this),
784 tile
->id(), base::Passed(&resource
)),
788 void TileManager::OnImageDecodeTaskCompleted(int layer_id
,
789 SkPixelRef
* pixel_ref
,
791 // If the task was canceled, we need to clean it up
792 // from |image_decode_tasks_|.
796 LayerPixelRefTaskMap::iterator layer_it
= image_decode_tasks_
.find(layer_id
);
797 if (layer_it
== image_decode_tasks_
.end())
800 PixelRefTaskMap
& pixel_ref_tasks
= layer_it
->second
;
801 PixelRefTaskMap::iterator task_it
=
802 pixel_ref_tasks
.find(pixel_ref
->getGenerationID());
804 if (task_it
!= pixel_ref_tasks
.end())
805 pixel_ref_tasks
.erase(task_it
);
808 void TileManager::OnRasterTaskCompleted(
810 scoped_ptr
<ScopedResource
> resource
,
811 const RasterSource::SolidColorAnalysis
& analysis
,
813 DCHECK(tiles_
.find(tile_id
) != tiles_
.end());
815 Tile
* tile
= tiles_
[tile_id
];
816 DCHECK(tile
->raster_task_
.get());
817 orphan_raster_tasks_
.push_back(tile
->raster_task_
);
818 tile
->raster_task_
= nullptr;
821 ++update_visible_tiles_stats_
.canceled_count
;
822 resource_pool_
->ReleaseResource(resource
.Pass());
826 UpdateTileDrawInfo(tile
, resource
.Pass(), analysis
);
829 void TileManager::UpdateTileDrawInfo(
831 scoped_ptr
<ScopedResource
> resource
,
832 const RasterSource::SolidColorAnalysis
& analysis
) {
833 TileDrawInfo
& draw_info
= tile
->draw_info();
835 ++update_visible_tiles_stats_
.completed_count
;
837 if (analysis
.is_solid_color
) {
838 draw_info
.set_solid_color(analysis
.solid_color
);
840 resource_pool_
->ReleaseResource(resource
.Pass());
843 draw_info
.set_use_resource();
844 draw_info
.resource_
= resource
.Pass();
847 client_
->NotifyTileStateChanged(tile
);
850 scoped_refptr
<Tile
> TileManager::CreateTile(
851 RasterSource
* raster_source
,
852 const gfx::Size
& desired_texture_size
,
853 const gfx::Rect
& content_rect
,
854 float contents_scale
,
856 int source_frame_number
,
858 scoped_refptr
<Tile
> tile
= make_scoped_refptr(
859 new Tile(this, raster_source
, desired_texture_size
, content_rect
,
860 contents_scale
, layer_id
, source_frame_number
, flags
));
861 DCHECK(tiles_
.find(tile
->id()) == tiles_
.end());
863 tiles_
[tile
->id()] = tile
.get();
864 used_layer_counts_
[tile
->layer_id()]++;
868 void TileManager::SetTileTaskRunnerForTesting(
869 TileTaskRunner
* tile_task_runner
) {
870 tile_task_runner_
= tile_task_runner
;
871 tile_task_runner_
->SetClient(this);
874 bool TileManager::AreRequiredTilesReadyToDraw(
875 RasterTilePriorityQueue::Type type
) const {
876 scoped_ptr
<RasterTilePriorityQueue
> raster_priority_queue(
877 client_
->BuildRasterQueue(global_state_
.tree_priority
, type
));
878 // It is insufficient to check whether the raster queue we constructed is
879 // empty. The reason for this is that there are situations (rasterize on
880 // demand) when the tile both needs raster and it's ready to draw. Hence, we
881 // have to iterate the queue to check whether the required tiles are ready to
883 for (; !raster_priority_queue
->IsEmpty(); raster_priority_queue
->Pop()) {
884 if (!raster_priority_queue
->Top()->IsReadyToDraw())
889 bool TileManager::IsReadyToActivate() const {
890 TRACE_EVENT0("cc", "TileManager::IsReadyToActivate");
891 return AreRequiredTilesReadyToDraw(
892 RasterTilePriorityQueue::Type::REQUIRED_FOR_ACTIVATION
);
895 bool TileManager::IsReadyToDraw() const {
896 TRACE_EVENT0("cc", "TileManager::IsReadyToDraw");
897 return AreRequiredTilesReadyToDraw(
898 RasterTilePriorityQueue::Type::REQUIRED_FOR_DRAW
);
901 void TileManager::NotifyReadyToActivate() {
902 TRACE_EVENT0("cc", "TileManager::NotifyReadyToActivate");
903 if (did_notify_ready_to_activate_
)
905 client_
->NotifyReadyToActivate();
906 did_notify_ready_to_activate_
= true;
909 void TileManager::NotifyReadyToDraw() {
910 TRACE_EVENT0("cc", "TileManager::NotifyReadyToDraw");
911 if (did_notify_ready_to_draw_
)
913 client_
->NotifyReadyToDraw();
914 did_notify_ready_to_draw_
= true;
917 void TileManager::CheckIfReadyToActivate() {
918 TRACE_EVENT0("cc", "TileManager::CheckIfReadyToActivate");
920 tile_task_runner_
->CheckForCompletedTasks();
921 did_check_for_completed_tasks_since_last_schedule_tasks_
= true;
923 if (did_notify_ready_to_activate_
)
925 if (!IsReadyToActivate())
928 NotifyReadyToActivate();
931 void TileManager::CheckIfReadyToDraw() {
932 TRACE_EVENT0("cc", "TileManager::CheckIfReadyToDraw");
934 tile_task_runner_
->CheckForCompletedTasks();
935 did_check_for_completed_tasks_since_last_schedule_tasks_
= true;
937 if (did_notify_ready_to_draw_
)
939 if (!IsReadyToDraw())
945 void TileManager::CheckIfMoreTilesNeedToBePrepared() {
946 tile_task_runner_
->CheckForCompletedTasks();
947 did_check_for_completed_tasks_since_last_schedule_tasks_
= true;
949 // When OOM, keep re-assigning memory until we reach a steady state
950 // where top-priority tiles are initialized.
951 TileVector tiles_that_need_to_be_rasterized
;
952 scoped_ptr
<RasterTilePriorityQueue
> raster_priority_queue(
953 client_
->BuildRasterQueue(global_state_
.tree_priority
,
954 RasterTilePriorityQueue::Type::ALL
));
955 AssignGpuMemoryToTiles(raster_priority_queue
.get(),
956 scheduled_raster_task_limit_
,
957 &tiles_that_need_to_be_rasterized
);
959 // |tiles_that_need_to_be_rasterized| will be empty when we reach a
960 // steady memory state. Keep scheduling tasks until we reach this state.
961 if (!tiles_that_need_to_be_rasterized
.empty()) {
962 ScheduleTasks(tiles_that_need_to_be_rasterized
);
966 FreeResourcesForReleasedTiles();
968 resource_pool_
->ReduceResourceUsage();
970 // We don't reserve memory for required-for-activation tiles during
971 // accelerated gestures, so we just postpone activation when we don't
972 // have these tiles, and activate after the accelerated gesture.
973 // Likewise if we don't allow any tiles (as is the case when we're
974 // invisible), if we have tiles that aren't ready, then we shouldn't
975 // activate as activation can cause checkerboards.
976 bool wait_for_all_required_tiles
=
977 global_state_
.tree_priority
== SMOOTHNESS_TAKES_PRIORITY
||
978 global_state_
.memory_limit_policy
== ALLOW_NOTHING
;
980 // Mark any required-for-activation tiles that have not been been assigned
981 // memory after reaching a steady memory state as OOM. This ensures that we
982 // activate even when OOM. Note that we can't reuse the queue we used for
983 // AssignGpuMemoryToTiles, since the AssignGpuMemoryToTiles call could have
984 // evicted some tiles that would not be picked up by the old raster queue.
985 scoped_ptr
<RasterTilePriorityQueue
> required_for_activation_queue(
986 client_
->BuildRasterQueue(
987 global_state_
.tree_priority
,
988 RasterTilePriorityQueue::Type::REQUIRED_FOR_ACTIVATION
));
990 // If we have tiles left to raster for activation, and we don't allow
991 // activating without them, then skip activation and return early.
992 if (!required_for_activation_queue
->IsEmpty() && wait_for_all_required_tiles
)
995 // Mark required tiles as OOM so that we can activate without them.
996 for (; !required_for_activation_queue
->IsEmpty();
997 required_for_activation_queue
->Pop()) {
998 Tile
* tile
= required_for_activation_queue
->Top();
999 tile
->draw_info().set_oom();
1000 client_
->NotifyTileStateChanged(tile
);
1003 DCHECK(IsReadyToActivate());
1004 ready_to_activate_check_notifier_
.Schedule();
1007 TileManager::MemoryUsage::MemoryUsage() : memory_bytes_(0), resource_count_(0) {
1010 TileManager::MemoryUsage::MemoryUsage(int64 memory_bytes
, int resource_count
)
1011 : memory_bytes_(memory_bytes
), resource_count_(resource_count
) {
1015 TileManager::MemoryUsage
TileManager::MemoryUsage::FromConfig(
1016 const gfx::Size
& size
,
1017 ResourceFormat format
) {
1018 return MemoryUsage(Resource::MemorySizeBytes(size
, format
), 1);
1022 TileManager::MemoryUsage
TileManager::MemoryUsage::FromTile(const Tile
* tile
) {
1023 const TileDrawInfo
& draw_info
= tile
->draw_info();
1024 if (draw_info
.resource_
) {
1025 return MemoryUsage::FromConfig(draw_info
.resource_
->size(),
1026 draw_info
.resource_
->format());
1028 return MemoryUsage();
1031 TileManager::MemoryUsage
& TileManager::MemoryUsage::operator+=(
1032 const MemoryUsage
& other
) {
1033 memory_bytes_
+= other
.memory_bytes_
;
1034 resource_count_
+= other
.resource_count_
;
1038 TileManager::MemoryUsage
& TileManager::MemoryUsage::operator-=(
1039 const MemoryUsage
& other
) {
1040 memory_bytes_
-= other
.memory_bytes_
;
1041 resource_count_
-= other
.resource_count_
;
1045 TileManager::MemoryUsage
TileManager::MemoryUsage::operator-(
1046 const MemoryUsage
& other
) {
1047 MemoryUsage result
= *this;
1052 bool TileManager::MemoryUsage::Exceeds(const MemoryUsage
& limit
) const {
1053 return memory_bytes_
> limit
.memory_bytes_
||
1054 resource_count_
> limit
.resource_count_
;