Update SplitString calls to new form
[chromium-blink-merge.git] / cc / tiles / tile_manager.cc
blob6d192a14e7d381ec9de39b06eaa9453810641026
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"
7 #include <algorithm>
8 #include <limits>
9 #include <string>
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"
27 namespace cc {
28 namespace {
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 {
40 public:
41 RasterTaskImpl(
42 const Resource* resource,
43 RasterSource* raster_source,
44 const gfx::Rect& content_rect,
45 const gfx::Rect& invalid_content_rect,
46 float contents_scale,
47 TileResolution tile_resolution,
48 int layer_id,
49 uint64_t source_prepare_tiles_id,
50 const void* tile,
51 uint64_t new_content_id,
52 uint64_t previous_content_id,
53 uint64_t resource_content_id,
54 int source_frame_number,
55 bool analyze_picture,
56 const base::Callback<void(const RasterSource::SolidColorAnalysis&, bool)>&
57 reply,
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),
65 layer_id_(layer_id),
66 source_prepare_tiles_id_(source_prepare_tiles_id),
67 tile_(tile),
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),
73 reply_(reply) {}
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)
86 return;
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());
106 protected:
107 ~RasterTaskImpl() override { DCHECK(!raster_buffer_); }
109 private:
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_,
117 &analysis_);
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_;
142 int layer_id_;
143 uint64_t source_prepare_tiles_id_;
144 const void* tile_;
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)>
151 reply_;
152 scoped_ptr<RasterBuffer> raster_buffer_;
154 DISALLOW_COPY_AND_ASSIGN(RasterTaskImpl);
157 const char* TaskSetName(TaskSet task_set) {
158 switch (task_set) {
159 case TileManager::ALL:
160 return "ALL";
161 case TileManager::REQUIRED_FOR_ACTIVATION:
162 return "REQUIRED_FOR_ACTIVATION";
163 case TileManager::REQUIRED_FOR_DRAW:
164 return "REQUIRED_FOR_DRAW";
167 NOTREACHED();
168 return "Invalid TaskSet";
171 } // namespace
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));
184 return state;
187 // static
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)
200 : client_(client),
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_(
209 task_runner_.get(),
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_)
225 return;
227 global_state_ = GlobalStateThatImpactsTilePriority();
229 TileTaskQueue empty;
230 tile_task_runner_->ScheduleTasks(&empty);
231 orphan_raster_tasks_.clear();
233 // This should finish all pending tasks and release any uninitialized
234 // resources.
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);
280 continue;
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());
288 delete tile;
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_);
299 switch (task_set) {
300 case ALL: {
301 has_scheduled_tile_tasks_ = false;
303 bool memory_usage_above_limit =
304 resource_pool_->total_memory_usage_bytes() >
305 global_state_.soft_memory_limit_in_bytes;
307 if (all_tiles_that_need_to_be_rasterized_are_scheduled_ &&
308 !memory_usage_above_limit) {
309 // TODO(ericrk): We should find a better way to safely handle re-entrant
310 // notifications than always having to schedule a new task.
311 // http://crbug.com/498439
312 signals_.all_tile_tasks_completed = true;
313 signals_check_notifier_.Schedule();
314 return;
317 more_tiles_need_prepare_check_notifier_.Schedule();
318 return;
320 case REQUIRED_FOR_ACTIVATION:
321 signals_.ready_to_activate = true;
322 signals_check_notifier_.Schedule();
323 return;
325 case REQUIRED_FOR_DRAW:
326 signals_.ready_to_draw = true;
327 signals_check_notifier_.Schedule();
328 return;
331 NOTREACHED();
334 bool TileManager::PrepareTiles(
335 const GlobalStateThatImpactsTilePriority& state) {
336 ++prepare_tiles_count_;
338 TRACE_EVENT1("cc", "TileManager::PrepareTiles", "prepare_tiles_id",
339 prepare_tiles_count_);
341 if (!tile_task_runner_) {
342 TRACE_EVENT_INSTANT0("cc", "PrepareTiles aborted",
343 TRACE_EVENT_SCOPE_THREAD);
344 return false;
347 signals_.reset();
348 global_state_ = state;
350 // We need to call CheckForCompletedTasks() once in-between each call
351 // to ScheduleTasks() to prevent canceled tasks from being scheduled.
352 if (!did_check_for_completed_tasks_since_last_schedule_tasks_) {
353 tile_task_runner_->CheckForCompletedTasks();
354 did_check_for_completed_tasks_since_last_schedule_tasks_ = true;
357 FreeResourcesForReleasedTiles();
358 CleanUpReleasedTiles();
360 PrioritizedTileVector tiles_that_need_to_be_rasterized;
361 scoped_ptr<RasterTilePriorityQueue> raster_priority_queue(
362 client_->BuildRasterQueue(global_state_.tree_priority,
363 RasterTilePriorityQueue::Type::ALL));
364 AssignGpuMemoryToTiles(raster_priority_queue.get(),
365 scheduled_raster_task_limit_,
366 &tiles_that_need_to_be_rasterized);
368 // Inform the client that will likely require a draw if the highest priority
369 // tile that will be rasterized is required for draw.
370 client_->SetIsLikelyToRequireADraw(
371 !tiles_that_need_to_be_rasterized.empty() &&
372 tiles_that_need_to_be_rasterized.front().tile()->required_for_draw());
374 // Schedule tile tasks.
375 ScheduleTasks(tiles_that_need_to_be_rasterized);
377 TRACE_EVENT_INSTANT1("cc", "DidPrepareTiles", TRACE_EVENT_SCOPE_THREAD,
378 "state", BasicStateAsValue());
380 TRACE_COUNTER_ID1("cc", "unused_memory_bytes", this,
381 resource_pool_->total_memory_usage_bytes() -
382 resource_pool_->acquired_memory_usage_bytes());
383 return true;
386 void TileManager::Flush() {
387 TRACE_EVENT0("cc", "TileManager::Flush");
389 if (!tile_task_runner_) {
390 TRACE_EVENT_INSTANT0("cc", "Flush aborted", TRACE_EVENT_SCOPE_THREAD);
391 return;
394 tile_task_runner_->CheckForCompletedTasks();
396 did_check_for_completed_tasks_since_last_schedule_tasks_ = true;
398 TRACE_EVENT_INSTANT1("cc", "DidFlush", TRACE_EVENT_SCOPE_THREAD, "stats",
399 RasterTaskCompletionStatsAsValue(flush_stats_));
400 flush_stats_ = RasterTaskCompletionStats();
403 scoped_refptr<base::trace_event::ConvertableToTraceFormat>
404 TileManager::BasicStateAsValue() const {
405 scoped_refptr<base::trace_event::TracedValue> value =
406 new base::trace_event::TracedValue();
407 BasicStateAsValueInto(value.get());
408 return value;
411 void TileManager::BasicStateAsValueInto(
412 base::trace_event::TracedValue* state) const {
413 state->SetInteger("tile_count", base::saturated_cast<int>(tiles_.size()));
414 state->SetBoolean("did_oom_on_last_assign", did_oom_on_last_assign_);
415 state->BeginDictionary("global_state");
416 global_state_.AsValueInto(state);
417 state->EndDictionary();
420 scoped_ptr<EvictionTilePriorityQueue>
421 TileManager::FreeTileResourcesUntilUsageIsWithinLimit(
422 scoped_ptr<EvictionTilePriorityQueue> eviction_priority_queue,
423 const MemoryUsage& limit,
424 MemoryUsage* usage) {
425 while (usage->Exceeds(limit)) {
426 if (!eviction_priority_queue) {
427 eviction_priority_queue =
428 client_->BuildEvictionQueue(global_state_.tree_priority);
430 if (eviction_priority_queue->IsEmpty())
431 break;
433 Tile* tile = eviction_priority_queue->Top().tile();
434 *usage -= MemoryUsage::FromTile(tile);
435 FreeResourcesForTileAndNotifyClientIfTileWasReadyToDraw(tile);
436 eviction_priority_queue->Pop();
438 return eviction_priority_queue;
441 scoped_ptr<EvictionTilePriorityQueue>
442 TileManager::FreeTileResourcesWithLowerPriorityUntilUsageIsWithinLimit(
443 scoped_ptr<EvictionTilePriorityQueue> eviction_priority_queue,
444 const MemoryUsage& limit,
445 const TilePriority& other_priority,
446 MemoryUsage* usage) {
447 while (usage->Exceeds(limit)) {
448 if (!eviction_priority_queue) {
449 eviction_priority_queue =
450 client_->BuildEvictionQueue(global_state_.tree_priority);
452 if (eviction_priority_queue->IsEmpty())
453 break;
455 const PrioritizedTile& prioritized_tile = eviction_priority_queue->Top();
456 if (!other_priority.IsHigherPriorityThan(prioritized_tile.priority()))
457 break;
459 Tile* tile = prioritized_tile.tile();
460 *usage -= MemoryUsage::FromTile(tile);
461 FreeResourcesForTileAndNotifyClientIfTileWasReadyToDraw(tile);
462 eviction_priority_queue->Pop();
464 return eviction_priority_queue;
467 bool TileManager::TilePriorityViolatesMemoryPolicy(
468 const TilePriority& priority) {
469 switch (global_state_.memory_limit_policy) {
470 case ALLOW_NOTHING:
471 return true;
472 case ALLOW_ABSOLUTE_MINIMUM:
473 return priority.priority_bin > TilePriority::NOW;
474 case ALLOW_PREPAINT_ONLY:
475 return priority.priority_bin > TilePriority::SOON;
476 case ALLOW_ANYTHING:
477 return priority.distance_to_visible ==
478 std::numeric_limits<float>::infinity();
480 NOTREACHED();
481 return true;
484 void TileManager::AssignGpuMemoryToTiles(
485 RasterTilePriorityQueue* raster_priority_queue,
486 size_t scheduled_raster_task_limit,
487 PrioritizedTileVector* tiles_that_need_to_be_rasterized) {
488 TRACE_EVENT_BEGIN0("cc", "TileManager::AssignGpuMemoryToTiles");
490 DCHECK(resource_pool_);
491 DCHECK(tile_task_runner_);
493 // Maintain the list of released resources that can potentially be re-used
494 // or deleted. If this operation becomes expensive too, only do this after
495 // some resource(s) was returned. Note that in that case, one also need to
496 // invalidate when releasing some resource from the pool.
497 resource_pool_->CheckBusyResources();
499 // Now give memory out to the tiles until we're out, and build
500 // the needs-to-be-rasterized queue.
501 unsigned schedule_priority = 1u;
502 all_tiles_that_need_to_be_rasterized_are_scheduled_ = true;
503 bool had_enough_memory_to_schedule_tiles_needed_now = true;
505 MemoryUsage hard_memory_limit(global_state_.hard_memory_limit_in_bytes,
506 global_state_.num_resources_limit);
507 MemoryUsage soft_memory_limit(global_state_.soft_memory_limit_in_bytes,
508 global_state_.num_resources_limit);
509 MemoryUsage memory_usage(resource_pool_->acquired_memory_usage_bytes(),
510 resource_pool_->acquired_resource_count());
512 scoped_ptr<EvictionTilePriorityQueue> eviction_priority_queue;
513 for (; !raster_priority_queue->IsEmpty(); raster_priority_queue->Pop()) {
514 const PrioritizedTile& prioritized_tile = raster_priority_queue->Top();
515 Tile* tile = prioritized_tile.tile();
516 TilePriority priority = prioritized_tile.priority();
518 if (TilePriorityViolatesMemoryPolicy(priority)) {
519 TRACE_EVENT_INSTANT0(
520 "cc", "TileManager::AssignGpuMemory tile violates memory policy",
521 TRACE_EVENT_SCOPE_THREAD);
522 break;
525 // We won't be able to schedule this tile, so break out early.
526 if (tiles_that_need_to_be_rasterized->size() >=
527 scheduled_raster_task_limit) {
528 all_tiles_that_need_to_be_rasterized_are_scheduled_ = false;
529 break;
532 tile->scheduled_priority_ = schedule_priority++;
534 DCHECK_IMPLIES(tile->draw_info().mode() != TileDrawInfo::OOM_MODE,
535 !tile->draw_info().IsReadyToDraw());
537 // If the tile already has a raster_task, then the memory used by it is
538 // already accounted for in memory_usage. Otherwise, we'll have to acquire
539 // more memory to create a raster task.
540 MemoryUsage memory_required_by_tile_to_be_scheduled;
541 if (!tile->raster_task_.get()) {
542 memory_required_by_tile_to_be_scheduled = MemoryUsage::FromConfig(
543 tile->desired_texture_size(), tile_task_runner_->GetResourceFormat());
546 bool tile_is_needed_now = priority.priority_bin == TilePriority::NOW;
548 // This is the memory limit that will be used by this tile. Depending on
549 // the tile priority, it will be one of hard_memory_limit or
550 // soft_memory_limit.
551 MemoryUsage& tile_memory_limit =
552 tile_is_needed_now ? hard_memory_limit : soft_memory_limit;
554 const MemoryUsage& scheduled_tile_memory_limit =
555 tile_memory_limit - memory_required_by_tile_to_be_scheduled;
556 eviction_priority_queue =
557 FreeTileResourcesWithLowerPriorityUntilUsageIsWithinLimit(
558 eviction_priority_queue.Pass(), scheduled_tile_memory_limit,
559 priority, &memory_usage);
560 bool memory_usage_is_within_limit =
561 !memory_usage.Exceeds(scheduled_tile_memory_limit);
563 // If we couldn't fit the tile into our current memory limit, then we're
564 // done.
565 if (!memory_usage_is_within_limit) {
566 if (tile_is_needed_now)
567 had_enough_memory_to_schedule_tiles_needed_now = false;
568 all_tiles_that_need_to_be_rasterized_are_scheduled_ = false;
569 break;
572 memory_usage += memory_required_by_tile_to_be_scheduled;
573 tiles_that_need_to_be_rasterized->push_back(prioritized_tile);
576 // Note that we should try and further reduce memory in case the above loop
577 // didn't reduce memory. This ensures that we always release as many resources
578 // as possible to stay within the memory limit.
579 eviction_priority_queue = FreeTileResourcesUntilUsageIsWithinLimit(
580 eviction_priority_queue.Pass(), hard_memory_limit, &memory_usage);
582 UMA_HISTOGRAM_BOOLEAN("TileManager.ExceededMemoryBudget",
583 !had_enough_memory_to_schedule_tiles_needed_now);
584 did_oom_on_last_assign_ = !had_enough_memory_to_schedule_tiles_needed_now;
586 memory_stats_from_last_assign_.total_budget_in_bytes =
587 global_state_.hard_memory_limit_in_bytes;
588 memory_stats_from_last_assign_.total_bytes_used = memory_usage.memory_bytes();
589 DCHECK_GE(memory_stats_from_last_assign_.total_bytes_used, 0);
590 memory_stats_from_last_assign_.had_enough_memory =
591 had_enough_memory_to_schedule_tiles_needed_now;
593 TRACE_EVENT_END2("cc", "TileManager::AssignGpuMemoryToTiles",
594 "all_tiles_that_need_to_be_rasterized_are_scheduled",
595 all_tiles_that_need_to_be_rasterized_are_scheduled_,
596 "had_enough_memory_to_schedule_tiles_needed_now",
597 had_enough_memory_to_schedule_tiles_needed_now);
600 void TileManager::FreeResourcesForTile(Tile* tile) {
601 TileDrawInfo& draw_info = tile->draw_info();
602 if (draw_info.resource_)
603 resource_pool_->ReleaseResource(draw_info.resource_.Pass(), tile->id());
606 void TileManager::FreeResourcesForTileAndNotifyClientIfTileWasReadyToDraw(
607 Tile* tile) {
608 bool was_ready_to_draw = tile->draw_info().IsReadyToDraw();
609 FreeResourcesForTile(tile);
610 if (was_ready_to_draw)
611 client_->NotifyTileStateChanged(tile);
614 void TileManager::ScheduleTasks(
615 const PrioritizedTileVector& tiles_that_need_to_be_rasterized) {
616 TRACE_EVENT1("cc",
617 "TileManager::ScheduleTasks",
618 "count",
619 tiles_that_need_to_be_rasterized.size());
621 DCHECK(did_check_for_completed_tasks_since_last_schedule_tasks_);
623 raster_queue_.Reset();
625 // Even when scheduling an empty set of tiles, the TTWP does some work, and
626 // will always trigger a DidFinishRunningTileTasks notification. Because of
627 // this we unconditionally set |has_scheduled_tile_tasks_| to true.
628 has_scheduled_tile_tasks_ = true;
630 // Build a new task queue containing all task currently needed. Tasks
631 // are added in order of priority, highest priority task first.
632 for (auto& prioritized_tile : tiles_that_need_to_be_rasterized) {
633 Tile* tile = prioritized_tile.tile();
635 DCHECK(tile->draw_info().requires_resource());
636 DCHECK(!tile->draw_info().resource_);
638 if (!tile->raster_task_.get())
639 tile->raster_task_ = CreateRasterTask(prioritized_tile);
641 TaskSetCollection task_sets;
642 if (tile->required_for_activation())
643 task_sets.set(REQUIRED_FOR_ACTIVATION);
644 if (tile->required_for_draw())
645 task_sets.set(REQUIRED_FOR_DRAW);
646 task_sets.set(ALL);
647 raster_queue_.items.push_back(
648 TileTaskQueue::Item(tile->raster_task_.get(), task_sets));
651 // We must reduce the amount of unused resoruces before calling
652 // ScheduleTasks to prevent usage from rising above limits.
653 resource_pool_->ReduceResourceUsage();
655 // Schedule running of |raster_queue_|. This replaces any previously
656 // scheduled tasks and effectively cancels all tasks not present
657 // in |raster_queue_|.
658 tile_task_runner_->ScheduleTasks(&raster_queue_);
660 // It's now safe to clean up orphan tasks as raster worker pool is not
661 // allowed to keep around unreferenced raster tasks after ScheduleTasks() has
662 // been called.
663 orphan_raster_tasks_.clear();
665 did_check_for_completed_tasks_since_last_schedule_tasks_ = false;
668 scoped_refptr<RasterTask> TileManager::CreateRasterTask(
669 const PrioritizedTile& prioritized_tile) {
670 Tile* tile = prioritized_tile.tile();
671 uint64_t resource_content_id = 0;
672 scoped_ptr<ScopedResource> resource;
673 if (tile->invalidated_id()) {
674 // TODO(danakj): For resources that are in use, we should still grab them
675 // and copy from them instead of rastering everything. crbug.com/492754
676 resource =
677 resource_pool_->TryAcquireResourceWithContentId(tile->invalidated_id());
679 if (resource) {
680 resource_content_id = tile->invalidated_id();
681 DCHECK_EQ(tile_task_runner_->GetResourceFormat(), resource->format());
682 DCHECK_EQ(tile->desired_texture_size().ToString(),
683 resource->size().ToString());
684 } else {
685 resource = resource_pool_->AcquireResource(
686 tile->desired_texture_size(), tile_task_runner_->GetResourceFormat());
688 const ScopedResource* const_resource = resource.get();
690 // Create and queue all image decode tasks that this tile depends on.
691 ImageDecodeTask::Vector decode_tasks;
692 std::vector<SkPixelRef*> pixel_refs;
693 prioritized_tile.raster_source()->GatherPixelRefs(
694 tile->content_rect(), tile->contents_scale(), &pixel_refs);
695 for (SkPixelRef* pixel_ref : pixel_refs) {
696 decode_tasks.push_back(image_decode_controller_.GetTaskForPixelRef(
697 pixel_ref, tile->layer_id(), prepare_tiles_count_));
700 return make_scoped_refptr(new RasterTaskImpl(
701 const_resource, prioritized_tile.raster_source(), tile->content_rect(),
702 tile->invalidated_content_rect(), tile->contents_scale(),
703 prioritized_tile.priority().resolution, tile->layer_id(),
704 prepare_tiles_count_, static_cast<const void*>(tile), tile->id(),
705 tile->invalidated_id(), resource_content_id, tile->source_frame_number(),
706 tile->use_picture_analysis(),
707 base::Bind(&TileManager::OnRasterTaskCompleted, base::Unretained(this),
708 tile->id(), base::Passed(&resource)),
709 &decode_tasks));
712 void TileManager::OnRasterTaskCompleted(
713 Tile::Id tile_id,
714 scoped_ptr<ScopedResource> resource,
715 const RasterSource::SolidColorAnalysis& analysis,
716 bool was_canceled) {
717 DCHECK(tiles_.find(tile_id) != tiles_.end());
719 Tile* tile = tiles_[tile_id];
720 DCHECK(tile->raster_task_.get());
721 orphan_raster_tasks_.push_back(tile->raster_task_);
722 tile->raster_task_ = nullptr;
724 if (was_canceled) {
725 ++flush_stats_.canceled_count;
726 resource_pool_->ReleaseResource(resource.Pass(), tile->invalidated_id());
727 return;
730 UpdateTileDrawInfo(tile, resource.Pass(), analysis);
733 void TileManager::UpdateTileDrawInfo(
734 Tile* tile,
735 scoped_ptr<ScopedResource> resource,
736 const RasterSource::SolidColorAnalysis& analysis) {
737 TileDrawInfo& draw_info = tile->draw_info();
739 ++flush_stats_.completed_count;
741 if (analysis.is_solid_color) {
742 draw_info.set_solid_color(analysis.solid_color);
743 if (resource) {
744 // Pass the old tile id here because the tile is solid color so we did not
745 // raster anything into the tile resource.
746 resource_pool_->ReleaseResource(resource.Pass(), tile->invalidated_id());
748 } else {
749 DCHECK(resource);
750 draw_info.set_use_resource();
751 draw_info.resource_ = resource.Pass();
752 draw_info.contents_swizzled_ =
753 tile_task_runner_->GetResourceRequiresSwizzle();
755 DCHECK(draw_info.IsReadyToDraw());
756 draw_info.set_was_ever_ready_to_draw();
758 client_->NotifyTileStateChanged(tile);
761 ScopedTilePtr TileManager::CreateTile(const gfx::Size& desired_texture_size,
762 const gfx::Rect& content_rect,
763 float contents_scale,
764 int layer_id,
765 int source_frame_number,
766 int flags) {
767 // We need to have a tile task worker pool to do anything meaningful with
768 // tiles.
769 DCHECK(tile_task_runner_);
770 ScopedTilePtr tile(new Tile(this, desired_texture_size, content_rect,
771 contents_scale, layer_id, source_frame_number,
772 flags));
773 DCHECK(tiles_.find(tile->id()) == tiles_.end());
775 tiles_[tile->id()] = tile.get();
776 image_decode_controller_.AddLayerUsedCount(tile->layer_id());
777 return tile;
780 void TileManager::SetTileTaskRunnerForTesting(
781 TileTaskRunner* tile_task_runner) {
782 tile_task_runner_ = tile_task_runner;
783 tile_task_runner_->SetClient(this);
786 bool TileManager::AreRequiredTilesReadyToDraw(
787 RasterTilePriorityQueue::Type type) const {
788 scoped_ptr<RasterTilePriorityQueue> raster_priority_queue(
789 client_->BuildRasterQueue(global_state_.tree_priority, type));
790 // It is insufficient to check whether the raster queue we constructed is
791 // empty. The reason for this is that there are situations (rasterize on
792 // demand) when the tile both needs raster and it's ready to draw. Hence, we
793 // have to iterate the queue to check whether the required tiles are ready to
794 // draw.
795 for (; !raster_priority_queue->IsEmpty(); raster_priority_queue->Pop()) {
796 if (!raster_priority_queue->Top().tile()->draw_info().IsReadyToDraw())
797 return false;
800 #if DCHECK_IS_ON()
801 scoped_ptr<RasterTilePriorityQueue> all_queue(
802 client_->BuildRasterQueue(global_state_.tree_priority, type));
803 for (; !all_queue->IsEmpty(); all_queue->Pop()) {
804 Tile* tile = all_queue->Top().tile();
805 DCHECK_IMPLIES(tile->required_for_activation(),
806 tile->draw_info().IsReadyToDraw());
808 #endif
809 return true;
812 bool TileManager::IsReadyToActivate() const {
813 TRACE_EVENT0("cc", "TileManager::IsReadyToActivate");
814 return AreRequiredTilesReadyToDraw(
815 RasterTilePriorityQueue::Type::REQUIRED_FOR_ACTIVATION);
818 bool TileManager::IsReadyToDraw() const {
819 TRACE_EVENT0("cc", "TileManager::IsReadyToDraw");
820 return AreRequiredTilesReadyToDraw(
821 RasterTilePriorityQueue::Type::REQUIRED_FOR_DRAW);
824 void TileManager::CheckAndIssueSignals() {
825 TRACE_EVENT0("cc", "TileManager::CheckAndIssueSignals");
826 tile_task_runner_->CheckForCompletedTasks();
827 did_check_for_completed_tasks_since_last_schedule_tasks_ = true;
829 // Ready to activate.
830 if (signals_.ready_to_activate && !signals_.did_notify_ready_to_activate) {
831 signals_.ready_to_activate = false;
832 if (IsReadyToActivate()) {
833 TRACE_EVENT0("cc",
834 "TileManager::CheckAndIssueSignals - ready to activate");
835 signals_.did_notify_ready_to_activate = true;
836 client_->NotifyReadyToActivate();
840 // Ready to draw.
841 if (signals_.ready_to_draw && !signals_.did_notify_ready_to_draw) {
842 signals_.ready_to_draw = false;
843 if (IsReadyToDraw()) {
844 TRACE_EVENT0("cc", "TileManager::CheckAndIssueSignals - ready to draw");
845 signals_.did_notify_ready_to_draw = true;
846 client_->NotifyReadyToDraw();
850 // All tile tasks completed.
851 if (signals_.all_tile_tasks_completed &&
852 !signals_.did_notify_all_tile_tasks_completed) {
853 signals_.all_tile_tasks_completed = false;
854 if (!has_scheduled_tile_tasks_) {
855 TRACE_EVENT0(
856 "cc", "TileManager::CheckAndIssueSignals - all tile tasks completed");
857 signals_.did_notify_all_tile_tasks_completed = true;
858 client_->NotifyAllTileTasksCompleted();
863 void TileManager::CheckIfMoreTilesNeedToBePrepared() {
864 tile_task_runner_->CheckForCompletedTasks();
865 did_check_for_completed_tasks_since_last_schedule_tasks_ = true;
867 // When OOM, keep re-assigning memory until we reach a steady state
868 // where top-priority tiles are initialized.
869 PrioritizedTileVector tiles_that_need_to_be_rasterized;
870 scoped_ptr<RasterTilePriorityQueue> raster_priority_queue(
871 client_->BuildRasterQueue(global_state_.tree_priority,
872 RasterTilePriorityQueue::Type::ALL));
873 AssignGpuMemoryToTiles(raster_priority_queue.get(),
874 scheduled_raster_task_limit_,
875 &tiles_that_need_to_be_rasterized);
877 // Inform the client that will likely require a draw if the highest priority
878 // tile that will be rasterized is required for draw.
879 client_->SetIsLikelyToRequireADraw(
880 !tiles_that_need_to_be_rasterized.empty() &&
881 tiles_that_need_to_be_rasterized.front().tile()->required_for_draw());
883 // |tiles_that_need_to_be_rasterized| will be empty when we reach a
884 // steady memory state. Keep scheduling tasks until we reach this state.
885 if (!tiles_that_need_to_be_rasterized.empty()) {
886 ScheduleTasks(tiles_that_need_to_be_rasterized);
887 return;
890 FreeResourcesForReleasedTiles();
892 resource_pool_->ReduceResourceUsage();
894 signals_.all_tile_tasks_completed = true;
895 signals_check_notifier_.Schedule();
897 // We don't reserve memory for required-for-activation tiles during
898 // accelerated gestures, so we just postpone activation when we don't
899 // have these tiles, and activate after the accelerated gesture.
900 // Likewise if we don't allow any tiles (as is the case when we're
901 // invisible), if we have tiles that aren't ready, then we shouldn't
902 // activate as activation can cause checkerboards.
903 bool wait_for_all_required_tiles =
904 global_state_.tree_priority == SMOOTHNESS_TAKES_PRIORITY ||
905 global_state_.memory_limit_policy == ALLOW_NOTHING;
907 // Mark any required-for-activation tiles that have not been been assigned
908 // memory after reaching a steady memory state as OOM. This ensures that we
909 // activate even when OOM. Note that we can't reuse the queue we used for
910 // AssignGpuMemoryToTiles, since the AssignGpuMemoryToTiles call could have
911 // evicted some tiles that would not be picked up by the old raster queue.
912 scoped_ptr<RasterTilePriorityQueue> required_for_activation_queue(
913 client_->BuildRasterQueue(
914 global_state_.tree_priority,
915 RasterTilePriorityQueue::Type::REQUIRED_FOR_ACTIVATION));
917 // If we have tiles left to raster for activation, and we don't allow
918 // activating without them, then skip activation and return early.
919 if (!required_for_activation_queue->IsEmpty() && wait_for_all_required_tiles)
920 return;
922 // Mark required tiles as OOM so that we can activate without them.
923 for (; !required_for_activation_queue->IsEmpty();
924 required_for_activation_queue->Pop()) {
925 Tile* tile = required_for_activation_queue->Top().tile();
926 tile->draw_info().set_oom();
927 client_->NotifyTileStateChanged(tile);
930 DCHECK(IsReadyToActivate());
931 // TODO(ericrk): Investigate why we need to schedule this (not just call it
932 // inline). http://crbug.com/498439
933 signals_.ready_to_activate = true;
934 signals_check_notifier_.Schedule();
937 TileManager::MemoryUsage::MemoryUsage() : memory_bytes_(0), resource_count_(0) {
940 TileManager::MemoryUsage::MemoryUsage(size_t memory_bytes,
941 size_t resource_count)
942 : memory_bytes_(static_cast<int64>(memory_bytes)),
943 resource_count_(static_cast<int>(resource_count)) {
944 // MemoryUsage is constructed using size_ts, since it deals with memory and
945 // the inputs are typically size_t. However, during the course of usage (in
946 // particular operator-=) can cause internal values to become negative. Thus,
947 // member variables are signed.
948 DCHECK_LE(memory_bytes,
949 static_cast<size_t>(std::numeric_limits<int64>::max()));
950 DCHECK_LE(resource_count,
951 static_cast<size_t>(std::numeric_limits<int>::max()));
954 // static
955 TileManager::MemoryUsage TileManager::MemoryUsage::FromConfig(
956 const gfx::Size& size,
957 ResourceFormat format) {
958 // We can use UncheckedSizeInBytes here since this is used with a tile
959 // size which is determined by the compositor (it's at most max texture size).
960 return MemoryUsage(ResourceUtil::UncheckedSizeInBytes<size_t>(size, format),
964 // static
965 TileManager::MemoryUsage TileManager::MemoryUsage::FromTile(const Tile* tile) {
966 const TileDrawInfo& draw_info = tile->draw_info();
967 if (draw_info.resource_) {
968 return MemoryUsage::FromConfig(draw_info.resource_->size(),
969 draw_info.resource_->format());
971 return MemoryUsage();
974 TileManager::MemoryUsage& TileManager::MemoryUsage::operator+=(
975 const MemoryUsage& other) {
976 memory_bytes_ += other.memory_bytes_;
977 resource_count_ += other.resource_count_;
978 return *this;
981 TileManager::MemoryUsage& TileManager::MemoryUsage::operator-=(
982 const MemoryUsage& other) {
983 memory_bytes_ -= other.memory_bytes_;
984 resource_count_ -= other.resource_count_;
985 return *this;
988 TileManager::MemoryUsage TileManager::MemoryUsage::operator-(
989 const MemoryUsage& other) {
990 MemoryUsage result = *this;
991 result -= other;
992 return result;
995 bool TileManager::MemoryUsage::Exceeds(const MemoryUsage& limit) const {
996 return memory_bytes_ > limit.memory_bytes_ ||
997 resource_count_ > limit.resource_count_;
1000 TileManager::Signals::Signals() {
1001 reset();
1004 void TileManager::Signals::reset() {
1005 ready_to_activate = false;
1006 did_notify_ready_to_activate = false;
1007 ready_to_draw = false;
1008 did_notify_ready_to_draw = false;
1009 all_tile_tasks_completed = false;
1010 did_notify_all_tile_tasks_completed = false;
1013 } // namespace cc