[refactor] More post-NSS WebCrypto cleanups (utility functions).
[chromium-blink-merge.git] / cc / tiles / tile_manager.cc
blob6f798e67c6a822ba173ffa0439fad3bbca6a4d64
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.%s.RasterTask.RasterUs",
37 "Compositing.%s.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(dependencies),
60 resource_(resource),
61 raster_source_(raster_source),
62 content_rect_(content_rect),
63 invalid_content_rect_(invalid_content_rect),
64 contents_scale_(contents_scale),
65 tile_resolution_(tile_resolution),
66 layer_id_(layer_id),
67 source_prepare_tiles_id_(source_prepare_tiles_id),
68 tile_(tile),
69 new_content_id_(new_content_id),
70 previous_content_id_(previous_content_id),
71 resource_content_id_(resource_content_id),
72 source_frame_number_(source_frame_number),
73 analyze_picture_(analyze_picture),
74 reply_(reply) {}
76 // Overridden from Task:
77 void RunOnWorkerThread() override {
78 TRACE_EVENT1("cc", "RasterizerTaskImpl::RunOnWorkerThread",
79 "source_prepare_tiles_id", source_prepare_tiles_id_);
81 DCHECK(raster_source_.get());
82 DCHECK(raster_buffer_);
84 if (analyze_picture_) {
85 Analyze(raster_source_.get());
86 if (analysis_.is_solid_color)
87 return;
90 Raster(raster_source_.get());
93 // Overridden from TileTask:
94 void ScheduleOnOriginThread(TileTaskClient* client) override {
95 DCHECK(!raster_buffer_);
96 raster_buffer_ = client->AcquireBufferForRaster(
97 resource_, resource_content_id_, previous_content_id_);
99 void CompleteOnOriginThread(TileTaskClient* client) override {
100 client->ReleaseBufferForRaster(raster_buffer_.Pass());
101 reply_.Run(analysis_, !HasFinishedRunning());
104 protected:
105 ~RasterTaskImpl() override { DCHECK(!raster_buffer_); }
107 private:
108 void Analyze(const RasterSource* raster_source) {
109 frame_viewer_instrumentation::ScopedAnalyzeTask analyze_task(
110 tile_, tile_resolution_, source_frame_number_, layer_id_);
112 DCHECK(raster_source);
114 raster_source->PerformSolidColorAnalysis(content_rect_, contents_scale_,
115 &analysis_);
116 // Clear the flag if we're not using the estimator.
117 analysis_.is_solid_color &= kUseColorEstimator;
120 void Raster(const RasterSource* raster_source) {
121 frame_viewer_instrumentation::ScopedRasterTask raster_task(
122 tile_, tile_resolution_, source_frame_number_, layer_id_);
123 ScopedRasterTaskTimer timer;
124 timer.SetArea(content_rect_.size().GetArea());
126 DCHECK(raster_source);
128 bool include_images = tile_resolution_ != LOW_RESOLUTION;
129 raster_buffer_->Playback(raster_source_.get(), content_rect_,
130 invalid_content_rect_, new_content_id_,
131 contents_scale_, include_images);
134 const Resource* resource_;
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_;
141 int layer_id_;
142 uint64_t source_prepare_tiles_id_;
143 const void* tile_;
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)>
150 reply_;
151 scoped_ptr<RasterBuffer> raster_buffer_;
153 DISALLOW_COPY_AND_ASSIGN(RasterTaskImpl);
156 const char* TaskSetName(TaskSet task_set) {
157 switch (task_set) {
158 case TileManager::ALL:
159 return "ALL";
160 case TileManager::REQUIRED_FOR_ACTIVATION:
161 return "REQUIRED_FOR_ACTIVATION";
162 case TileManager::REQUIRED_FOR_DRAW:
163 return "REQUIRED_FOR_DRAW";
166 NOTREACHED();
167 return "Invalid TaskSet";
170 } // namespace
172 RasterTaskCompletionStats::RasterTaskCompletionStats()
173 : completed_count(0u), canceled_count(0u) {}
175 scoped_refptr<base::trace_event::ConvertableToTraceFormat>
176 RasterTaskCompletionStatsAsValue(const RasterTaskCompletionStats& stats) {
177 scoped_refptr<base::trace_event::TracedValue> state =
178 new base::trace_event::TracedValue();
179 state->SetInteger("completed_count",
180 base::saturated_cast<int>(stats.completed_count));
181 state->SetInteger("canceled_count",
182 base::saturated_cast<int>(stats.canceled_count));
183 return state;
186 // static
187 scoped_ptr<TileManager> TileManager::Create(
188 TileManagerClient* client,
189 base::SequencedTaskRunner* task_runner,
190 size_t scheduled_raster_task_limit) {
191 return make_scoped_ptr(
192 new TileManager(client, task_runner, scheduled_raster_task_limit));
195 TileManager::TileManager(
196 TileManagerClient* client,
197 const scoped_refptr<base::SequencedTaskRunner>& task_runner,
198 size_t scheduled_raster_task_limit)
199 : client_(client),
200 task_runner_(task_runner),
201 resource_pool_(nullptr),
202 tile_task_runner_(nullptr),
203 scheduled_raster_task_limit_(scheduled_raster_task_limit),
204 all_tiles_that_need_to_be_rasterized_are_scheduled_(true),
205 did_check_for_completed_tasks_since_last_schedule_tasks_(true),
206 did_oom_on_last_assign_(false),
207 more_tiles_need_prepare_check_notifier_(
208 task_runner_.get(),
209 base::Bind(&TileManager::CheckIfMoreTilesNeedToBePrepared,
210 base::Unretained(this))),
211 signals_check_notifier_(task_runner_.get(),
212 base::Bind(&TileManager::CheckAndIssueSignals,
213 base::Unretained(this))),
214 has_scheduled_tile_tasks_(false),
215 prepare_tiles_count_(0u),
216 next_tile_id_(0u) {}
218 TileManager::~TileManager() {
219 FinishTasksAndCleanUp();
222 void TileManager::FinishTasksAndCleanUp() {
223 if (!tile_task_runner_)
224 return;
226 global_state_ = GlobalStateThatImpactsTilePriority();
228 TileTaskQueue empty;
229 tile_task_runner_->ScheduleTasks(&empty);
230 orphan_raster_tasks_.clear();
232 // This should finish all pending tasks and release any uninitialized
233 // resources.
234 tile_task_runner_->Shutdown();
235 tile_task_runner_->CheckForCompletedTasks();
237 FreeResourcesForReleasedTiles();
238 CleanUpReleasedTiles();
240 tile_task_runner_ = nullptr;
241 resource_pool_ = nullptr;
242 more_tiles_need_prepare_check_notifier_.Cancel();
243 signals_check_notifier_.Cancel();
246 void TileManager::SetResources(ResourcePool* resource_pool,
247 TileTaskRunner* tile_task_runner,
248 size_t scheduled_raster_task_limit) {
249 DCHECK(!tile_task_runner_);
250 DCHECK(tile_task_runner);
252 scheduled_raster_task_limit_ = scheduled_raster_task_limit;
253 resource_pool_ = resource_pool;
254 tile_task_runner_ = tile_task_runner;
255 tile_task_runner_->SetClient(this);
258 void TileManager::Release(Tile* tile) {
259 released_tiles_.push_back(tile);
262 void TileManager::FreeResourcesForReleasedTiles() {
263 for (auto* tile : released_tiles_)
264 FreeResourcesForTile(tile);
267 void TileManager::CleanUpReleasedTiles() {
268 std::vector<Tile*> tiles_to_retain;
269 for (auto* tile : released_tiles_) {
270 if (tile->HasRasterTask()) {
271 tiles_to_retain.push_back(tile);
272 continue;
275 DCHECK(!tile->draw_info().has_resource());
276 DCHECK(tiles_.find(tile->id()) != tiles_.end());
277 tiles_.erase(tile->id());
279 image_decode_controller_.SubtractLayerUsedCount(tile->layer_id());
280 delete tile;
282 released_tiles_.swap(tiles_to_retain);
285 void TileManager::DidFinishRunningTileTasks(TaskSet task_set) {
286 TRACE_EVENT1("cc", "TileManager::DidFinishRunningTileTasks", "task_set",
287 TaskSetName(task_set));
288 DCHECK(resource_pool_);
289 DCHECK(tile_task_runner_);
291 switch (task_set) {
292 case ALL: {
293 has_scheduled_tile_tasks_ = false;
295 bool memory_usage_above_limit = resource_pool_->memory_usage_bytes() >
296 global_state_.soft_memory_limit_in_bytes;
298 if (all_tiles_that_need_to_be_rasterized_are_scheduled_ &&
299 !memory_usage_above_limit) {
300 // TODO(ericrk): We should find a better way to safely handle re-entrant
301 // notifications than always having to schedule a new task.
302 // http://crbug.com/498439
303 signals_.all_tile_tasks_completed = true;
304 signals_check_notifier_.Schedule();
305 return;
308 more_tiles_need_prepare_check_notifier_.Schedule();
309 return;
311 case REQUIRED_FOR_ACTIVATION:
312 signals_.ready_to_activate = true;
313 signals_check_notifier_.Schedule();
314 return;
316 case REQUIRED_FOR_DRAW:
317 signals_.ready_to_draw = true;
318 signals_check_notifier_.Schedule();
319 return;
322 NOTREACHED();
325 bool TileManager::PrepareTiles(
326 const GlobalStateThatImpactsTilePriority& state) {
327 ++prepare_tiles_count_;
329 TRACE_EVENT1("cc", "TileManager::PrepareTiles", "prepare_tiles_id",
330 prepare_tiles_count_);
332 if (!tile_task_runner_) {
333 TRACE_EVENT_INSTANT0("cc", "PrepareTiles aborted",
334 TRACE_EVENT_SCOPE_THREAD);
335 return false;
338 signals_.reset();
339 global_state_ = state;
341 // We need to call CheckForCompletedTasks() once in-between each call
342 // to ScheduleTasks() to prevent canceled tasks from being scheduled.
343 if (!did_check_for_completed_tasks_since_last_schedule_tasks_) {
344 tile_task_runner_->CheckForCompletedTasks();
345 did_check_for_completed_tasks_since_last_schedule_tasks_ = true;
348 FreeResourcesForReleasedTiles();
349 CleanUpReleasedTiles();
351 PrioritizedTileVector tiles_that_need_to_be_rasterized;
352 scoped_ptr<RasterTilePriorityQueue> raster_priority_queue(
353 client_->BuildRasterQueue(global_state_.tree_priority,
354 RasterTilePriorityQueue::Type::ALL));
355 AssignGpuMemoryToTiles(raster_priority_queue.get(),
356 scheduled_raster_task_limit_,
357 &tiles_that_need_to_be_rasterized);
359 // Inform the client that will likely require a draw if the highest priority
360 // tile that will be rasterized is required for draw.
361 client_->SetIsLikelyToRequireADraw(
362 !tiles_that_need_to_be_rasterized.empty() &&
363 tiles_that_need_to_be_rasterized.front().tile()->required_for_draw());
365 // Schedule tile tasks.
366 ScheduleTasks(tiles_that_need_to_be_rasterized);
368 TRACE_EVENT_INSTANT1("cc", "DidPrepareTiles", TRACE_EVENT_SCOPE_THREAD,
369 "state", BasicStateAsValue());
370 return true;
373 void TileManager::Flush() {
374 TRACE_EVENT0("cc", "TileManager::Flush");
376 if (!tile_task_runner_) {
377 TRACE_EVENT_INSTANT0("cc", "Flush aborted", TRACE_EVENT_SCOPE_THREAD);
378 return;
381 tile_task_runner_->CheckForCompletedTasks();
383 did_check_for_completed_tasks_since_last_schedule_tasks_ = true;
385 TRACE_EVENT_INSTANT1("cc", "DidFlush", TRACE_EVENT_SCOPE_THREAD, "stats",
386 RasterTaskCompletionStatsAsValue(flush_stats_));
387 flush_stats_ = RasterTaskCompletionStats();
390 scoped_refptr<base::trace_event::ConvertableToTraceFormat>
391 TileManager::BasicStateAsValue() const {
392 scoped_refptr<base::trace_event::TracedValue> value =
393 new base::trace_event::TracedValue();
394 BasicStateAsValueInto(value.get());
395 return value;
398 void TileManager::BasicStateAsValueInto(
399 base::trace_event::TracedValue* state) const {
400 state->SetInteger("tile_count", base::saturated_cast<int>(tiles_.size()));
401 state->SetBoolean("did_oom_on_last_assign", did_oom_on_last_assign_);
402 state->BeginDictionary("global_state");
403 global_state_.AsValueInto(state);
404 state->EndDictionary();
407 scoped_ptr<EvictionTilePriorityQueue>
408 TileManager::FreeTileResourcesUntilUsageIsWithinLimit(
409 scoped_ptr<EvictionTilePriorityQueue> eviction_priority_queue,
410 const MemoryUsage& limit,
411 MemoryUsage* usage) {
412 while (usage->Exceeds(limit)) {
413 if (!eviction_priority_queue) {
414 eviction_priority_queue =
415 client_->BuildEvictionQueue(global_state_.tree_priority);
417 if (eviction_priority_queue->IsEmpty())
418 break;
420 Tile* tile = eviction_priority_queue->Top().tile();
421 *usage -= MemoryUsage::FromTile(tile);
422 FreeResourcesForTileAndNotifyClientIfTileWasReadyToDraw(tile);
423 eviction_priority_queue->Pop();
425 return eviction_priority_queue;
428 scoped_ptr<EvictionTilePriorityQueue>
429 TileManager::FreeTileResourcesWithLowerPriorityUntilUsageIsWithinLimit(
430 scoped_ptr<EvictionTilePriorityQueue> eviction_priority_queue,
431 const MemoryUsage& limit,
432 const TilePriority& other_priority,
433 MemoryUsage* usage) {
434 while (usage->Exceeds(limit)) {
435 if (!eviction_priority_queue) {
436 eviction_priority_queue =
437 client_->BuildEvictionQueue(global_state_.tree_priority);
439 if (eviction_priority_queue->IsEmpty())
440 break;
442 const PrioritizedTile& prioritized_tile = eviction_priority_queue->Top();
443 if (!other_priority.IsHigherPriorityThan(prioritized_tile.priority()))
444 break;
446 Tile* tile = prioritized_tile.tile();
447 *usage -= MemoryUsage::FromTile(tile);
448 FreeResourcesForTileAndNotifyClientIfTileWasReadyToDraw(tile);
449 eviction_priority_queue->Pop();
451 return eviction_priority_queue;
454 bool TileManager::TilePriorityViolatesMemoryPolicy(
455 const TilePriority& priority) {
456 switch (global_state_.memory_limit_policy) {
457 case ALLOW_NOTHING:
458 return true;
459 case ALLOW_ABSOLUTE_MINIMUM:
460 return priority.priority_bin > TilePriority::NOW;
461 case ALLOW_PREPAINT_ONLY:
462 return priority.priority_bin > TilePriority::SOON;
463 case ALLOW_ANYTHING:
464 return priority.distance_to_visible ==
465 std::numeric_limits<float>::infinity();
467 NOTREACHED();
468 return true;
471 void TileManager::AssignGpuMemoryToTiles(
472 RasterTilePriorityQueue* raster_priority_queue,
473 size_t scheduled_raster_task_limit,
474 PrioritizedTileVector* tiles_that_need_to_be_rasterized) {
475 TRACE_EVENT_BEGIN0("cc", "TileManager::AssignGpuMemoryToTiles");
477 DCHECK(resource_pool_);
478 DCHECK(tile_task_runner_);
480 // Maintain the list of released resources that can potentially be re-used
481 // or deleted. If this operation becomes expensive too, only do this after
482 // some resource(s) was returned. Note that in that case, one also need to
483 // invalidate when releasing some resource from the pool.
484 resource_pool_->CheckBusyResources();
486 // Now give memory out to the tiles until we're out, and build
487 // the needs-to-be-rasterized queue.
488 unsigned schedule_priority = 1u;
489 all_tiles_that_need_to_be_rasterized_are_scheduled_ = true;
490 bool had_enough_memory_to_schedule_tiles_needed_now = true;
492 MemoryUsage hard_memory_limit(global_state_.hard_memory_limit_in_bytes,
493 global_state_.num_resources_limit);
494 MemoryUsage soft_memory_limit(global_state_.soft_memory_limit_in_bytes,
495 global_state_.num_resources_limit);
496 MemoryUsage memory_usage(resource_pool_->memory_usage_bytes(),
497 resource_pool_->resource_count());
499 scoped_ptr<EvictionTilePriorityQueue> eviction_priority_queue;
500 for (; !raster_priority_queue->IsEmpty(); raster_priority_queue->Pop()) {
501 const PrioritizedTile& prioritized_tile = raster_priority_queue->Top();
502 Tile* tile = prioritized_tile.tile();
503 TilePriority priority = prioritized_tile.priority();
505 if (TilePriorityViolatesMemoryPolicy(priority)) {
506 TRACE_EVENT_INSTANT0(
507 "cc", "TileManager::AssignGpuMemory tile violates memory policy",
508 TRACE_EVENT_SCOPE_THREAD);
509 break;
512 // We won't be able to schedule this tile, so break out early.
513 if (tiles_that_need_to_be_rasterized->size() >=
514 scheduled_raster_task_limit) {
515 all_tiles_that_need_to_be_rasterized_are_scheduled_ = false;
516 break;
519 tile->scheduled_priority_ = schedule_priority++;
521 DCHECK_IMPLIES(tile->draw_info().mode() != TileDrawInfo::OOM_MODE,
522 !tile->draw_info().IsReadyToDraw());
524 // If the tile already has a raster_task, then the memory used by it is
525 // already accounted for in memory_usage. Otherwise, we'll have to acquire
526 // more memory to create a raster task.
527 MemoryUsage memory_required_by_tile_to_be_scheduled;
528 if (!tile->raster_task_.get()) {
529 memory_required_by_tile_to_be_scheduled = MemoryUsage::FromConfig(
530 tile->desired_texture_size(), tile_task_runner_->GetResourceFormat());
533 bool tile_is_needed_now = priority.priority_bin == TilePriority::NOW;
535 // This is the memory limit that will be used by this tile. Depending on
536 // the tile priority, it will be one of hard_memory_limit or
537 // soft_memory_limit.
538 MemoryUsage& tile_memory_limit =
539 tile_is_needed_now ? hard_memory_limit : soft_memory_limit;
541 const MemoryUsage& scheduled_tile_memory_limit =
542 tile_memory_limit - memory_required_by_tile_to_be_scheduled;
543 eviction_priority_queue =
544 FreeTileResourcesWithLowerPriorityUntilUsageIsWithinLimit(
545 eviction_priority_queue.Pass(), scheduled_tile_memory_limit,
546 priority, &memory_usage);
547 bool memory_usage_is_within_limit =
548 !memory_usage.Exceeds(scheduled_tile_memory_limit);
550 // If we couldn't fit the tile into our current memory limit, then we're
551 // done.
552 if (!memory_usage_is_within_limit) {
553 if (tile_is_needed_now)
554 had_enough_memory_to_schedule_tiles_needed_now = false;
555 all_tiles_that_need_to_be_rasterized_are_scheduled_ = false;
556 break;
559 memory_usage += memory_required_by_tile_to_be_scheduled;
560 tiles_that_need_to_be_rasterized->push_back(prioritized_tile);
563 // Note that we should try and further reduce memory in case the above loop
564 // didn't reduce memory. This ensures that we always release as many resources
565 // as possible to stay within the memory limit.
566 eviction_priority_queue = FreeTileResourcesUntilUsageIsWithinLimit(
567 eviction_priority_queue.Pass(), hard_memory_limit, &memory_usage);
569 UMA_HISTOGRAM_BOOLEAN("TileManager.ExceededMemoryBudget",
570 !had_enough_memory_to_schedule_tiles_needed_now);
571 did_oom_on_last_assign_ = !had_enough_memory_to_schedule_tiles_needed_now;
573 memory_stats_from_last_assign_.total_budget_in_bytes =
574 global_state_.hard_memory_limit_in_bytes;
575 memory_stats_from_last_assign_.total_bytes_used = memory_usage.memory_bytes();
576 DCHECK_GE(memory_stats_from_last_assign_.total_bytes_used, 0);
577 memory_stats_from_last_assign_.had_enough_memory =
578 had_enough_memory_to_schedule_tiles_needed_now;
580 TRACE_EVENT_END2("cc", "TileManager::AssignGpuMemoryToTiles",
581 "all_tiles_that_need_to_be_rasterized_are_scheduled",
582 all_tiles_that_need_to_be_rasterized_are_scheduled_,
583 "had_enough_memory_to_schedule_tiles_needed_now",
584 had_enough_memory_to_schedule_tiles_needed_now);
587 void TileManager::FreeResourcesForTile(Tile* tile) {
588 TileDrawInfo& draw_info = tile->draw_info();
589 if (draw_info.resource_) {
590 resource_pool_->ReleaseResource(draw_info.resource_, tile->id());
591 draw_info.resource_ = nullptr;
595 void TileManager::FreeResourcesForTileAndNotifyClientIfTileWasReadyToDraw(
596 Tile* tile) {
597 bool was_ready_to_draw = tile->draw_info().IsReadyToDraw();
598 FreeResourcesForTile(tile);
599 if (was_ready_to_draw)
600 client_->NotifyTileStateChanged(tile);
603 void TileManager::ScheduleTasks(
604 const PrioritizedTileVector& tiles_that_need_to_be_rasterized) {
605 TRACE_EVENT1("cc",
606 "TileManager::ScheduleTasks",
607 "count",
608 tiles_that_need_to_be_rasterized.size());
610 DCHECK(did_check_for_completed_tasks_since_last_schedule_tasks_);
612 raster_queue_.Reset();
614 // Even when scheduling an empty set of tiles, the TTWP does some work, and
615 // will always trigger a DidFinishRunningTileTasks notification. Because of
616 // this we unconditionally set |has_scheduled_tile_tasks_| to true.
617 has_scheduled_tile_tasks_ = true;
619 // Build a new task queue containing all task currently needed. Tasks
620 // are added in order of priority, highest priority task first.
621 for (auto& prioritized_tile : tiles_that_need_to_be_rasterized) {
622 Tile* tile = prioritized_tile.tile();
624 DCHECK(tile->draw_info().requires_resource());
625 DCHECK(!tile->draw_info().resource_);
627 if (!tile->raster_task_.get())
628 tile->raster_task_ = CreateRasterTask(prioritized_tile);
630 TaskSetCollection task_sets;
631 if (tile->required_for_activation())
632 task_sets.set(REQUIRED_FOR_ACTIVATION);
633 if (tile->required_for_draw())
634 task_sets.set(REQUIRED_FOR_DRAW);
635 task_sets.set(ALL);
636 raster_queue_.items.push_back(
637 TileTaskQueue::Item(tile->raster_task_.get(), task_sets));
640 // We must reduce the amount of unused resoruces before calling
641 // ScheduleTasks to prevent usage from rising above limits.
642 resource_pool_->ReduceResourceUsage();
644 // Schedule running of |raster_queue_|. This replaces any previously
645 // scheduled tasks and effectively cancels all tasks not present
646 // in |raster_queue_|.
647 tile_task_runner_->ScheduleTasks(&raster_queue_);
649 // It's now safe to clean up orphan tasks as raster worker pool is not
650 // allowed to keep around unreferenced raster tasks after ScheduleTasks() has
651 // been called.
652 orphan_raster_tasks_.clear();
654 did_check_for_completed_tasks_since_last_schedule_tasks_ = false;
657 scoped_refptr<RasterTask> TileManager::CreateRasterTask(
658 const PrioritizedTile& prioritized_tile) {
659 Tile* tile = prioritized_tile.tile();
660 uint64_t resource_content_id = 0;
661 Resource* resource = nullptr;
662 if (tile->invalidated_id()) {
663 // TODO(danakj): For resources that are in use, we should still grab them
664 // and copy from them instead of rastering everything. crbug.com/492754
665 resource =
666 resource_pool_->TryAcquireResourceWithContentId(tile->invalidated_id());
668 if (resource) {
669 resource_content_id = tile->invalidated_id();
670 DCHECK_EQ(tile_task_runner_->GetResourceFormat(), resource->format());
671 DCHECK_EQ(tile->desired_texture_size().ToString(),
672 resource->size().ToString());
673 } else {
674 resource = resource_pool_->AcquireResource(
675 tile->desired_texture_size(), tile_task_runner_->GetResourceFormat());
678 // Create and queue all image decode tasks that this tile depends on.
679 ImageDecodeTask::Vector decode_tasks;
680 std::vector<skia::PositionImage> images;
681 prioritized_tile.raster_source()->GatherDiscardableImages(
682 tile->enclosing_layer_rect(), &images);
683 for (const skia::PositionImage& image : images) {
684 decode_tasks.push_back(image_decode_controller_.GetTaskForImage(
685 image, tile->layer_id(), prepare_tiles_count_));
688 return make_scoped_refptr(new RasterTaskImpl(
689 resource, prioritized_tile.raster_source(), tile->content_rect(),
690 tile->invalidated_content_rect(), tile->contents_scale(),
691 prioritized_tile.priority().resolution, tile->layer_id(),
692 prepare_tiles_count_, static_cast<const void*>(tile), tile->id(),
693 tile->invalidated_id(), resource_content_id, tile->source_frame_number(),
694 tile->use_picture_analysis(),
695 base::Bind(&TileManager::OnRasterTaskCompleted, base::Unretained(this),
696 tile->id(), resource),
697 &decode_tasks));
700 void TileManager::OnRasterTaskCompleted(
701 Tile::Id tile_id,
702 Resource* resource,
703 const RasterSource::SolidColorAnalysis& analysis,
704 bool was_canceled) {
705 DCHECK(tiles_.find(tile_id) != tiles_.end());
707 Tile* tile = tiles_[tile_id];
708 DCHECK(tile->raster_task_.get());
709 orphan_raster_tasks_.push_back(tile->raster_task_);
710 tile->raster_task_ = nullptr;
712 if (was_canceled) {
713 ++flush_stats_.canceled_count;
714 resource_pool_->ReleaseResource(resource, tile->invalidated_id());
715 return;
718 UpdateTileDrawInfo(tile, resource, analysis);
721 void TileManager::UpdateTileDrawInfo(
722 Tile* tile,
723 Resource* resource,
724 const RasterSource::SolidColorAnalysis& analysis) {
725 TileDrawInfo& draw_info = tile->draw_info();
727 ++flush_stats_.completed_count;
729 if (analysis.is_solid_color) {
730 draw_info.set_solid_color(analysis.solid_color);
731 if (resource) {
732 // Pass the old tile id here because the tile is solid color so we did not
733 // raster anything into the tile resource.
734 resource_pool_->ReleaseResource(resource, tile->invalidated_id());
736 } else {
737 DCHECK(resource);
738 draw_info.set_use_resource();
739 draw_info.resource_ = resource;
740 draw_info.contents_swizzled_ =
741 tile_task_runner_->GetResourceRequiresSwizzle();
743 DCHECK(draw_info.IsReadyToDraw());
744 draw_info.set_was_ever_ready_to_draw();
746 client_->NotifyTileStateChanged(tile);
749 ScopedTilePtr TileManager::CreateTile(const Tile::CreateInfo& info,
750 int layer_id,
751 int source_frame_number,
752 int flags) {
753 // We need to have a tile task worker pool to do anything meaningful with
754 // tiles.
755 DCHECK(tile_task_runner_);
756 ScopedTilePtr tile(
757 new Tile(this, info, layer_id, source_frame_number, flags));
758 DCHECK(tiles_.find(tile->id()) == tiles_.end());
760 tiles_[tile->id()] = tile.get();
761 image_decode_controller_.AddLayerUsedCount(tile->layer_id());
762 return tile;
765 void TileManager::SetTileTaskRunnerForTesting(
766 TileTaskRunner* tile_task_runner) {
767 tile_task_runner_ = tile_task_runner;
768 tile_task_runner_->SetClient(this);
771 bool TileManager::AreRequiredTilesReadyToDraw(
772 RasterTilePriorityQueue::Type type) const {
773 scoped_ptr<RasterTilePriorityQueue> raster_priority_queue(
774 client_->BuildRasterQueue(global_state_.tree_priority, type));
775 // It is insufficient to check whether the raster queue we constructed is
776 // empty. The reason for this is that there are situations (rasterize on
777 // demand) when the tile both needs raster and it's ready to draw. Hence, we
778 // have to iterate the queue to check whether the required tiles are ready to
779 // draw.
780 for (; !raster_priority_queue->IsEmpty(); raster_priority_queue->Pop()) {
781 if (!raster_priority_queue->Top().tile()->draw_info().IsReadyToDraw())
782 return false;
785 #if DCHECK_IS_ON()
786 scoped_ptr<RasterTilePriorityQueue> all_queue(
787 client_->BuildRasterQueue(global_state_.tree_priority, type));
788 for (; !all_queue->IsEmpty(); all_queue->Pop()) {
789 Tile* tile = all_queue->Top().tile();
790 DCHECK_IMPLIES(tile->required_for_activation(),
791 tile->draw_info().IsReadyToDraw());
793 #endif
794 return true;
797 bool TileManager::IsReadyToActivate() const {
798 TRACE_EVENT0("cc", "TileManager::IsReadyToActivate");
799 return AreRequiredTilesReadyToDraw(
800 RasterTilePriorityQueue::Type::REQUIRED_FOR_ACTIVATION);
803 bool TileManager::IsReadyToDraw() const {
804 TRACE_EVENT0("cc", "TileManager::IsReadyToDraw");
805 return AreRequiredTilesReadyToDraw(
806 RasterTilePriorityQueue::Type::REQUIRED_FOR_DRAW);
809 void TileManager::CheckAndIssueSignals() {
810 TRACE_EVENT0("cc", "TileManager::CheckAndIssueSignals");
811 tile_task_runner_->CheckForCompletedTasks();
812 did_check_for_completed_tasks_since_last_schedule_tasks_ = true;
814 // Ready to activate.
815 if (signals_.ready_to_activate && !signals_.did_notify_ready_to_activate) {
816 signals_.ready_to_activate = false;
817 if (IsReadyToActivate()) {
818 TRACE_EVENT0("cc",
819 "TileManager::CheckAndIssueSignals - ready to activate");
820 signals_.did_notify_ready_to_activate = true;
821 client_->NotifyReadyToActivate();
825 // Ready to draw.
826 if (signals_.ready_to_draw && !signals_.did_notify_ready_to_draw) {
827 signals_.ready_to_draw = false;
828 if (IsReadyToDraw()) {
829 TRACE_EVENT0("cc", "TileManager::CheckAndIssueSignals - ready to draw");
830 signals_.did_notify_ready_to_draw = true;
831 client_->NotifyReadyToDraw();
835 // All tile tasks completed.
836 if (signals_.all_tile_tasks_completed &&
837 !signals_.did_notify_all_tile_tasks_completed) {
838 signals_.all_tile_tasks_completed = false;
839 if (!has_scheduled_tile_tasks_) {
840 TRACE_EVENT0(
841 "cc", "TileManager::CheckAndIssueSignals - all tile tasks completed");
842 signals_.did_notify_all_tile_tasks_completed = true;
843 client_->NotifyAllTileTasksCompleted();
848 void TileManager::CheckIfMoreTilesNeedToBePrepared() {
849 tile_task_runner_->CheckForCompletedTasks();
850 did_check_for_completed_tasks_since_last_schedule_tasks_ = true;
852 // When OOM, keep re-assigning memory until we reach a steady state
853 // where top-priority tiles are initialized.
854 PrioritizedTileVector tiles_that_need_to_be_rasterized;
855 scoped_ptr<RasterTilePriorityQueue> raster_priority_queue(
856 client_->BuildRasterQueue(global_state_.tree_priority,
857 RasterTilePriorityQueue::Type::ALL));
858 AssignGpuMemoryToTiles(raster_priority_queue.get(),
859 scheduled_raster_task_limit_,
860 &tiles_that_need_to_be_rasterized);
862 // Inform the client that will likely require a draw if the highest priority
863 // tile that will be rasterized is required for draw.
864 client_->SetIsLikelyToRequireADraw(
865 !tiles_that_need_to_be_rasterized.empty() &&
866 tiles_that_need_to_be_rasterized.front().tile()->required_for_draw());
868 // |tiles_that_need_to_be_rasterized| will be empty when we reach a
869 // steady memory state. Keep scheduling tasks until we reach this state.
870 if (!tiles_that_need_to_be_rasterized.empty()) {
871 ScheduleTasks(tiles_that_need_to_be_rasterized);
872 return;
875 FreeResourcesForReleasedTiles();
877 resource_pool_->ReduceResourceUsage();
879 signals_.all_tile_tasks_completed = true;
880 signals_check_notifier_.Schedule();
882 // We don't reserve memory for required-for-activation tiles during
883 // accelerated gestures, so we just postpone activation when we don't
884 // have these tiles, and activate after the accelerated gesture.
885 // Likewise if we don't allow any tiles (as is the case when we're
886 // invisible), if we have tiles that aren't ready, then we shouldn't
887 // activate as activation can cause checkerboards.
888 bool wait_for_all_required_tiles =
889 global_state_.tree_priority == SMOOTHNESS_TAKES_PRIORITY ||
890 global_state_.memory_limit_policy == ALLOW_NOTHING;
892 // Mark any required-for-activation tiles that have not been been assigned
893 // memory after reaching a steady memory state as OOM. This ensures that we
894 // activate even when OOM. Note that we can't reuse the queue we used for
895 // AssignGpuMemoryToTiles, since the AssignGpuMemoryToTiles call could have
896 // evicted some tiles that would not be picked up by the old raster queue.
897 scoped_ptr<RasterTilePriorityQueue> required_for_activation_queue(
898 client_->BuildRasterQueue(
899 global_state_.tree_priority,
900 RasterTilePriorityQueue::Type::REQUIRED_FOR_ACTIVATION));
902 // If we have tiles left to raster for activation, and we don't allow
903 // activating without them, then skip activation and return early.
904 if (!required_for_activation_queue->IsEmpty() && wait_for_all_required_tiles)
905 return;
907 // Mark required tiles as OOM so that we can activate without them.
908 for (; !required_for_activation_queue->IsEmpty();
909 required_for_activation_queue->Pop()) {
910 Tile* tile = required_for_activation_queue->Top().tile();
911 tile->draw_info().set_oom();
912 client_->NotifyTileStateChanged(tile);
915 DCHECK(IsReadyToActivate());
916 // TODO(ericrk): Investigate why we need to schedule this (not just call it
917 // inline). http://crbug.com/498439
918 signals_.ready_to_activate = true;
919 signals_check_notifier_.Schedule();
922 TileManager::MemoryUsage::MemoryUsage() : memory_bytes_(0), resource_count_(0) {
925 TileManager::MemoryUsage::MemoryUsage(size_t memory_bytes,
926 size_t resource_count)
927 : memory_bytes_(static_cast<int64>(memory_bytes)),
928 resource_count_(static_cast<int>(resource_count)) {
929 // MemoryUsage is constructed using size_ts, since it deals with memory and
930 // the inputs are typically size_t. However, during the course of usage (in
931 // particular operator-=) can cause internal values to become negative. Thus,
932 // member variables are signed.
933 DCHECK_LE(memory_bytes,
934 static_cast<size_t>(std::numeric_limits<int64>::max()));
935 DCHECK_LE(resource_count,
936 static_cast<size_t>(std::numeric_limits<int>::max()));
939 // static
940 TileManager::MemoryUsage TileManager::MemoryUsage::FromConfig(
941 const gfx::Size& size,
942 ResourceFormat format) {
943 // We can use UncheckedSizeInBytes here since this is used with a tile
944 // size which is determined by the compositor (it's at most max texture size).
945 return MemoryUsage(ResourceUtil::UncheckedSizeInBytes<size_t>(size, format),
949 // static
950 TileManager::MemoryUsage TileManager::MemoryUsage::FromTile(const Tile* tile) {
951 const TileDrawInfo& draw_info = tile->draw_info();
952 if (draw_info.resource_) {
953 return MemoryUsage::FromConfig(draw_info.resource_->size(),
954 draw_info.resource_->format());
956 return MemoryUsage();
959 TileManager::MemoryUsage& TileManager::MemoryUsage::operator+=(
960 const MemoryUsage& other) {
961 memory_bytes_ += other.memory_bytes_;
962 resource_count_ += other.resource_count_;
963 return *this;
966 TileManager::MemoryUsage& TileManager::MemoryUsage::operator-=(
967 const MemoryUsage& other) {
968 memory_bytes_ -= other.memory_bytes_;
969 resource_count_ -= other.resource_count_;
970 return *this;
973 TileManager::MemoryUsage TileManager::MemoryUsage::operator-(
974 const MemoryUsage& other) {
975 MemoryUsage result = *this;
976 result -= other;
977 return result;
980 bool TileManager::MemoryUsage::Exceeds(const MemoryUsage& limit) const {
981 return memory_bytes_ > limit.memory_bytes_ ||
982 resource_count_ > limit.resource_count_;
985 TileManager::Signals::Signals() {
986 reset();
989 void TileManager::Signals::reset() {
990 ready_to_activate = false;
991 did_notify_ready_to_activate = false;
992 ready_to_draw = false;
993 did_notify_ready_to_draw = false;
994 all_tile_tasks_completed = false;
995 did_notify_all_tile_tasks_completed = false;
998 } // namespace cc