Preserve viewport scrolling layer when a fling starts
[chromium-blink-merge.git] / cc / tiles / tile_manager.cc
blobe92a3df33b30d368d9a0058b51b60785663f0eba
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 raster_buffer_->Playback(raster_source_.get(), content_rect_,
131 invalid_content_rect_, new_content_id_,
132 contents_scale_);
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 class ImageDecodeTaskImpl : public ImageDecodeTask {
157 public:
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),
163 reply_(reply) {}
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(
171 pixel_ref_.get());
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.
178 pixel_ref_.clear();
181 // Overridden from TileTask:
182 void ScheduleOnOriginThread(TileTaskClient* client) override {}
183 void CompleteOnOriginThread(TileTaskClient* client) override {}
184 void RunReplyOnOriginThread() override { reply_.Run(!HasFinishedRunning()); }
186 protected:
187 ~ImageDecodeTaskImpl() override {}
189 private:
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) {
198 switch (task_set) {
199 case TileManager::ALL:
200 return "ALL";
201 case TileManager::REQUIRED_FOR_ACTIVATION:
202 return "REQUIRED_FOR_ACTIVATION";
203 case TileManager::REQUIRED_FOR_DRAW:
204 return "REQUIRED_FOR_DRAW";
207 NOTREACHED();
208 return "Invalid TaskSet";
211 } // namespace
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));
224 return state;
227 // static
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,
235 tile_task_runner,
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)
245 : client_(client),
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_(
254 task_runner_.get(),
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();
270 TileTaskQueue empty;
271 tile_task_runner_->ScheduleTasks(&empty);
272 orphan_raster_tasks_.clear();
274 // This should finish all pending tasks and release any uninitialized
275 // resources.
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);
304 continue;
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());
319 delete tile;
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));
328 switch (task_set) {
329 case ALL: {
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();
343 return;
346 more_tiles_need_prepare_check_notifier_.Schedule();
347 return;
349 case REQUIRED_FOR_ACTIVATION:
350 signals_.ready_to_activate = true;
351 signals_check_notifier_.Schedule();
352 return;
354 case REQUIRED_FOR_DRAW:
355 signals_.ready_to_draw = true;
356 signals_check_notifier_.Schedule();
357 return;
360 NOTREACHED();
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_);
370 signals_.reset();
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());
425 return value;
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())
448 break;
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())
470 break;
472 const PrioritizedTile& prioritized_tile = eviction_priority_queue->Top();
473 if (!other_priority.IsHigherPriorityThan(prioritized_tile.priority()))
474 break;
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) {
487 case ALLOW_NOTHING:
488 return true;
489 case ALLOW_ABSOLUTE_MINIMUM:
490 return priority.priority_bin > TilePriority::NOW;
491 case ALLOW_PREPAINT_ONLY:
492 return priority.priority_bin > TilePriority::SOON;
493 case ALLOW_ANYTHING:
494 return priority.distance_to_visible ==
495 std::numeric_limits<float>::infinity();
497 NOTREACHED();
498 return true;
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);
536 break;
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;
543 break;
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
578 // done.
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;
583 break;
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(
621 Tile* tile) {
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) {
630 TRACE_EVENT1("cc",
631 "TileManager::ScheduleTasks",
632 "count",
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);
660 task_sets.set(ALL);
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
676 // been called.
677 orphan_raster_tasks_.clear();
679 did_check_for_completed_tasks_since_last_schedule_tasks_ = false;
682 scoped_refptr<ImageDecodeTask> TileManager::CreateImageDecodeTask(
683 Tile* tile,
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
700 resource =
701 resource_pool_->TryAcquireResourceWithContentId(tile->invalidated_id());
703 if (resource) {
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());
708 } else {
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);
727 continue;
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)),
746 &decode_tasks));
749 void TileManager::OnImageDecodeTaskCompleted(int layer_id,
750 SkPixelRef* pixel_ref,
751 bool was_canceled) {
752 // If the task was canceled, we need to clean it up
753 // from |image_decode_tasks_|.
754 if (!was_canceled)
755 return;
757 LayerPixelRefTaskMap::iterator layer_it = image_decode_tasks_.find(layer_id);
758 if (layer_it == image_decode_tasks_.end())
759 return;
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(
770 Tile::Id tile_id,
771 scoped_ptr<ScopedResource> resource,
772 const RasterSource::SolidColorAnalysis& analysis,
773 bool was_canceled) {
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;
781 if (was_canceled) {
782 ++flush_stats_.canceled_count;
783 resource_pool_->ReleaseResource(resource.Pass(), tile->invalidated_id());
784 return;
787 UpdateTileDrawInfo(tile, resource.Pass(), analysis);
790 void TileManager::UpdateTileDrawInfo(
791 Tile* tile,
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);
800 if (resource) {
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());
805 } else {
806 DCHECK(resource);
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,
821 int layer_id,
822 int source_frame_number,
823 int flags) {
824 ScopedTilePtr tile(new Tile(this, desired_texture_size, content_rect,
825 contents_scale, layer_id, source_frame_number,
826 flags));
827 DCHECK(tiles_.find(tile->id()) == tiles_.end());
829 tiles_[tile->id()] = tile.get();
830 used_layer_counts_[tile->layer_id()]++;
831 return tile;
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
848 // draw.
849 for (; !raster_priority_queue->IsEmpty(); raster_priority_queue->Pop()) {
850 if (!raster_priority_queue->Top().tile()->draw_info().IsReadyToDraw())
851 return false;
854 #if DCHECK_IS_ON()
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());
862 #endif
863 return true;
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()) {
887 TRACE_EVENT0("cc",
888 "TileManager::CheckAndIssueSignals - ready to activate");
889 signals_.did_notify_ready_to_activate = true;
890 client_->NotifyReadyToActivate();
894 // Ready to draw.
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_) {
909 TRACE_EVENT0(
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);
941 return;
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)
974 return;
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()));
1008 // static
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);
1017 // static
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_;
1031 return *this;
1034 TileManager::MemoryUsage& TileManager::MemoryUsage::operator-=(
1035 const MemoryUsage& other) {
1036 memory_bytes_ -= other.memory_bytes_;
1037 resource_count_ -= other.resource_count_;
1038 return *this;
1041 TileManager::MemoryUsage TileManager::MemoryUsage::operator-(
1042 const MemoryUsage& other) {
1043 MemoryUsage result = *this;
1044 result -= other;
1045 return result;
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() {
1054 reset();
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;
1066 } // namespace cc