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 raster_buffer_
->Playback(raster_source_
.get(), content_rect_
,
131 invalid_content_rect_
, new_content_id_
,
135 RasterSource::SolidColorAnalysis analysis_
;
136 scoped_refptr
<RasterSource
> raster_source_
;
137 gfx::Rect content_rect_
;
138 gfx::Rect invalid_content_rect_
;
139 float contents_scale_
;
140 TileResolution tile_resolution_
;
142 uint64_t source_prepare_tiles_id_
;
144 uint64_t new_content_id_
;
145 uint64_t previous_content_id_
;
146 uint64_t resource_content_id_
;
147 int source_frame_number_
;
148 bool analyze_picture_
;
149 const base::Callback
<void(const RasterSource::SolidColorAnalysis
&, bool)>
151 scoped_ptr
<RasterBuffer
> raster_buffer_
;
153 DISALLOW_COPY_AND_ASSIGN(RasterTaskImpl
);
156 class ImageDecodeTaskImpl
: public ImageDecodeTask
{
158 ImageDecodeTaskImpl(SkPixelRef
* pixel_ref
,
159 uint64_t source_prepare_tiles_id
,
160 const base::Callback
<void(bool was_canceled
)>& reply
)
161 : pixel_ref_(skia::SharePtr(pixel_ref
)),
162 source_prepare_tiles_id_(source_prepare_tiles_id
),
165 // Overridden from Task:
166 void RunOnWorkerThread() override
{
167 TRACE_EVENT1("cc", "ImageDecodeTaskImpl::RunOnWorkerThread",
168 "source_prepare_tiles_id", source_prepare_tiles_id_
);
170 devtools_instrumentation::ScopedImageDecodeTask
image_decode_task(
172 // This will cause the image referred to by pixel ref to be decoded.
173 pixel_ref_
->lockPixels();
174 pixel_ref_
->unlockPixels();
176 // Release the reference after decoding image to ensure that it is not
177 // kept alive unless needed.
181 // Overridden from TileTask:
182 void ScheduleOnOriginThread(TileTaskClient
* client
) override
{}
183 void CompleteOnOriginThread(TileTaskClient
* client
) override
{}
184 void RunReplyOnOriginThread() override
{ reply_
.Run(!HasFinishedRunning()); }
187 ~ImageDecodeTaskImpl() override
{}
190 skia::RefPtr
<SkPixelRef
> pixel_ref_
;
191 uint64_t source_prepare_tiles_id_
;
192 const base::Callback
<void(bool was_canceled
)> reply_
;
194 DISALLOW_COPY_AND_ASSIGN(ImageDecodeTaskImpl
);
197 const char* TaskSetName(TaskSet task_set
) {
199 case TileManager::ALL
:
201 case TileManager::REQUIRED_FOR_ACTIVATION
:
202 return "REQUIRED_FOR_ACTIVATION";
203 case TileManager::REQUIRED_FOR_DRAW
:
204 return "REQUIRED_FOR_DRAW";
208 return "Invalid TaskSet";
213 RasterTaskCompletionStats::RasterTaskCompletionStats()
214 : completed_count(0u), canceled_count(0u) {}
216 scoped_refptr
<base::trace_event::ConvertableToTraceFormat
>
217 RasterTaskCompletionStatsAsValue(const RasterTaskCompletionStats
& stats
) {
218 scoped_refptr
<base::trace_event::TracedValue
> state
=
219 new base::trace_event::TracedValue();
220 state
->SetInteger("completed_count",
221 base::saturated_cast
<int>(stats
.completed_count
));
222 state
->SetInteger("canceled_count",
223 base::saturated_cast
<int>(stats
.canceled_count
));
228 scoped_ptr
<TileManager
> TileManager::Create(
229 TileManagerClient
* client
,
230 base::SequencedTaskRunner
* task_runner
,
231 ResourcePool
* resource_pool
,
232 TileTaskRunner
* tile_task_runner
,
233 size_t scheduled_raster_task_limit
) {
234 return make_scoped_ptr(new TileManager(client
, task_runner
, resource_pool
,
236 scheduled_raster_task_limit
));
239 TileManager::TileManager(
240 TileManagerClient
* client
,
241 const scoped_refptr
<base::SequencedTaskRunner
>& task_runner
,
242 ResourcePool
* resource_pool
,
243 TileTaskRunner
* tile_task_runner
,
244 size_t scheduled_raster_task_limit
)
246 task_runner_(task_runner
),
247 resource_pool_(resource_pool
),
248 tile_task_runner_(tile_task_runner
),
249 scheduled_raster_task_limit_(scheduled_raster_task_limit
),
250 all_tiles_that_need_to_be_rasterized_are_scheduled_(true),
251 did_check_for_completed_tasks_since_last_schedule_tasks_(true),
252 did_oom_on_last_assign_(false),
253 more_tiles_need_prepare_check_notifier_(
255 base::Bind(&TileManager::CheckIfMoreTilesNeedToBePrepared
,
256 base::Unretained(this))),
257 signals_check_notifier_(task_runner_
.get(),
258 base::Bind(&TileManager::CheckAndIssueSignals
,
259 base::Unretained(this))),
260 has_scheduled_tile_tasks_(false),
261 prepare_tiles_count_(0u) {
262 tile_task_runner_
->SetClient(this);
265 TileManager::~TileManager() {
266 // Reset global state and manage. This should cause
267 // our memory usage to drop to zero.
268 global_state_
= GlobalStateThatImpactsTilePriority();
271 tile_task_runner_
->ScheduleTasks(&empty
);
272 orphan_raster_tasks_
.clear();
274 // This should finish all pending tasks and release any uninitialized
276 tile_task_runner_
->Shutdown();
277 tile_task_runner_
->CheckForCompletedTasks();
279 FreeResourcesForReleasedTiles();
280 CleanUpReleasedTiles();
283 void TileManager::Release(Tile
* tile
) {
284 released_tiles_
.push_back(tile
);
287 TaskSetCollection
TileManager::TasksThatShouldBeForcedToComplete() const {
288 TaskSetCollection tasks_that_should_be_forced_to_complete
;
289 if (global_state_
.tree_priority
!= SMOOTHNESS_TAKES_PRIORITY
)
290 tasks_that_should_be_forced_to_complete
[REQUIRED_FOR_ACTIVATION
] = true;
291 return tasks_that_should_be_forced_to_complete
;
294 void TileManager::FreeResourcesForReleasedTiles() {
295 for (auto* tile
: released_tiles_
)
296 FreeResourcesForTile(tile
);
299 void TileManager::CleanUpReleasedTiles() {
300 std::vector
<Tile
*> tiles_to_retain
;
301 for (auto* tile
: released_tiles_
) {
302 if (tile
->HasRasterTask()) {
303 tiles_to_retain
.push_back(tile
);
307 DCHECK(!tile
->draw_info().has_resource());
308 DCHECK(tiles_
.find(tile
->id()) != tiles_
.end());
309 tiles_
.erase(tile
->id());
311 LayerCountMap::iterator layer_it
=
312 used_layer_counts_
.find(tile
->layer_id());
313 DCHECK_GT(layer_it
->second
, 0);
314 if (--layer_it
->second
== 0) {
315 used_layer_counts_
.erase(layer_it
);
316 image_decode_tasks_
.erase(tile
->layer_id());
321 released_tiles_
.swap(tiles_to_retain
);
324 void TileManager::DidFinishRunningTileTasks(TaskSet task_set
) {
325 TRACE_EVENT1("cc", "TileManager::DidFinishRunningTileTasks", "task_set",
326 TaskSetName(task_set
));
330 has_scheduled_tile_tasks_
= false;
332 bool memory_usage_above_limit
=
333 resource_pool_
->total_memory_usage_bytes() >
334 global_state_
.soft_memory_limit_in_bytes
;
336 if (all_tiles_that_need_to_be_rasterized_are_scheduled_
&&
337 !memory_usage_above_limit
) {
338 // TODO(ericrk): We should find a better way to safely handle re-entrant
339 // notifications than always having to schedule a new task.
340 // http://crbug.com/498439
341 signals_
.all_tile_tasks_completed
= true;
342 signals_check_notifier_
.Schedule();
346 more_tiles_need_prepare_check_notifier_
.Schedule();
349 case REQUIRED_FOR_ACTIVATION
:
350 signals_
.ready_to_activate
= true;
351 signals_check_notifier_
.Schedule();
354 case REQUIRED_FOR_DRAW
:
355 signals_
.ready_to_draw
= true;
356 signals_check_notifier_
.Schedule();
363 void TileManager::PrepareTiles(
364 const GlobalStateThatImpactsTilePriority
& state
) {
365 ++prepare_tiles_count_
;
367 TRACE_EVENT1("cc", "TileManager::PrepareTiles", "prepare_tiles_id",
368 prepare_tiles_count_
);
371 global_state_
= state
;
373 // We need to call CheckForCompletedTasks() once in-between each call
374 // to ScheduleTasks() to prevent canceled tasks from being scheduled.
375 if (!did_check_for_completed_tasks_since_last_schedule_tasks_
) {
376 tile_task_runner_
->CheckForCompletedTasks();
377 did_check_for_completed_tasks_since_last_schedule_tasks_
= true;
380 FreeResourcesForReleasedTiles();
381 CleanUpReleasedTiles();
383 PrioritizedTileVector tiles_that_need_to_be_rasterized
;
384 scoped_ptr
<RasterTilePriorityQueue
> raster_priority_queue(
385 client_
->BuildRasterQueue(global_state_
.tree_priority
,
386 RasterTilePriorityQueue::Type::ALL
));
387 AssignGpuMemoryToTiles(raster_priority_queue
.get(),
388 scheduled_raster_task_limit_
,
389 &tiles_that_need_to_be_rasterized
);
391 // Inform the client that will likely require a draw if the highest priority
392 // tile that will be rasterized is required for draw.
393 client_
->SetIsLikelyToRequireADraw(
394 !tiles_that_need_to_be_rasterized
.empty() &&
395 tiles_that_need_to_be_rasterized
.front().tile()->required_for_draw());
397 // Schedule tile tasks.
398 ScheduleTasks(tiles_that_need_to_be_rasterized
);
400 TRACE_EVENT_INSTANT1("cc", "DidPrepareTiles", TRACE_EVENT_SCOPE_THREAD
,
401 "state", BasicStateAsValue());
403 TRACE_COUNTER_ID1("cc", "unused_memory_bytes", this,
404 resource_pool_
->total_memory_usage_bytes() -
405 resource_pool_
->acquired_memory_usage_bytes());
408 void TileManager::Flush() {
409 TRACE_EVENT0("cc", "TileManager::Flush");
411 tile_task_runner_
->CheckForCompletedTasks();
413 did_check_for_completed_tasks_since_last_schedule_tasks_
= true;
415 TRACE_EVENT_INSTANT1("cc", "DidFlush", TRACE_EVENT_SCOPE_THREAD
, "stats",
416 RasterTaskCompletionStatsAsValue(flush_stats_
));
417 flush_stats_
= RasterTaskCompletionStats();
420 scoped_refptr
<base::trace_event::ConvertableToTraceFormat
>
421 TileManager::BasicStateAsValue() const {
422 scoped_refptr
<base::trace_event::TracedValue
> value
=
423 new base::trace_event::TracedValue();
424 BasicStateAsValueInto(value
.get());
428 void TileManager::BasicStateAsValueInto(
429 base::trace_event::TracedValue
* state
) const {
430 state
->SetInteger("tile_count", base::saturated_cast
<int>(tiles_
.size()));
431 state
->SetBoolean("did_oom_on_last_assign", did_oom_on_last_assign_
);
432 state
->BeginDictionary("global_state");
433 global_state_
.AsValueInto(state
);
434 state
->EndDictionary();
437 scoped_ptr
<EvictionTilePriorityQueue
>
438 TileManager::FreeTileResourcesUntilUsageIsWithinLimit(
439 scoped_ptr
<EvictionTilePriorityQueue
> eviction_priority_queue
,
440 const MemoryUsage
& limit
,
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 Tile
* tile
= eviction_priority_queue
->Top().tile();
451 *usage
-= MemoryUsage::FromTile(tile
);
452 FreeResourcesForTileAndNotifyClientIfTileWasReadyToDraw(tile
);
453 eviction_priority_queue
->Pop();
455 return eviction_priority_queue
;
458 scoped_ptr
<EvictionTilePriorityQueue
>
459 TileManager::FreeTileResourcesWithLowerPriorityUntilUsageIsWithinLimit(
460 scoped_ptr
<EvictionTilePriorityQueue
> eviction_priority_queue
,
461 const MemoryUsage
& limit
,
462 const TilePriority
& other_priority
,
463 MemoryUsage
* usage
) {
464 while (usage
->Exceeds(limit
)) {
465 if (!eviction_priority_queue
) {
466 eviction_priority_queue
=
467 client_
->BuildEvictionQueue(global_state_
.tree_priority
);
469 if (eviction_priority_queue
->IsEmpty())
472 const PrioritizedTile
& prioritized_tile
= eviction_priority_queue
->Top();
473 if (!other_priority
.IsHigherPriorityThan(prioritized_tile
.priority()))
476 Tile
* tile
= prioritized_tile
.tile();
477 *usage
-= MemoryUsage::FromTile(tile
);
478 FreeResourcesForTileAndNotifyClientIfTileWasReadyToDraw(tile
);
479 eviction_priority_queue
->Pop();
481 return eviction_priority_queue
;
484 bool TileManager::TilePriorityViolatesMemoryPolicy(
485 const TilePriority
& priority
) {
486 switch (global_state_
.memory_limit_policy
) {
489 case ALLOW_ABSOLUTE_MINIMUM
:
490 return priority
.priority_bin
> TilePriority::NOW
;
491 case ALLOW_PREPAINT_ONLY
:
492 return priority
.priority_bin
> TilePriority::SOON
;
494 return priority
.distance_to_visible
==
495 std::numeric_limits
<float>::infinity();
501 void TileManager::AssignGpuMemoryToTiles(
502 RasterTilePriorityQueue
* raster_priority_queue
,
503 size_t scheduled_raster_task_limit
,
504 PrioritizedTileVector
* tiles_that_need_to_be_rasterized
) {
505 TRACE_EVENT_BEGIN0("cc", "TileManager::AssignGpuMemoryToTiles");
507 // Maintain the list of released resources that can potentially be re-used
508 // or deleted. If this operation becomes expensive too, only do this after
509 // some resource(s) was returned. Note that in that case, one also need to
510 // invalidate when releasing some resource from the pool.
511 resource_pool_
->CheckBusyResources(false);
513 // Now give memory out to the tiles until we're out, and build
514 // the needs-to-be-rasterized queue.
515 unsigned schedule_priority
= 1u;
516 all_tiles_that_need_to_be_rasterized_are_scheduled_
= true;
517 bool had_enough_memory_to_schedule_tiles_needed_now
= true;
519 MemoryUsage
hard_memory_limit(global_state_
.hard_memory_limit_in_bytes
,
520 global_state_
.num_resources_limit
);
521 MemoryUsage
soft_memory_limit(global_state_
.soft_memory_limit_in_bytes
,
522 global_state_
.num_resources_limit
);
523 MemoryUsage
memory_usage(resource_pool_
->acquired_memory_usage_bytes(),
524 resource_pool_
->acquired_resource_count());
526 scoped_ptr
<EvictionTilePriorityQueue
> eviction_priority_queue
;
527 for (; !raster_priority_queue
->IsEmpty(); raster_priority_queue
->Pop()) {
528 const PrioritizedTile
& prioritized_tile
= raster_priority_queue
->Top();
529 Tile
* tile
= prioritized_tile
.tile();
530 TilePriority priority
= prioritized_tile
.priority();
532 if (TilePriorityViolatesMemoryPolicy(priority
)) {
533 TRACE_EVENT_INSTANT0(
534 "cc", "TileManager::AssignGpuMemory tile violates memory policy",
535 TRACE_EVENT_SCOPE_THREAD
);
539 // We won't be able to schedule this tile, so break out early.
540 if (tiles_that_need_to_be_rasterized
->size() >=
541 scheduled_raster_task_limit
) {
542 all_tiles_that_need_to_be_rasterized_are_scheduled_
= false;
546 tile
->scheduled_priority_
= schedule_priority
++;
548 DCHECK_IMPLIES(tile
->draw_info().mode() != TileDrawInfo::OOM_MODE
,
549 !tile
->draw_info().IsReadyToDraw());
551 // If the tile already has a raster_task, then the memory used by it is
552 // already accounted for in memory_usage. Otherwise, we'll have to acquire
553 // more memory to create a raster task.
554 MemoryUsage memory_required_by_tile_to_be_scheduled
;
555 if (!tile
->raster_task_
.get()) {
556 memory_required_by_tile_to_be_scheduled
= MemoryUsage::FromConfig(
557 tile
->desired_texture_size(), tile_task_runner_
->GetResourceFormat());
560 bool tile_is_needed_now
= priority
.priority_bin
== TilePriority::NOW
;
562 // This is the memory limit that will be used by this tile. Depending on
563 // the tile priority, it will be one of hard_memory_limit or
564 // soft_memory_limit.
565 MemoryUsage
& tile_memory_limit
=
566 tile_is_needed_now
? hard_memory_limit
: soft_memory_limit
;
568 const MemoryUsage
& scheduled_tile_memory_limit
=
569 tile_memory_limit
- memory_required_by_tile_to_be_scheduled
;
570 eviction_priority_queue
=
571 FreeTileResourcesWithLowerPriorityUntilUsageIsWithinLimit(
572 eviction_priority_queue
.Pass(), scheduled_tile_memory_limit
,
573 priority
, &memory_usage
);
574 bool memory_usage_is_within_limit
=
575 !memory_usage
.Exceeds(scheduled_tile_memory_limit
);
577 // If we couldn't fit the tile into our current memory limit, then we're
579 if (!memory_usage_is_within_limit
) {
580 if (tile_is_needed_now
)
581 had_enough_memory_to_schedule_tiles_needed_now
= false;
582 all_tiles_that_need_to_be_rasterized_are_scheduled_
= false;
586 memory_usage
+= memory_required_by_tile_to_be_scheduled
;
587 tiles_that_need_to_be_rasterized
->push_back(prioritized_tile
);
590 // Note that we should try and further reduce memory in case the above loop
591 // didn't reduce memory. This ensures that we always release as many resources
592 // as possible to stay within the memory limit.
593 eviction_priority_queue
= FreeTileResourcesUntilUsageIsWithinLimit(
594 eviction_priority_queue
.Pass(), hard_memory_limit
, &memory_usage
);
596 UMA_HISTOGRAM_BOOLEAN("TileManager.ExceededMemoryBudget",
597 !had_enough_memory_to_schedule_tiles_needed_now
);
598 did_oom_on_last_assign_
= !had_enough_memory_to_schedule_tiles_needed_now
;
600 memory_stats_from_last_assign_
.total_budget_in_bytes
=
601 global_state_
.hard_memory_limit_in_bytes
;
602 memory_stats_from_last_assign_
.total_bytes_used
= memory_usage
.memory_bytes();
603 DCHECK_GE(memory_stats_from_last_assign_
.total_bytes_used
, 0);
604 memory_stats_from_last_assign_
.had_enough_memory
=
605 had_enough_memory_to_schedule_tiles_needed_now
;
607 TRACE_EVENT_END2("cc", "TileManager::AssignGpuMemoryToTiles",
608 "all_tiles_that_need_to_be_rasterized_are_scheduled",
609 all_tiles_that_need_to_be_rasterized_are_scheduled_
,
610 "had_enough_memory_to_schedule_tiles_needed_now",
611 had_enough_memory_to_schedule_tiles_needed_now
);
614 void TileManager::FreeResourcesForTile(Tile
* tile
) {
615 TileDrawInfo
& draw_info
= tile
->draw_info();
616 if (draw_info
.resource_
)
617 resource_pool_
->ReleaseResource(draw_info
.resource_
.Pass(), tile
->id());
620 void TileManager::FreeResourcesForTileAndNotifyClientIfTileWasReadyToDraw(
622 bool was_ready_to_draw
= tile
->draw_info().IsReadyToDraw();
623 FreeResourcesForTile(tile
);
624 if (was_ready_to_draw
)
625 client_
->NotifyTileStateChanged(tile
);
628 void TileManager::ScheduleTasks(
629 const PrioritizedTileVector
& tiles_that_need_to_be_rasterized
) {
631 "TileManager::ScheduleTasks",
633 tiles_that_need_to_be_rasterized
.size());
635 DCHECK(did_check_for_completed_tasks_since_last_schedule_tasks_
);
637 raster_queue_
.Reset();
639 // Even when scheduling an empty set of tiles, the TTWP does some work, and
640 // will always trigger a DidFinishRunningTileTasks notification. Because of
641 // this we unconditionally set |has_scheduled_tile_tasks_| to true.
642 has_scheduled_tile_tasks_
= true;
644 // Build a new task queue containing all task currently needed. Tasks
645 // are added in order of priority, highest priority task first.
646 for (auto& prioritized_tile
: tiles_that_need_to_be_rasterized
) {
647 Tile
* tile
= prioritized_tile
.tile();
649 DCHECK(tile
->draw_info().requires_resource());
650 DCHECK(!tile
->draw_info().resource_
);
652 if (!tile
->raster_task_
.get())
653 tile
->raster_task_
= CreateRasterTask(prioritized_tile
);
655 TaskSetCollection task_sets
;
656 if (tile
->required_for_activation())
657 task_sets
.set(REQUIRED_FOR_ACTIVATION
);
658 if (tile
->required_for_draw())
659 task_sets
.set(REQUIRED_FOR_DRAW
);
661 raster_queue_
.items
.push_back(
662 TileTaskQueue::Item(tile
->raster_task_
.get(), task_sets
));
665 // We must reduce the amount of unused resoruces before calling
666 // ScheduleTasks to prevent usage from rising above limits.
667 resource_pool_
->ReduceResourceUsage();
669 // Schedule running of |raster_queue_|. This replaces any previously
670 // scheduled tasks and effectively cancels all tasks not present
671 // in |raster_queue_|.
672 tile_task_runner_
->ScheduleTasks(&raster_queue_
);
674 // It's now safe to clean up orphan tasks as raster worker pool is not
675 // allowed to keep around unreferenced raster tasks after ScheduleTasks() has
677 orphan_raster_tasks_
.clear();
679 did_check_for_completed_tasks_since_last_schedule_tasks_
= false;
682 scoped_refptr
<ImageDecodeTask
> TileManager::CreateImageDecodeTask(
684 SkPixelRef
* pixel_ref
) {
685 return make_scoped_refptr(new ImageDecodeTaskImpl(
686 pixel_ref
, prepare_tiles_count_
,
687 base::Bind(&TileManager::OnImageDecodeTaskCompleted
,
688 base::Unretained(this), tile
->layer_id(),
689 base::Unretained(pixel_ref
))));
692 scoped_refptr
<RasterTask
> TileManager::CreateRasterTask(
693 const PrioritizedTile
& prioritized_tile
) {
694 Tile
* tile
= prioritized_tile
.tile();
695 uint64_t resource_content_id
= 0;
696 scoped_ptr
<ScopedResource
> resource
;
697 if (tile
->invalidated_id()) {
698 // TODO(danakj): For resources that are in use, we should still grab them
699 // and copy from them instead of rastering everything. crbug.com/492754
701 resource_pool_
->TryAcquireResourceWithContentId(tile
->invalidated_id());
704 resource_content_id
= tile
->invalidated_id();
705 DCHECK_EQ(tile_task_runner_
->GetResourceFormat(), resource
->format());
706 DCHECK_EQ(tile
->desired_texture_size().ToString(),
707 resource
->size().ToString());
709 resource
= resource_pool_
->AcquireResource(
710 tile
->desired_texture_size(), tile_task_runner_
->GetResourceFormat());
712 const ScopedResource
* const_resource
= resource
.get();
714 // Create and queue all image decode tasks that this tile depends on.
715 ImageDecodeTask::Vector decode_tasks
;
716 PixelRefTaskMap
& existing_pixel_refs
= image_decode_tasks_
[tile
->layer_id()];
717 std::vector
<SkPixelRef
*> pixel_refs
;
718 prioritized_tile
.raster_source()->GatherPixelRefs(
719 tile
->content_rect(), tile
->contents_scale(), &pixel_refs
);
720 for (SkPixelRef
* pixel_ref
: pixel_refs
) {
721 uint32_t id
= pixel_ref
->getGenerationID();
723 // Append existing image decode task if available.
724 PixelRefTaskMap::iterator decode_task_it
= existing_pixel_refs
.find(id
);
725 if (decode_task_it
!= existing_pixel_refs
.end()) {
726 decode_tasks
.push_back(decode_task_it
->second
);
730 // Create and append new image decode task for this pixel ref.
731 scoped_refptr
<ImageDecodeTask
> decode_task
=
732 CreateImageDecodeTask(tile
, pixel_ref
);
733 decode_tasks
.push_back(decode_task
);
734 existing_pixel_refs
[id
] = decode_task
;
737 return make_scoped_refptr(new RasterTaskImpl(
738 const_resource
, prioritized_tile
.raster_source(), tile
->content_rect(),
739 tile
->invalidated_content_rect(), tile
->contents_scale(),
740 prioritized_tile
.priority().resolution
, tile
->layer_id(),
741 prepare_tiles_count_
, static_cast<const void*>(tile
), tile
->id(),
742 tile
->invalidated_id(), resource_content_id
, tile
->source_frame_number(),
743 tile
->use_picture_analysis(),
744 base::Bind(&TileManager::OnRasterTaskCompleted
, base::Unretained(this),
745 tile
->id(), base::Passed(&resource
)),
749 void TileManager::OnImageDecodeTaskCompleted(int layer_id
,
750 SkPixelRef
* pixel_ref
,
752 // If the task was canceled, we need to clean it up
753 // from |image_decode_tasks_|.
757 LayerPixelRefTaskMap::iterator layer_it
= image_decode_tasks_
.find(layer_id
);
758 if (layer_it
== image_decode_tasks_
.end())
761 PixelRefTaskMap
& pixel_ref_tasks
= layer_it
->second
;
762 PixelRefTaskMap::iterator task_it
=
763 pixel_ref_tasks
.find(pixel_ref
->getGenerationID());
765 if (task_it
!= pixel_ref_tasks
.end())
766 pixel_ref_tasks
.erase(task_it
);
769 void TileManager::OnRasterTaskCompleted(
771 scoped_ptr
<ScopedResource
> resource
,
772 const RasterSource::SolidColorAnalysis
& analysis
,
774 DCHECK(tiles_
.find(tile_id
) != tiles_
.end());
776 Tile
* tile
= tiles_
[tile_id
];
777 DCHECK(tile
->raster_task_
.get());
778 orphan_raster_tasks_
.push_back(tile
->raster_task_
);
779 tile
->raster_task_
= nullptr;
782 ++flush_stats_
.canceled_count
;
783 resource_pool_
->ReleaseResource(resource
.Pass(), tile
->invalidated_id());
787 UpdateTileDrawInfo(tile
, resource
.Pass(), analysis
);
790 void TileManager::UpdateTileDrawInfo(
792 scoped_ptr
<ScopedResource
> resource
,
793 const RasterSource::SolidColorAnalysis
& analysis
) {
794 TileDrawInfo
& draw_info
= tile
->draw_info();
796 ++flush_stats_
.completed_count
;
798 if (analysis
.is_solid_color
) {
799 draw_info
.set_solid_color(analysis
.solid_color
);
801 // Pass the old tile id here because the tile is solid color so we did not
802 // raster anything into the tile resource.
803 resource_pool_
->ReleaseResource(resource
.Pass(), tile
->invalidated_id());
807 draw_info
.set_use_resource();
808 draw_info
.resource_
= resource
.Pass();
809 draw_info
.contents_swizzled_
=
810 tile_task_runner_
->GetResourceRequiresSwizzle();
812 DCHECK(draw_info
.IsReadyToDraw());
813 draw_info
.set_was_ever_ready_to_draw();
815 client_
->NotifyTileStateChanged(tile
);
818 ScopedTilePtr
TileManager::CreateTile(const gfx::Size
& desired_texture_size
,
819 const gfx::Rect
& content_rect
,
820 float contents_scale
,
822 int source_frame_number
,
824 ScopedTilePtr
tile(new Tile(this, desired_texture_size
, content_rect
,
825 contents_scale
, layer_id
, source_frame_number
,
827 DCHECK(tiles_
.find(tile
->id()) == tiles_
.end());
829 tiles_
[tile
->id()] = tile
.get();
830 used_layer_counts_
[tile
->layer_id()]++;
834 void TileManager::SetTileTaskRunnerForTesting(
835 TileTaskRunner
* tile_task_runner
) {
836 tile_task_runner_
= tile_task_runner
;
837 tile_task_runner_
->SetClient(this);
840 bool TileManager::AreRequiredTilesReadyToDraw(
841 RasterTilePriorityQueue::Type type
) const {
842 scoped_ptr
<RasterTilePriorityQueue
> raster_priority_queue(
843 client_
->BuildRasterQueue(global_state_
.tree_priority
, type
));
844 // It is insufficient to check whether the raster queue we constructed is
845 // empty. The reason for this is that there are situations (rasterize on
846 // demand) when the tile both needs raster and it's ready to draw. Hence, we
847 // have to iterate the queue to check whether the required tiles are ready to
849 for (; !raster_priority_queue
->IsEmpty(); raster_priority_queue
->Pop()) {
850 if (!raster_priority_queue
->Top().tile()->draw_info().IsReadyToDraw())
855 scoped_ptr
<RasterTilePriorityQueue
> all_queue(
856 client_
->BuildRasterQueue(global_state_
.tree_priority
, type
));
857 for (; !all_queue
->IsEmpty(); all_queue
->Pop()) {
858 Tile
* tile
= all_queue
->Top().tile();
859 DCHECK_IMPLIES(tile
->required_for_activation(),
860 tile
->draw_info().IsReadyToDraw());
866 bool TileManager::IsReadyToActivate() const {
867 TRACE_EVENT0("cc", "TileManager::IsReadyToActivate");
868 return AreRequiredTilesReadyToDraw(
869 RasterTilePriorityQueue::Type::REQUIRED_FOR_ACTIVATION
);
872 bool TileManager::IsReadyToDraw() const {
873 TRACE_EVENT0("cc", "TileManager::IsReadyToDraw");
874 return AreRequiredTilesReadyToDraw(
875 RasterTilePriorityQueue::Type::REQUIRED_FOR_DRAW
);
878 void TileManager::CheckAndIssueSignals() {
879 TRACE_EVENT0("cc", "TileManager::CheckAndIssueSignals");
880 tile_task_runner_
->CheckForCompletedTasks();
881 did_check_for_completed_tasks_since_last_schedule_tasks_
= true;
883 // Ready to activate.
884 if (signals_
.ready_to_activate
&& !signals_
.did_notify_ready_to_activate
) {
885 signals_
.ready_to_activate
= false;
886 if (IsReadyToActivate()) {
888 "TileManager::CheckAndIssueSignals - ready to activate");
889 signals_
.did_notify_ready_to_activate
= true;
890 client_
->NotifyReadyToActivate();
895 if (signals_
.ready_to_draw
&& !signals_
.did_notify_ready_to_draw
) {
896 signals_
.ready_to_draw
= false;
897 if (IsReadyToDraw()) {
898 TRACE_EVENT0("cc", "TileManager::CheckAndIssueSignals - ready to draw");
899 signals_
.did_notify_ready_to_draw
= true;
900 client_
->NotifyReadyToDraw();
904 // All tile tasks completed.
905 if (signals_
.all_tile_tasks_completed
&&
906 !signals_
.did_notify_all_tile_tasks_completed
) {
907 signals_
.all_tile_tasks_completed
= false;
908 if (!has_scheduled_tile_tasks_
) {
910 "cc", "TileManager::CheckAndIssueSignals - all tile tasks completed");
911 signals_
.did_notify_all_tile_tasks_completed
= true;
912 client_
->NotifyAllTileTasksCompleted();
917 void TileManager::CheckIfMoreTilesNeedToBePrepared() {
918 tile_task_runner_
->CheckForCompletedTasks();
919 did_check_for_completed_tasks_since_last_schedule_tasks_
= true;
921 // When OOM, keep re-assigning memory until we reach a steady state
922 // where top-priority tiles are initialized.
923 PrioritizedTileVector tiles_that_need_to_be_rasterized
;
924 scoped_ptr
<RasterTilePriorityQueue
> raster_priority_queue(
925 client_
->BuildRasterQueue(global_state_
.tree_priority
,
926 RasterTilePriorityQueue::Type::ALL
));
927 AssignGpuMemoryToTiles(raster_priority_queue
.get(),
928 scheduled_raster_task_limit_
,
929 &tiles_that_need_to_be_rasterized
);
931 // Inform the client that will likely require a draw if the highest priority
932 // tile that will be rasterized is required for draw.
933 client_
->SetIsLikelyToRequireADraw(
934 !tiles_that_need_to_be_rasterized
.empty() &&
935 tiles_that_need_to_be_rasterized
.front().tile()->required_for_draw());
937 // |tiles_that_need_to_be_rasterized| will be empty when we reach a
938 // steady memory state. Keep scheduling tasks until we reach this state.
939 if (!tiles_that_need_to_be_rasterized
.empty()) {
940 ScheduleTasks(tiles_that_need_to_be_rasterized
);
944 FreeResourcesForReleasedTiles();
946 resource_pool_
->ReduceResourceUsage();
948 signals_
.all_tile_tasks_completed
= true;
949 signals_check_notifier_
.Schedule();
951 // We don't reserve memory for required-for-activation tiles during
952 // accelerated gestures, so we just postpone activation when we don't
953 // have these tiles, and activate after the accelerated gesture.
954 // Likewise if we don't allow any tiles (as is the case when we're
955 // invisible), if we have tiles that aren't ready, then we shouldn't
956 // activate as activation can cause checkerboards.
957 bool wait_for_all_required_tiles
=
958 global_state_
.tree_priority
== SMOOTHNESS_TAKES_PRIORITY
||
959 global_state_
.memory_limit_policy
== ALLOW_NOTHING
;
961 // Mark any required-for-activation tiles that have not been been assigned
962 // memory after reaching a steady memory state as OOM. This ensures that we
963 // activate even when OOM. Note that we can't reuse the queue we used for
964 // AssignGpuMemoryToTiles, since the AssignGpuMemoryToTiles call could have
965 // evicted some tiles that would not be picked up by the old raster queue.
966 scoped_ptr
<RasterTilePriorityQueue
> required_for_activation_queue(
967 client_
->BuildRasterQueue(
968 global_state_
.tree_priority
,
969 RasterTilePriorityQueue::Type::REQUIRED_FOR_ACTIVATION
));
971 // If we have tiles left to raster for activation, and we don't allow
972 // activating without them, then skip activation and return early.
973 if (!required_for_activation_queue
->IsEmpty() && wait_for_all_required_tiles
)
976 // Mark required tiles as OOM so that we can activate without them.
977 for (; !required_for_activation_queue
->IsEmpty();
978 required_for_activation_queue
->Pop()) {
979 Tile
* tile
= required_for_activation_queue
->Top().tile();
980 tile
->draw_info().set_oom();
981 client_
->NotifyTileStateChanged(tile
);
984 DCHECK(IsReadyToActivate());
985 // TODO(ericrk): Investigate why we need to schedule this (not just call it
986 // inline). http://crbug.com/498439
987 signals_
.ready_to_activate
= true;
988 signals_check_notifier_
.Schedule();
991 TileManager::MemoryUsage::MemoryUsage() : memory_bytes_(0), resource_count_(0) {
994 TileManager::MemoryUsage::MemoryUsage(size_t memory_bytes
,
995 size_t resource_count
)
996 : memory_bytes_(static_cast<int64
>(memory_bytes
)),
997 resource_count_(static_cast<int>(resource_count
)) {
998 // MemoryUsage is constructed using size_ts, since it deals with memory and
999 // the inputs are typically size_t. However, during the course of usage (in
1000 // particular operator-=) can cause internal values to become negative. Thus,
1001 // member variables are signed.
1002 DCHECK_LE(memory_bytes
,
1003 static_cast<size_t>(std::numeric_limits
<int64
>::max()));
1004 DCHECK_LE(resource_count
,
1005 static_cast<size_t>(std::numeric_limits
<int>::max()));
1009 TileManager::MemoryUsage
TileManager::MemoryUsage::FromConfig(
1010 const gfx::Size
& size
,
1011 ResourceFormat format
) {
1012 // We can use UncheckedMemorySizeBytes here since this is used with a tile
1013 // size which is determined by the compositor (it's at most max texture size).
1014 return MemoryUsage(Resource::UncheckedMemorySizeBytes(size
, format
), 1);
1018 TileManager::MemoryUsage
TileManager::MemoryUsage::FromTile(const Tile
* tile
) {
1019 const TileDrawInfo
& draw_info
= tile
->draw_info();
1020 if (draw_info
.resource_
) {
1021 return MemoryUsage::FromConfig(draw_info
.resource_
->size(),
1022 draw_info
.resource_
->format());
1024 return MemoryUsage();
1027 TileManager::MemoryUsage
& TileManager::MemoryUsage::operator+=(
1028 const MemoryUsage
& other
) {
1029 memory_bytes_
+= other
.memory_bytes_
;
1030 resource_count_
+= other
.resource_count_
;
1034 TileManager::MemoryUsage
& TileManager::MemoryUsage::operator-=(
1035 const MemoryUsage
& other
) {
1036 memory_bytes_
-= other
.memory_bytes_
;
1037 resource_count_
-= other
.resource_count_
;
1041 TileManager::MemoryUsage
TileManager::MemoryUsage::operator-(
1042 const MemoryUsage
& other
) {
1043 MemoryUsage result
= *this;
1048 bool TileManager::MemoryUsage::Exceeds(const MemoryUsage
& limit
) const {
1049 return memory_bytes_
> limit
.memory_bytes_
||
1050 resource_count_
> limit
.resource_count_
;
1053 TileManager::Signals::Signals() {
1057 void TileManager::Signals::reset() {
1058 ready_to_activate
= false;
1059 did_notify_ready_to_activate
= false;
1060 ready_to_draw
= false;
1061 did_notify_ready_to_draw
= false;
1062 all_tile_tasks_completed
= false;
1063 did_notify_all_tile_tasks_completed
= false;