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/tiles/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/numerics/safe_conversions.h"
16 #include "base/trace_event/trace_event_argument.h"
17 #include "cc/base/histograms.h"
18 #include "cc/debug/devtools_instrumentation.h"
19 #include "cc/debug/frame_viewer_instrumentation.h"
20 #include "cc/debug/traced_value.h"
21 #include "cc/layers/picture_layer_impl.h"
22 #include "cc/raster/raster_buffer.h"
23 #include "cc/raster/tile_task_runner.h"
24 #include "cc/tiles/tile.h"
25 #include "ui/gfx/geometry/rect_conversions.h"
30 // Flag to indicate whether we should try and detect that
31 // a tile is of solid color.
32 const bool kUseColorEstimator
= true;
34 DEFINE_SCOPED_UMA_HISTOGRAM_AREA_TIMER(
35 ScopedRasterTaskTimer
,
36 "Compositing.RasterTask.RasterUs",
37 "Compositing.RasterTask.RasterPixelsPerMs");
39 class RasterTaskImpl
: public RasterTask
{
42 const Resource
* resource
,
43 RasterSource
* raster_source
,
44 const gfx::Rect
& content_rect
,
45 const gfx::Rect
& invalid_content_rect
,
47 TileResolution tile_resolution
,
49 uint64_t source_prepare_tiles_id
,
51 uint64_t new_content_id
,
52 uint64_t previous_content_id
,
53 uint64_t resource_content_id
,
54 int source_frame_number
,
56 const base::Callback
<void(const RasterSource::SolidColorAnalysis
&, bool)>&
58 ImageDecodeTask::Vector
* dependencies
)
59 : RasterTask(resource
, dependencies
),
60 raster_source_(raster_source
),
61 content_rect_(content_rect
),
62 invalid_content_rect_(invalid_content_rect
),
63 contents_scale_(contents_scale
),
64 tile_resolution_(tile_resolution
),
66 source_prepare_tiles_id_(source_prepare_tiles_id
),
68 new_content_id_(new_content_id
),
69 previous_content_id_(previous_content_id
),
70 resource_content_id_(resource_content_id
),
71 source_frame_number_(source_frame_number
),
72 analyze_picture_(analyze_picture
),
75 // Overridden from Task:
76 void RunOnWorkerThread() override
{
77 TRACE_EVENT1("cc", "RasterizerTaskImpl::RunOnWorkerThread",
78 "source_prepare_tiles_id", source_prepare_tiles_id_
);
80 DCHECK(raster_source_
.get());
81 DCHECK(raster_buffer_
);
83 if (analyze_picture_
) {
84 Analyze(raster_source_
.get());
85 if (analysis_
.is_solid_color
)
89 Raster(raster_source_
.get());
92 // Overridden from TileTask:
93 void ScheduleOnOriginThread(TileTaskClient
* client
) override
{
94 DCHECK(!raster_buffer_
);
95 raster_buffer_
= client
->AcquireBufferForRaster(
96 resource(), resource_content_id_
, previous_content_id_
);
98 void CompleteOnOriginThread(TileTaskClient
* client
) override
{
99 client
->ReleaseBufferForRaster(raster_buffer_
.Pass());
101 void RunReplyOnOriginThread() override
{
102 DCHECK(!raster_buffer_
);
103 reply_
.Run(analysis_
, !HasFinishedRunning());
107 ~RasterTaskImpl() override
{ DCHECK(!raster_buffer_
); }
110 void Analyze(const RasterSource
* raster_source
) {
111 frame_viewer_instrumentation::ScopedAnalyzeTask
analyze_task(
112 tile_
, tile_resolution_
, source_frame_number_
, layer_id_
);
114 DCHECK(raster_source
);
116 raster_source
->PerformSolidColorAnalysis(content_rect_
, contents_scale_
,
118 // Clear the flag if we're not using the estimator.
119 analysis_
.is_solid_color
&= kUseColorEstimator
;
122 void Raster(const RasterSource
* raster_source
) {
123 frame_viewer_instrumentation::ScopedRasterTask
raster_task(
124 tile_
, tile_resolution_
, source_frame_number_
, layer_id_
);
125 ScopedRasterTaskTimer timer
;
126 timer
.SetArea(content_rect_
.size().GetArea());
128 DCHECK(raster_source
);
130 bool include_images
= tile_resolution_
!= LOW_RESOLUTION
;
131 raster_buffer_
->Playback(raster_source_
.get(), content_rect_
,
132 invalid_content_rect_
, new_content_id_
,
133 contents_scale_
, include_images
);
136 RasterSource::SolidColorAnalysis analysis_
;
137 scoped_refptr
<RasterSource
> raster_source_
;
138 gfx::Rect content_rect_
;
139 gfx::Rect invalid_content_rect_
;
140 float contents_scale_
;
141 TileResolution tile_resolution_
;
143 uint64_t source_prepare_tiles_id_
;
145 uint64_t new_content_id_
;
146 uint64_t previous_content_id_
;
147 uint64_t resource_content_id_
;
148 int source_frame_number_
;
149 bool analyze_picture_
;
150 const base::Callback
<void(const RasterSource::SolidColorAnalysis
&, bool)>
152 scoped_ptr
<RasterBuffer
> raster_buffer_
;
154 DISALLOW_COPY_AND_ASSIGN(RasterTaskImpl
);
157 const char* TaskSetName(TaskSet task_set
) {
159 case TileManager::ALL
:
161 case TileManager::REQUIRED_FOR_ACTIVATION
:
162 return "REQUIRED_FOR_ACTIVATION";
163 case TileManager::REQUIRED_FOR_DRAW
:
164 return "REQUIRED_FOR_DRAW";
168 return "Invalid TaskSet";
173 RasterTaskCompletionStats::RasterTaskCompletionStats()
174 : completed_count(0u), canceled_count(0u) {}
176 scoped_refptr
<base::trace_event::ConvertableToTraceFormat
>
177 RasterTaskCompletionStatsAsValue(const RasterTaskCompletionStats
& stats
) {
178 scoped_refptr
<base::trace_event::TracedValue
> state
=
179 new base::trace_event::TracedValue();
180 state
->SetInteger("completed_count",
181 base::saturated_cast
<int>(stats
.completed_count
));
182 state
->SetInteger("canceled_count",
183 base::saturated_cast
<int>(stats
.canceled_count
));
188 scoped_ptr
<TileManager
> TileManager::Create(
189 TileManagerClient
* client
,
190 base::SequencedTaskRunner
* task_runner
,
191 size_t scheduled_raster_task_limit
) {
192 return make_scoped_ptr(
193 new TileManager(client
, task_runner
, scheduled_raster_task_limit
));
196 TileManager::TileManager(
197 TileManagerClient
* client
,
198 const scoped_refptr
<base::SequencedTaskRunner
>& task_runner
,
199 size_t scheduled_raster_task_limit
)
201 task_runner_(task_runner
),
202 resource_pool_(nullptr),
203 tile_task_runner_(nullptr),
204 scheduled_raster_task_limit_(scheduled_raster_task_limit
),
205 all_tiles_that_need_to_be_rasterized_are_scheduled_(true),
206 did_check_for_completed_tasks_since_last_schedule_tasks_(true),
207 did_oom_on_last_assign_(false),
208 more_tiles_need_prepare_check_notifier_(
210 base::Bind(&TileManager::CheckIfMoreTilesNeedToBePrepared
,
211 base::Unretained(this))),
212 signals_check_notifier_(task_runner_
.get(),
213 base::Bind(&TileManager::CheckAndIssueSignals
,
214 base::Unretained(this))),
215 has_scheduled_tile_tasks_(false),
216 prepare_tiles_count_(0u) {
219 TileManager::~TileManager() {
220 FinishTasksAndCleanUp();
223 void TileManager::FinishTasksAndCleanUp() {
224 if (!tile_task_runner_
)
227 global_state_
= GlobalStateThatImpactsTilePriority();
230 tile_task_runner_
->ScheduleTasks(&empty
);
231 orphan_raster_tasks_
.clear();
233 // This should finish all pending tasks and release any uninitialized
235 tile_task_runner_
->Shutdown();
236 tile_task_runner_
->CheckForCompletedTasks();
238 FreeResourcesForReleasedTiles();
239 CleanUpReleasedTiles();
241 tile_task_runner_
= nullptr;
242 resource_pool_
= nullptr;
243 more_tiles_need_prepare_check_notifier_
.Cancel();
244 signals_check_notifier_
.Cancel();
247 void TileManager::SetResources(ResourcePool
* resource_pool
,
248 TileTaskRunner
* tile_task_runner
,
249 size_t scheduled_raster_task_limit
) {
250 DCHECK(!tile_task_runner_
);
251 DCHECK(tile_task_runner
);
253 scheduled_raster_task_limit_
= scheduled_raster_task_limit
;
254 resource_pool_
= resource_pool
;
255 tile_task_runner_
= tile_task_runner
;
256 tile_task_runner_
->SetClient(this);
259 void TileManager::Release(Tile
* tile
) {
260 released_tiles_
.push_back(tile
);
263 TaskSetCollection
TileManager::TasksThatShouldBeForcedToComplete() const {
264 TaskSetCollection tasks_that_should_be_forced_to_complete
;
265 if (global_state_
.tree_priority
!= SMOOTHNESS_TAKES_PRIORITY
)
266 tasks_that_should_be_forced_to_complete
[REQUIRED_FOR_ACTIVATION
] = true;
267 return tasks_that_should_be_forced_to_complete
;
270 void TileManager::FreeResourcesForReleasedTiles() {
271 for (auto* tile
: released_tiles_
)
272 FreeResourcesForTile(tile
);
275 void TileManager::CleanUpReleasedTiles() {
276 std::vector
<Tile
*> tiles_to_retain
;
277 for (auto* tile
: released_tiles_
) {
278 if (tile
->HasRasterTask()) {
279 tiles_to_retain
.push_back(tile
);
283 DCHECK(!tile
->draw_info().has_resource());
284 DCHECK(tiles_
.find(tile
->id()) != tiles_
.end());
285 tiles_
.erase(tile
->id());
287 image_decode_controller_
.SubtractLayerUsedCount(tile
->layer_id());
290 released_tiles_
.swap(tiles_to_retain
);
293 void TileManager::DidFinishRunningTileTasks(TaskSet task_set
) {
294 TRACE_EVENT1("cc", "TileManager::DidFinishRunningTileTasks", "task_set",
295 TaskSetName(task_set
));
296 DCHECK(resource_pool_
);
297 DCHECK(tile_task_runner_
);
301 has_scheduled_tile_tasks_
= false;
303 bool memory_usage_above_limit
= resource_pool_
->memory_usage_bytes() >
304 global_state_
.soft_memory_limit_in_bytes
;
306 if (all_tiles_that_need_to_be_rasterized_are_scheduled_
&&
307 !memory_usage_above_limit
) {
308 // TODO(ericrk): We should find a better way to safely handle re-entrant
309 // notifications than always having to schedule a new task.
310 // http://crbug.com/498439
311 signals_
.all_tile_tasks_completed
= true;
312 signals_check_notifier_
.Schedule();
316 more_tiles_need_prepare_check_notifier_
.Schedule();
319 case REQUIRED_FOR_ACTIVATION
:
320 signals_
.ready_to_activate
= true;
321 signals_check_notifier_
.Schedule();
324 case REQUIRED_FOR_DRAW
:
325 signals_
.ready_to_draw
= true;
326 signals_check_notifier_
.Schedule();
333 bool TileManager::PrepareTiles(
334 const GlobalStateThatImpactsTilePriority
& state
) {
335 ++prepare_tiles_count_
;
337 TRACE_EVENT1("cc", "TileManager::PrepareTiles", "prepare_tiles_id",
338 prepare_tiles_count_
);
340 if (!tile_task_runner_
) {
341 TRACE_EVENT_INSTANT0("cc", "PrepareTiles aborted",
342 TRACE_EVENT_SCOPE_THREAD
);
347 global_state_
= state
;
349 // We need to call CheckForCompletedTasks() once in-between each call
350 // to ScheduleTasks() to prevent canceled tasks from being scheduled.
351 if (!did_check_for_completed_tasks_since_last_schedule_tasks_
) {
352 tile_task_runner_
->CheckForCompletedTasks();
353 did_check_for_completed_tasks_since_last_schedule_tasks_
= true;
356 FreeResourcesForReleasedTiles();
357 CleanUpReleasedTiles();
359 PrioritizedTileVector tiles_that_need_to_be_rasterized
;
360 scoped_ptr
<RasterTilePriorityQueue
> raster_priority_queue(
361 client_
->BuildRasterQueue(global_state_
.tree_priority
,
362 RasterTilePriorityQueue::Type::ALL
));
363 AssignGpuMemoryToTiles(raster_priority_queue
.get(),
364 scheduled_raster_task_limit_
,
365 &tiles_that_need_to_be_rasterized
);
367 // Inform the client that will likely require a draw if the highest priority
368 // tile that will be rasterized is required for draw.
369 client_
->SetIsLikelyToRequireADraw(
370 !tiles_that_need_to_be_rasterized
.empty() &&
371 tiles_that_need_to_be_rasterized
.front().tile()->required_for_draw());
373 // Schedule tile tasks.
374 ScheduleTasks(tiles_that_need_to_be_rasterized
);
376 TRACE_EVENT_INSTANT1("cc", "DidPrepareTiles", TRACE_EVENT_SCOPE_THREAD
,
377 "state", BasicStateAsValue());
381 void TileManager::Flush() {
382 TRACE_EVENT0("cc", "TileManager::Flush");
384 if (!tile_task_runner_
) {
385 TRACE_EVENT_INSTANT0("cc", "Flush aborted", TRACE_EVENT_SCOPE_THREAD
);
389 tile_task_runner_
->CheckForCompletedTasks();
391 did_check_for_completed_tasks_since_last_schedule_tasks_
= true;
393 TRACE_EVENT_INSTANT1("cc", "DidFlush", TRACE_EVENT_SCOPE_THREAD
, "stats",
394 RasterTaskCompletionStatsAsValue(flush_stats_
));
395 flush_stats_
= RasterTaskCompletionStats();
398 scoped_refptr
<base::trace_event::ConvertableToTraceFormat
>
399 TileManager::BasicStateAsValue() const {
400 scoped_refptr
<base::trace_event::TracedValue
> value
=
401 new base::trace_event::TracedValue();
402 BasicStateAsValueInto(value
.get());
406 void TileManager::BasicStateAsValueInto(
407 base::trace_event::TracedValue
* state
) const {
408 state
->SetInteger("tile_count", base::saturated_cast
<int>(tiles_
.size()));
409 state
->SetBoolean("did_oom_on_last_assign", did_oom_on_last_assign_
);
410 state
->BeginDictionary("global_state");
411 global_state_
.AsValueInto(state
);
412 state
->EndDictionary();
415 scoped_ptr
<EvictionTilePriorityQueue
>
416 TileManager::FreeTileResourcesUntilUsageIsWithinLimit(
417 scoped_ptr
<EvictionTilePriorityQueue
> eviction_priority_queue
,
418 const MemoryUsage
& limit
,
419 MemoryUsage
* usage
) {
420 while (usage
->Exceeds(limit
)) {
421 if (!eviction_priority_queue
) {
422 eviction_priority_queue
=
423 client_
->BuildEvictionQueue(global_state_
.tree_priority
);
425 if (eviction_priority_queue
->IsEmpty())
428 Tile
* tile
= eviction_priority_queue
->Top().tile();
429 *usage
-= MemoryUsage::FromTile(tile
);
430 FreeResourcesForTileAndNotifyClientIfTileWasReadyToDraw(tile
);
431 eviction_priority_queue
->Pop();
433 return eviction_priority_queue
;
436 scoped_ptr
<EvictionTilePriorityQueue
>
437 TileManager::FreeTileResourcesWithLowerPriorityUntilUsageIsWithinLimit(
438 scoped_ptr
<EvictionTilePriorityQueue
> eviction_priority_queue
,
439 const MemoryUsage
& limit
,
440 const TilePriority
& other_priority
,
441 MemoryUsage
* usage
) {
442 while (usage
->Exceeds(limit
)) {
443 if (!eviction_priority_queue
) {
444 eviction_priority_queue
=
445 client_
->BuildEvictionQueue(global_state_
.tree_priority
);
447 if (eviction_priority_queue
->IsEmpty())
450 const PrioritizedTile
& prioritized_tile
= eviction_priority_queue
->Top();
451 if (!other_priority
.IsHigherPriorityThan(prioritized_tile
.priority()))
454 Tile
* tile
= prioritized_tile
.tile();
455 *usage
-= MemoryUsage::FromTile(tile
);
456 FreeResourcesForTileAndNotifyClientIfTileWasReadyToDraw(tile
);
457 eviction_priority_queue
->Pop();
459 return eviction_priority_queue
;
462 bool TileManager::TilePriorityViolatesMemoryPolicy(
463 const TilePriority
& priority
) {
464 switch (global_state_
.memory_limit_policy
) {
467 case ALLOW_ABSOLUTE_MINIMUM
:
468 return priority
.priority_bin
> TilePriority::NOW
;
469 case ALLOW_PREPAINT_ONLY
:
470 return priority
.priority_bin
> TilePriority::SOON
;
472 return priority
.distance_to_visible
==
473 std::numeric_limits
<float>::infinity();
479 void TileManager::AssignGpuMemoryToTiles(
480 RasterTilePriorityQueue
* raster_priority_queue
,
481 size_t scheduled_raster_task_limit
,
482 PrioritizedTileVector
* tiles_that_need_to_be_rasterized
) {
483 TRACE_EVENT_BEGIN0("cc", "TileManager::AssignGpuMemoryToTiles");
485 DCHECK(resource_pool_
);
486 DCHECK(tile_task_runner_
);
488 // Maintain the list of released resources that can potentially be re-used
489 // or deleted. If this operation becomes expensive too, only do this after
490 // some resource(s) was returned. Note that in that case, one also need to
491 // invalidate when releasing some resource from the pool.
492 resource_pool_
->CheckBusyResources();
494 // Now give memory out to the tiles until we're out, and build
495 // the needs-to-be-rasterized queue.
496 unsigned schedule_priority
= 1u;
497 all_tiles_that_need_to_be_rasterized_are_scheduled_
= true;
498 bool had_enough_memory_to_schedule_tiles_needed_now
= true;
500 MemoryUsage
hard_memory_limit(global_state_
.hard_memory_limit_in_bytes
,
501 global_state_
.num_resources_limit
);
502 MemoryUsage
soft_memory_limit(global_state_
.soft_memory_limit_in_bytes
,
503 global_state_
.num_resources_limit
);
504 MemoryUsage
memory_usage(resource_pool_
->memory_usage_bytes(),
505 resource_pool_
->resource_count());
507 scoped_ptr
<EvictionTilePriorityQueue
> eviction_priority_queue
;
508 for (; !raster_priority_queue
->IsEmpty(); raster_priority_queue
->Pop()) {
509 const PrioritizedTile
& prioritized_tile
= raster_priority_queue
->Top();
510 Tile
* tile
= prioritized_tile
.tile();
511 TilePriority priority
= prioritized_tile
.priority();
513 if (TilePriorityViolatesMemoryPolicy(priority
)) {
514 TRACE_EVENT_INSTANT0(
515 "cc", "TileManager::AssignGpuMemory tile violates memory policy",
516 TRACE_EVENT_SCOPE_THREAD
);
520 // We won't be able to schedule this tile, so break out early.
521 if (tiles_that_need_to_be_rasterized
->size() >=
522 scheduled_raster_task_limit
) {
523 all_tiles_that_need_to_be_rasterized_are_scheduled_
= false;
527 tile
->scheduled_priority_
= schedule_priority
++;
529 DCHECK_IMPLIES(tile
->draw_info().mode() != TileDrawInfo::OOM_MODE
,
530 !tile
->draw_info().IsReadyToDraw());
532 // If the tile already has a raster_task, then the memory used by it is
533 // already accounted for in memory_usage. Otherwise, we'll have to acquire
534 // more memory to create a raster task.
535 MemoryUsage memory_required_by_tile_to_be_scheduled
;
536 if (!tile
->raster_task_
.get()) {
537 memory_required_by_tile_to_be_scheduled
= MemoryUsage::FromConfig(
538 tile
->desired_texture_size(), tile_task_runner_
->GetResourceFormat());
541 bool tile_is_needed_now
= priority
.priority_bin
== TilePriority::NOW
;
543 // This is the memory limit that will be used by this tile. Depending on
544 // the tile priority, it will be one of hard_memory_limit or
545 // soft_memory_limit.
546 MemoryUsage
& tile_memory_limit
=
547 tile_is_needed_now
? hard_memory_limit
: soft_memory_limit
;
549 const MemoryUsage
& scheduled_tile_memory_limit
=
550 tile_memory_limit
- memory_required_by_tile_to_be_scheduled
;
551 eviction_priority_queue
=
552 FreeTileResourcesWithLowerPriorityUntilUsageIsWithinLimit(
553 eviction_priority_queue
.Pass(), scheduled_tile_memory_limit
,
554 priority
, &memory_usage
);
555 bool memory_usage_is_within_limit
=
556 !memory_usage
.Exceeds(scheduled_tile_memory_limit
);
558 // If we couldn't fit the tile into our current memory limit, then we're
560 if (!memory_usage_is_within_limit
) {
561 if (tile_is_needed_now
)
562 had_enough_memory_to_schedule_tiles_needed_now
= false;
563 all_tiles_that_need_to_be_rasterized_are_scheduled_
= false;
567 memory_usage
+= memory_required_by_tile_to_be_scheduled
;
568 tiles_that_need_to_be_rasterized
->push_back(prioritized_tile
);
571 // Note that we should try and further reduce memory in case the above loop
572 // didn't reduce memory. This ensures that we always release as many resources
573 // as possible to stay within the memory limit.
574 eviction_priority_queue
= FreeTileResourcesUntilUsageIsWithinLimit(
575 eviction_priority_queue
.Pass(), hard_memory_limit
, &memory_usage
);
577 UMA_HISTOGRAM_BOOLEAN("TileManager.ExceededMemoryBudget",
578 !had_enough_memory_to_schedule_tiles_needed_now
);
579 did_oom_on_last_assign_
= !had_enough_memory_to_schedule_tiles_needed_now
;
581 memory_stats_from_last_assign_
.total_budget_in_bytes
=
582 global_state_
.hard_memory_limit_in_bytes
;
583 memory_stats_from_last_assign_
.total_bytes_used
= memory_usage
.memory_bytes();
584 DCHECK_GE(memory_stats_from_last_assign_
.total_bytes_used
, 0);
585 memory_stats_from_last_assign_
.had_enough_memory
=
586 had_enough_memory_to_schedule_tiles_needed_now
;
588 TRACE_EVENT_END2("cc", "TileManager::AssignGpuMemoryToTiles",
589 "all_tiles_that_need_to_be_rasterized_are_scheduled",
590 all_tiles_that_need_to_be_rasterized_are_scheduled_
,
591 "had_enough_memory_to_schedule_tiles_needed_now",
592 had_enough_memory_to_schedule_tiles_needed_now
);
595 void TileManager::FreeResourcesForTile(Tile
* tile
) {
596 TileDrawInfo
& draw_info
= tile
->draw_info();
597 if (draw_info
.resource_
) {
598 resource_pool_
->ReleaseResource(draw_info
.resource_
, tile
->id());
599 draw_info
.resource_
= nullptr;
603 void TileManager::FreeResourcesForTileAndNotifyClientIfTileWasReadyToDraw(
605 bool was_ready_to_draw
= tile
->draw_info().IsReadyToDraw();
606 FreeResourcesForTile(tile
);
607 if (was_ready_to_draw
)
608 client_
->NotifyTileStateChanged(tile
);
611 void TileManager::ScheduleTasks(
612 const PrioritizedTileVector
& tiles_that_need_to_be_rasterized
) {
614 "TileManager::ScheduleTasks",
616 tiles_that_need_to_be_rasterized
.size());
618 DCHECK(did_check_for_completed_tasks_since_last_schedule_tasks_
);
620 raster_queue_
.Reset();
622 // Even when scheduling an empty set of tiles, the TTWP does some work, and
623 // will always trigger a DidFinishRunningTileTasks notification. Because of
624 // this we unconditionally set |has_scheduled_tile_tasks_| to true.
625 has_scheduled_tile_tasks_
= true;
627 // Build a new task queue containing all task currently needed. Tasks
628 // are added in order of priority, highest priority task first.
629 for (auto& prioritized_tile
: tiles_that_need_to_be_rasterized
) {
630 Tile
* tile
= prioritized_tile
.tile();
632 DCHECK(tile
->draw_info().requires_resource());
633 DCHECK(!tile
->draw_info().resource_
);
635 if (!tile
->raster_task_
.get())
636 tile
->raster_task_
= CreateRasterTask(prioritized_tile
);
638 TaskSetCollection task_sets
;
639 if (tile
->required_for_activation())
640 task_sets
.set(REQUIRED_FOR_ACTIVATION
);
641 if (tile
->required_for_draw())
642 task_sets
.set(REQUIRED_FOR_DRAW
);
644 raster_queue_
.items
.push_back(
645 TileTaskQueue::Item(tile
->raster_task_
.get(), task_sets
));
648 // We must reduce the amount of unused resoruces before calling
649 // ScheduleTasks to prevent usage from rising above limits.
650 resource_pool_
->ReduceResourceUsage();
652 // Schedule running of |raster_queue_|. This replaces any previously
653 // scheduled tasks and effectively cancels all tasks not present
654 // in |raster_queue_|.
655 tile_task_runner_
->ScheduleTasks(&raster_queue_
);
657 // It's now safe to clean up orphan tasks as raster worker pool is not
658 // allowed to keep around unreferenced raster tasks after ScheduleTasks() has
660 orphan_raster_tasks_
.clear();
662 did_check_for_completed_tasks_since_last_schedule_tasks_
= false;
665 scoped_refptr
<RasterTask
> TileManager::CreateRasterTask(
666 const PrioritizedTile
& prioritized_tile
) {
667 Tile
* tile
= prioritized_tile
.tile();
668 uint64_t resource_content_id
= 0;
669 Resource
* resource
= nullptr;
670 if (tile
->invalidated_id()) {
671 // TODO(danakj): For resources that are in use, we should still grab them
672 // and copy from them instead of rastering everything. crbug.com/492754
674 resource_pool_
->TryAcquireResourceWithContentId(tile
->invalidated_id());
677 resource_content_id
= tile
->invalidated_id();
678 DCHECK_EQ(tile_task_runner_
->GetResourceFormat(), resource
->format());
679 DCHECK_EQ(tile
->desired_texture_size().ToString(),
680 resource
->size().ToString());
682 resource
= resource_pool_
->AcquireResource(
683 tile
->desired_texture_size(), tile_task_runner_
->GetResourceFormat());
686 // Create and queue all image decode tasks that this tile depends on.
687 ImageDecodeTask::Vector decode_tasks
;
688 std::vector
<skia::PositionPixelRef
> pixel_refs
;
689 prioritized_tile
.raster_source()->GatherPixelRefs(
690 tile
->content_rect(), tile
->contents_scale(), &pixel_refs
);
691 for (const skia::PositionPixelRef
& pixel_ref
: pixel_refs
) {
692 decode_tasks
.push_back(image_decode_controller_
.GetTaskForPixelRef(
693 pixel_ref
, tile
->layer_id(), prepare_tiles_count_
));
696 return make_scoped_refptr(new RasterTaskImpl(
697 resource
, prioritized_tile
.raster_source(), tile
->content_rect(),
698 tile
->invalidated_content_rect(), tile
->contents_scale(),
699 prioritized_tile
.priority().resolution
, tile
->layer_id(),
700 prepare_tiles_count_
, static_cast<const void*>(tile
), tile
->id(),
701 tile
->invalidated_id(), resource_content_id
, tile
->source_frame_number(),
702 tile
->use_picture_analysis(),
703 base::Bind(&TileManager::OnRasterTaskCompleted
, base::Unretained(this),
704 tile
->id(), resource
),
708 void TileManager::OnRasterTaskCompleted(
711 const RasterSource::SolidColorAnalysis
& analysis
,
713 DCHECK(tiles_
.find(tile_id
) != tiles_
.end());
715 Tile
* tile
= tiles_
[tile_id
];
716 DCHECK(tile
->raster_task_
.get());
717 orphan_raster_tasks_
.push_back(tile
->raster_task_
);
718 tile
->raster_task_
= nullptr;
721 ++flush_stats_
.canceled_count
;
722 resource_pool_
->ReleaseResource(resource
, tile
->invalidated_id());
726 UpdateTileDrawInfo(tile
, resource
, analysis
);
729 void TileManager::UpdateTileDrawInfo(
732 const RasterSource::SolidColorAnalysis
& analysis
) {
733 TileDrawInfo
& draw_info
= tile
->draw_info();
735 ++flush_stats_
.completed_count
;
737 if (analysis
.is_solid_color
) {
738 draw_info
.set_solid_color(analysis
.solid_color
);
740 // Pass the old tile id here because the tile is solid color so we did not
741 // raster anything into the tile resource.
742 resource_pool_
->ReleaseResource(resource
, tile
->invalidated_id());
746 draw_info
.set_use_resource();
747 draw_info
.resource_
= resource
;
748 draw_info
.contents_swizzled_
=
749 tile_task_runner_
->GetResourceRequiresSwizzle();
751 DCHECK(draw_info
.IsReadyToDraw());
752 draw_info
.set_was_ever_ready_to_draw();
754 client_
->NotifyTileStateChanged(tile
);
757 ScopedTilePtr
TileManager::CreateTile(const gfx::Size
& desired_texture_size
,
758 const gfx::Rect
& content_rect
,
759 float contents_scale
,
761 int source_frame_number
,
763 // We need to have a tile task worker pool to do anything meaningful with
765 DCHECK(tile_task_runner_
);
766 ScopedTilePtr
tile(new Tile(this, desired_texture_size
, content_rect
,
767 contents_scale
, layer_id
, source_frame_number
,
769 DCHECK(tiles_
.find(tile
->id()) == tiles_
.end());
771 tiles_
[tile
->id()] = tile
.get();
772 image_decode_controller_
.AddLayerUsedCount(tile
->layer_id());
776 void TileManager::SetTileTaskRunnerForTesting(
777 TileTaskRunner
* tile_task_runner
) {
778 tile_task_runner_
= tile_task_runner
;
779 tile_task_runner_
->SetClient(this);
782 bool TileManager::AreRequiredTilesReadyToDraw(
783 RasterTilePriorityQueue::Type type
) const {
784 scoped_ptr
<RasterTilePriorityQueue
> raster_priority_queue(
785 client_
->BuildRasterQueue(global_state_
.tree_priority
, type
));
786 // It is insufficient to check whether the raster queue we constructed is
787 // empty. The reason for this is that there are situations (rasterize on
788 // demand) when the tile both needs raster and it's ready to draw. Hence, we
789 // have to iterate the queue to check whether the required tiles are ready to
791 for (; !raster_priority_queue
->IsEmpty(); raster_priority_queue
->Pop()) {
792 if (!raster_priority_queue
->Top().tile()->draw_info().IsReadyToDraw())
797 scoped_ptr
<RasterTilePriorityQueue
> all_queue(
798 client_
->BuildRasterQueue(global_state_
.tree_priority
, type
));
799 for (; !all_queue
->IsEmpty(); all_queue
->Pop()) {
800 Tile
* tile
= all_queue
->Top().tile();
801 DCHECK_IMPLIES(tile
->required_for_activation(),
802 tile
->draw_info().IsReadyToDraw());
808 bool TileManager::IsReadyToActivate() const {
809 TRACE_EVENT0("cc", "TileManager::IsReadyToActivate");
810 return AreRequiredTilesReadyToDraw(
811 RasterTilePriorityQueue::Type::REQUIRED_FOR_ACTIVATION
);
814 bool TileManager::IsReadyToDraw() const {
815 TRACE_EVENT0("cc", "TileManager::IsReadyToDraw");
816 return AreRequiredTilesReadyToDraw(
817 RasterTilePriorityQueue::Type::REQUIRED_FOR_DRAW
);
820 void TileManager::CheckAndIssueSignals() {
821 TRACE_EVENT0("cc", "TileManager::CheckAndIssueSignals");
822 tile_task_runner_
->CheckForCompletedTasks();
823 did_check_for_completed_tasks_since_last_schedule_tasks_
= true;
825 // Ready to activate.
826 if (signals_
.ready_to_activate
&& !signals_
.did_notify_ready_to_activate
) {
827 signals_
.ready_to_activate
= false;
828 if (IsReadyToActivate()) {
830 "TileManager::CheckAndIssueSignals - ready to activate");
831 signals_
.did_notify_ready_to_activate
= true;
832 client_
->NotifyReadyToActivate();
837 if (signals_
.ready_to_draw
&& !signals_
.did_notify_ready_to_draw
) {
838 signals_
.ready_to_draw
= false;
839 if (IsReadyToDraw()) {
840 TRACE_EVENT0("cc", "TileManager::CheckAndIssueSignals - ready to draw");
841 signals_
.did_notify_ready_to_draw
= true;
842 client_
->NotifyReadyToDraw();
846 // All tile tasks completed.
847 if (signals_
.all_tile_tasks_completed
&&
848 !signals_
.did_notify_all_tile_tasks_completed
) {
849 signals_
.all_tile_tasks_completed
= false;
850 if (!has_scheduled_tile_tasks_
) {
852 "cc", "TileManager::CheckAndIssueSignals - all tile tasks completed");
853 signals_
.did_notify_all_tile_tasks_completed
= true;
854 client_
->NotifyAllTileTasksCompleted();
859 void TileManager::CheckIfMoreTilesNeedToBePrepared() {
860 tile_task_runner_
->CheckForCompletedTasks();
861 did_check_for_completed_tasks_since_last_schedule_tasks_
= true;
863 // When OOM, keep re-assigning memory until we reach a steady state
864 // where top-priority tiles are initialized.
865 PrioritizedTileVector tiles_that_need_to_be_rasterized
;
866 scoped_ptr
<RasterTilePriorityQueue
> raster_priority_queue(
867 client_
->BuildRasterQueue(global_state_
.tree_priority
,
868 RasterTilePriorityQueue::Type::ALL
));
869 AssignGpuMemoryToTiles(raster_priority_queue
.get(),
870 scheduled_raster_task_limit_
,
871 &tiles_that_need_to_be_rasterized
);
873 // Inform the client that will likely require a draw if the highest priority
874 // tile that will be rasterized is required for draw.
875 client_
->SetIsLikelyToRequireADraw(
876 !tiles_that_need_to_be_rasterized
.empty() &&
877 tiles_that_need_to_be_rasterized
.front().tile()->required_for_draw());
879 // |tiles_that_need_to_be_rasterized| will be empty when we reach a
880 // steady memory state. Keep scheduling tasks until we reach this state.
881 if (!tiles_that_need_to_be_rasterized
.empty()) {
882 ScheduleTasks(tiles_that_need_to_be_rasterized
);
886 FreeResourcesForReleasedTiles();
888 resource_pool_
->ReduceResourceUsage();
890 signals_
.all_tile_tasks_completed
= true;
891 signals_check_notifier_
.Schedule();
893 // We don't reserve memory for required-for-activation tiles during
894 // accelerated gestures, so we just postpone activation when we don't
895 // have these tiles, and activate after the accelerated gesture.
896 // Likewise if we don't allow any tiles (as is the case when we're
897 // invisible), if we have tiles that aren't ready, then we shouldn't
898 // activate as activation can cause checkerboards.
899 bool wait_for_all_required_tiles
=
900 global_state_
.tree_priority
== SMOOTHNESS_TAKES_PRIORITY
||
901 global_state_
.memory_limit_policy
== ALLOW_NOTHING
;
903 // Mark any required-for-activation tiles that have not been been assigned
904 // memory after reaching a steady memory state as OOM. This ensures that we
905 // activate even when OOM. Note that we can't reuse the queue we used for
906 // AssignGpuMemoryToTiles, since the AssignGpuMemoryToTiles call could have
907 // evicted some tiles that would not be picked up by the old raster queue.
908 scoped_ptr
<RasterTilePriorityQueue
> required_for_activation_queue(
909 client_
->BuildRasterQueue(
910 global_state_
.tree_priority
,
911 RasterTilePriorityQueue::Type::REQUIRED_FOR_ACTIVATION
));
913 // If we have tiles left to raster for activation, and we don't allow
914 // activating without them, then skip activation and return early.
915 if (!required_for_activation_queue
->IsEmpty() && wait_for_all_required_tiles
)
918 // Mark required tiles as OOM so that we can activate without them.
919 for (; !required_for_activation_queue
->IsEmpty();
920 required_for_activation_queue
->Pop()) {
921 Tile
* tile
= required_for_activation_queue
->Top().tile();
922 tile
->draw_info().set_oom();
923 client_
->NotifyTileStateChanged(tile
);
926 DCHECK(IsReadyToActivate());
927 // TODO(ericrk): Investigate why we need to schedule this (not just call it
928 // inline). http://crbug.com/498439
929 signals_
.ready_to_activate
= true;
930 signals_check_notifier_
.Schedule();
933 TileManager::MemoryUsage::MemoryUsage() : memory_bytes_(0), resource_count_(0) {
936 TileManager::MemoryUsage::MemoryUsage(size_t memory_bytes
,
937 size_t resource_count
)
938 : memory_bytes_(static_cast<int64
>(memory_bytes
)),
939 resource_count_(static_cast<int>(resource_count
)) {
940 // MemoryUsage is constructed using size_ts, since it deals with memory and
941 // the inputs are typically size_t. However, during the course of usage (in
942 // particular operator-=) can cause internal values to become negative. Thus,
943 // member variables are signed.
944 DCHECK_LE(memory_bytes
,
945 static_cast<size_t>(std::numeric_limits
<int64
>::max()));
946 DCHECK_LE(resource_count
,
947 static_cast<size_t>(std::numeric_limits
<int>::max()));
951 TileManager::MemoryUsage
TileManager::MemoryUsage::FromConfig(
952 const gfx::Size
& size
,
953 ResourceFormat format
) {
954 // We can use UncheckedSizeInBytes here since this is used with a tile
955 // size which is determined by the compositor (it's at most max texture size).
956 return MemoryUsage(ResourceUtil::UncheckedSizeInBytes
<size_t>(size
, format
),
961 TileManager::MemoryUsage
TileManager::MemoryUsage::FromTile(const Tile
* tile
) {
962 const TileDrawInfo
& draw_info
= tile
->draw_info();
963 if (draw_info
.resource_
) {
964 return MemoryUsage::FromConfig(draw_info
.resource_
->size(),
965 draw_info
.resource_
->format());
967 return MemoryUsage();
970 TileManager::MemoryUsage
& TileManager::MemoryUsage::operator+=(
971 const MemoryUsage
& other
) {
972 memory_bytes_
+= other
.memory_bytes_
;
973 resource_count_
+= other
.resource_count_
;
977 TileManager::MemoryUsage
& TileManager::MemoryUsage::operator-=(
978 const MemoryUsage
& other
) {
979 memory_bytes_
-= other
.memory_bytes_
;
980 resource_count_
-= other
.resource_count_
;
984 TileManager::MemoryUsage
TileManager::MemoryUsage::operator-(
985 const MemoryUsage
& other
) {
986 MemoryUsage result
= *this;
991 bool TileManager::MemoryUsage::Exceeds(const MemoryUsage
& limit
) const {
992 return memory_bytes_
> limit
.memory_bytes_
||
993 resource_count_
> limit
.resource_count_
;
996 TileManager::Signals::Signals() {
1000 void TileManager::Signals::reset() {
1001 ready_to_activate
= false;
1002 did_notify_ready_to_activate
= false;
1003 ready_to_draw
= false;
1004 did_notify_ready_to_draw
= false;
1005 all_tile_tasks_completed
= false;
1006 did_notify_all_tile_tasks_completed
= false;