GPU workaround to simulate Out of Memory errors with large textures
[chromium-blink-merge.git] / cc / resources / tile_manager.cc
blob5d183ae55290c5ef6909462f62c34fe3225d2b55
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/resources/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/trace_event/trace_event_argument.h"
16 #include "cc/debug/devtools_instrumentation.h"
17 #include "cc/debug/frame_viewer_instrumentation.h"
18 #include "cc/debug/traced_value.h"
19 #include "cc/layers/picture_layer_impl.h"
20 #include "cc/resources/raster_buffer.h"
21 #include "cc/resources/tile.h"
22 #include "cc/resources/tile_task_runner.h"
23 #include "ui/gfx/geometry/rect_conversions.h"
25 namespace cc {
26 namespace {
28 // Flag to indicate whether we should try and detect that
29 // a tile is of solid color.
30 const bool kUseColorEstimator = true;
32 class RasterTaskImpl : public RasterTask {
33 public:
34 RasterTaskImpl(
35 const Resource* resource,
36 RasterSource* raster_source,
37 const gfx::Rect& content_rect,
38 float contents_scale,
39 TileResolution tile_resolution,
40 int layer_id,
41 const void* tile_id,
42 int source_frame_number,
43 bool analyze_picture,
44 const base::Callback<void(const RasterSource::SolidColorAnalysis&, bool)>&
45 reply,
46 ImageDecodeTask::Vector* dependencies)
47 : RasterTask(resource, dependencies),
48 raster_source_(raster_source),
49 content_rect_(content_rect),
50 contents_scale_(contents_scale),
51 tile_resolution_(tile_resolution),
52 layer_id_(layer_id),
53 tile_id_(tile_id),
54 source_frame_number_(source_frame_number),
55 analyze_picture_(analyze_picture),
56 reply_(reply) {}
58 // Overridden from Task:
59 void RunOnWorkerThread() override {
60 TRACE_EVENT0("cc", "RasterizerTaskImpl::RunOnWorkerThread");
62 DCHECK(raster_source_.get());
63 DCHECK(raster_buffer_);
65 if (analyze_picture_) {
66 Analyze(raster_source_.get());
67 if (analysis_.is_solid_color)
68 return;
71 Raster(raster_source_.get());
74 // Overridden from TileTask:
75 void ScheduleOnOriginThread(TileTaskClient* client) override {
76 DCHECK(!raster_buffer_);
77 raster_buffer_ = client->AcquireBufferForRaster(resource());
79 void CompleteOnOriginThread(TileTaskClient* client) override {
80 client->ReleaseBufferForRaster(raster_buffer_.Pass());
82 void RunReplyOnOriginThread() override {
83 DCHECK(!raster_buffer_);
84 reply_.Run(analysis_, !HasFinishedRunning());
87 protected:
88 ~RasterTaskImpl() override { DCHECK(!raster_buffer_); }
90 private:
91 void Analyze(const RasterSource* raster_source) {
92 frame_viewer_instrumentation::ScopedAnalyzeTask analyze_task(
93 tile_id_, tile_resolution_, source_frame_number_, layer_id_);
95 DCHECK(raster_source);
97 raster_source->PerformSolidColorAnalysis(content_rect_, contents_scale_,
98 &analysis_);
100 // Record the solid color prediction.
101 UMA_HISTOGRAM_BOOLEAN("Renderer4.SolidColorTilesAnalyzed",
102 analysis_.is_solid_color);
104 // Clear the flag if we're not using the estimator.
105 analysis_.is_solid_color &= kUseColorEstimator;
108 void Raster(const RasterSource* raster_source) {
109 frame_viewer_instrumentation::ScopedRasterTask raster_task(
110 tile_id_, tile_resolution_, source_frame_number_, layer_id_);
112 DCHECK(raster_source);
114 raster_buffer_->Playback(raster_source_.get(), content_rect_,
115 contents_scale_);
118 RasterSource::SolidColorAnalysis analysis_;
119 scoped_refptr<RasterSource> raster_source_;
120 gfx::Rect content_rect_;
121 float contents_scale_;
122 TileResolution tile_resolution_;
123 int layer_id_;
124 const void* tile_id_;
125 int source_frame_number_;
126 bool analyze_picture_;
127 const base::Callback<void(const RasterSource::SolidColorAnalysis&, bool)>
128 reply_;
129 scoped_ptr<RasterBuffer> raster_buffer_;
131 DISALLOW_COPY_AND_ASSIGN(RasterTaskImpl);
134 class ImageDecodeTaskImpl : public ImageDecodeTask {
135 public:
136 ImageDecodeTaskImpl(SkPixelRef* pixel_ref,
137 const base::Callback<void(bool was_canceled)>& reply)
138 : pixel_ref_(skia::SharePtr(pixel_ref)),
139 reply_(reply) {}
141 // Overridden from Task:
142 void RunOnWorkerThread() override {
143 TRACE_EVENT0("cc", "ImageDecodeTaskImpl::RunOnWorkerThread");
145 devtools_instrumentation::ScopedImageDecodeTask image_decode_task(
146 pixel_ref_.get());
147 // This will cause the image referred to by pixel ref to be decoded.
148 pixel_ref_->lockPixels();
149 pixel_ref_->unlockPixels();
151 // Release the reference after decoding image to ensure that it is not
152 // kept alive unless needed.
153 pixel_ref_.clear();
156 // Overridden from TileTask:
157 void ScheduleOnOriginThread(TileTaskClient* client) override {}
158 void CompleteOnOriginThread(TileTaskClient* client) override {}
159 void RunReplyOnOriginThread() override { reply_.Run(!HasFinishedRunning()); }
161 protected:
162 ~ImageDecodeTaskImpl() override {}
164 private:
165 skia::RefPtr<SkPixelRef> pixel_ref_;
166 const base::Callback<void(bool was_canceled)> reply_;
168 DISALLOW_COPY_AND_ASSIGN(ImageDecodeTaskImpl);
171 const char* TaskSetName(TaskSet task_set) {
172 switch (task_set) {
173 case TileManager::ALL:
174 return "ALL";
175 case TileManager::REQUIRED_FOR_ACTIVATION:
176 return "REQUIRED_FOR_ACTIVATION";
177 case TileManager::REQUIRED_FOR_DRAW:
178 return "REQUIRED_FOR_DRAW";
181 NOTREACHED();
182 return "Invalid TaskSet";
185 } // namespace
187 RasterTaskCompletionStats::RasterTaskCompletionStats()
188 : completed_count(0u), canceled_count(0u) {}
190 scoped_refptr<base::trace_event::ConvertableToTraceFormat>
191 RasterTaskCompletionStatsAsValue(const RasterTaskCompletionStats& stats) {
192 scoped_refptr<base::trace_event::TracedValue> state =
193 new base::trace_event::TracedValue();
194 state->SetInteger("completed_count", stats.completed_count);
195 state->SetInteger("canceled_count", stats.canceled_count);
196 return state;
199 // static
200 scoped_ptr<TileManager> TileManager::Create(
201 TileManagerClient* client,
202 base::SequencedTaskRunner* task_runner,
203 ResourcePool* resource_pool,
204 TileTaskRunner* tile_task_runner,
205 size_t scheduled_raster_task_limit) {
206 return make_scoped_ptr(new TileManager(client, task_runner, resource_pool,
207 tile_task_runner,
208 scheduled_raster_task_limit));
211 TileManager::TileManager(
212 TileManagerClient* client,
213 const scoped_refptr<base::SequencedTaskRunner>& task_runner,
214 ResourcePool* resource_pool,
215 TileTaskRunner* tile_task_runner,
216 size_t scheduled_raster_task_limit)
217 : client_(client),
218 task_runner_(task_runner),
219 resource_pool_(resource_pool),
220 tile_task_runner_(tile_task_runner),
221 scheduled_raster_task_limit_(scheduled_raster_task_limit),
222 all_tiles_that_need_to_be_rasterized_are_scheduled_(true),
223 did_check_for_completed_tasks_since_last_schedule_tasks_(true),
224 did_oom_on_last_assign_(false),
225 ready_to_activate_check_notifier_(
226 task_runner_.get(),
227 base::Bind(&TileManager::CheckIfReadyToActivate,
228 base::Unretained(this))),
229 ready_to_draw_check_notifier_(
230 task_runner_.get(),
231 base::Bind(&TileManager::CheckIfReadyToDraw, base::Unretained(this))),
232 more_tiles_need_prepare_check_notifier_(
233 task_runner_.get(),
234 base::Bind(&TileManager::CheckIfMoreTilesNeedToBePrepared,
235 base::Unretained(this))),
236 did_notify_ready_to_activate_(false),
237 did_notify_ready_to_draw_(false) {
238 tile_task_runner_->SetClient(this);
241 TileManager::~TileManager() {
242 // Reset global state and manage. This should cause
243 // our memory usage to drop to zero.
244 global_state_ = GlobalStateThatImpactsTilePriority();
246 TileTaskQueue empty;
247 tile_task_runner_->ScheduleTasks(&empty);
248 orphan_raster_tasks_.clear();
250 // This should finish all pending tasks and release any uninitialized
251 // resources.
252 tile_task_runner_->Shutdown();
253 tile_task_runner_->CheckForCompletedTasks();
255 FreeResourcesForReleasedTiles();
256 CleanUpReleasedTiles();
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 (std::vector<Tile*>::iterator it = released_tiles_.begin();
272 it != released_tiles_.end();
273 ++it) {
274 Tile* tile = *it;
275 FreeResourcesForTile(tile);
279 void TileManager::CleanUpReleasedTiles() {
280 std::vector<Tile*>::iterator it = released_tiles_.begin();
281 while (it != released_tiles_.end()) {
282 Tile* tile = *it;
284 if (tile->HasRasterTask()) {
285 ++it;
286 continue;
289 DCHECK(!tile->HasResource());
290 DCHECK(tiles_.find(tile->id()) != tiles_.end());
291 tiles_.erase(tile->id());
293 LayerCountMap::iterator layer_it =
294 used_layer_counts_.find(tile->layer_id());
295 DCHECK_GT(layer_it->second, 0);
296 if (--layer_it->second == 0) {
297 used_layer_counts_.erase(layer_it);
298 image_decode_tasks_.erase(tile->layer_id());
301 delete tile;
302 it = released_tiles_.erase(it);
306 void TileManager::DidFinishRunningTileTasks(TaskSet task_set) {
307 TRACE_EVENT1("cc", "TileManager::DidFinishRunningTileTasks", "task_set",
308 TaskSetName(task_set));
310 switch (task_set) {
311 case ALL: {
312 bool memory_usage_above_limit =
313 resource_pool_->total_memory_usage_bytes() >
314 global_state_.soft_memory_limit_in_bytes;
316 if (all_tiles_that_need_to_be_rasterized_are_scheduled_ &&
317 !memory_usage_above_limit)
318 return;
320 more_tiles_need_prepare_check_notifier_.Schedule();
321 return;
323 case REQUIRED_FOR_ACTIVATION:
324 ready_to_activate_check_notifier_.Schedule();
325 return;
326 case REQUIRED_FOR_DRAW:
327 ready_to_draw_check_notifier_.Schedule();
328 return;
331 NOTREACHED();
334 void TileManager::PrepareTiles(
335 const GlobalStateThatImpactsTilePriority& state) {
336 TRACE_EVENT0("cc", "TileManager::PrepareTiles");
338 global_state_ = state;
340 // We need to call CheckForCompletedTasks() once in-between each call
341 // to ScheduleTasks() to prevent canceled tasks from being scheduled.
342 if (!did_check_for_completed_tasks_since_last_schedule_tasks_) {
343 tile_task_runner_->CheckForCompletedTasks();
344 did_check_for_completed_tasks_since_last_schedule_tasks_ = true;
347 FreeResourcesForReleasedTiles();
348 CleanUpReleasedTiles();
350 TileVector tiles_that_need_to_be_rasterized;
351 scoped_ptr<RasterTilePriorityQueue> raster_priority_queue(
352 client_->BuildRasterQueue(global_state_.tree_priority,
353 RasterTilePriorityQueue::Type::ALL));
354 AssignGpuMemoryToTiles(raster_priority_queue.get(),
355 scheduled_raster_task_limit_,
356 &tiles_that_need_to_be_rasterized);
358 // Inform the client that will likely require a draw if the highest priority
359 // tile that will be rasterized is required for draw.
360 client_->SetIsLikelyToRequireADraw(
361 !tiles_that_need_to_be_rasterized.empty() &&
362 (*tiles_that_need_to_be_rasterized.begin())->required_for_draw());
364 // Schedule tile tasks.
365 ScheduleTasks(tiles_that_need_to_be_rasterized);
367 did_notify_ready_to_activate_ = false;
368 did_notify_ready_to_draw_ = false;
370 TRACE_EVENT_INSTANT1("cc", "DidPrepareTiles", TRACE_EVENT_SCOPE_THREAD,
371 "state", BasicStateAsValue());
373 TRACE_COUNTER_ID1("cc", "unused_memory_bytes", this,
374 resource_pool_->total_memory_usage_bytes() -
375 resource_pool_->acquired_memory_usage_bytes());
378 void TileManager::UpdateVisibleTiles(
379 const GlobalStateThatImpactsTilePriority& state) {
380 TRACE_EVENT0("cc", "TileManager::UpdateVisibleTiles");
382 tile_task_runner_->CheckForCompletedTasks();
384 did_check_for_completed_tasks_since_last_schedule_tasks_ = true;
386 TRACE_EVENT_INSTANT1(
387 "cc",
388 "DidUpdateVisibleTiles",
389 TRACE_EVENT_SCOPE_THREAD,
390 "stats",
391 RasterTaskCompletionStatsAsValue(update_visible_tiles_stats_));
392 update_visible_tiles_stats_ = RasterTaskCompletionStats();
395 scoped_refptr<base::trace_event::ConvertableToTraceFormat>
396 TileManager::BasicStateAsValue() const {
397 scoped_refptr<base::trace_event::TracedValue> value =
398 new base::trace_event::TracedValue();
399 BasicStateAsValueInto(value.get());
400 return value;
403 void TileManager::BasicStateAsValueInto(
404 base::trace_event::TracedValue* state) const {
405 state->SetInteger("tile_count", tiles_.size());
406 state->SetBoolean("did_oom_on_last_assign", did_oom_on_last_assign_);
407 state->BeginDictionary("global_state");
408 global_state_.AsValueInto(state);
409 state->EndDictionary();
412 scoped_ptr<EvictionTilePriorityQueue>
413 TileManager::FreeTileResourcesUntilUsageIsWithinLimit(
414 scoped_ptr<EvictionTilePriorityQueue> eviction_priority_queue,
415 const MemoryUsage& limit,
416 MemoryUsage* usage) {
417 while (usage->Exceeds(limit)) {
418 if (!eviction_priority_queue) {
419 eviction_priority_queue =
420 client_->BuildEvictionQueue(global_state_.tree_priority);
422 if (eviction_priority_queue->IsEmpty())
423 break;
425 Tile* tile = eviction_priority_queue->Top();
426 *usage -= MemoryUsage::FromTile(tile);
427 FreeResourcesForTileAndNotifyClientIfTileWasReadyToDraw(tile);
428 eviction_priority_queue->Pop();
430 return eviction_priority_queue;
433 scoped_ptr<EvictionTilePriorityQueue>
434 TileManager::FreeTileResourcesWithLowerPriorityUntilUsageIsWithinLimit(
435 scoped_ptr<EvictionTilePriorityQueue> eviction_priority_queue,
436 const MemoryUsage& limit,
437 const TilePriority& other_priority,
438 MemoryUsage* usage) {
439 while (usage->Exceeds(limit)) {
440 if (!eviction_priority_queue) {
441 eviction_priority_queue =
442 client_->BuildEvictionQueue(global_state_.tree_priority);
444 if (eviction_priority_queue->IsEmpty())
445 break;
447 Tile* tile = eviction_priority_queue->Top();
448 if (!other_priority.IsHigherPriorityThan(tile->combined_priority()))
449 break;
451 *usage -= MemoryUsage::FromTile(tile);
452 FreeResourcesForTileAndNotifyClientIfTileWasReadyToDraw(tile);
453 eviction_priority_queue->Pop();
455 return eviction_priority_queue;
458 bool TileManager::TilePriorityViolatesMemoryPolicy(
459 const TilePriority& priority) {
460 switch (global_state_.memory_limit_policy) {
461 case ALLOW_NOTHING:
462 return true;
463 case ALLOW_ABSOLUTE_MINIMUM:
464 return priority.priority_bin > TilePriority::NOW;
465 case ALLOW_PREPAINT_ONLY:
466 return priority.priority_bin > TilePriority::SOON;
467 case ALLOW_ANYTHING:
468 return priority.distance_to_visible ==
469 std::numeric_limits<float>::infinity();
471 NOTREACHED();
472 return true;
475 void TileManager::AssignGpuMemoryToTiles(
476 RasterTilePriorityQueue* raster_priority_queue,
477 size_t scheduled_raster_task_limit,
478 TileVector* tiles_that_need_to_be_rasterized) {
479 TRACE_EVENT_BEGIN0("cc", "TileManager::AssignGpuMemoryToTiles");
481 // Maintain the list of released resources that can potentially be re-used
482 // or deleted. If this operation becomes expensive too, only do this after
483 // some resource(s) was returned. Note that in that case, one also need to
484 // invalidate when releasing some resource from the pool.
485 resource_pool_->CheckBusyResources(false);
487 // Now give memory out to the tiles until we're out, and build
488 // the needs-to-be-rasterized queue.
489 unsigned schedule_priority = 1u;
490 all_tiles_that_need_to_be_rasterized_are_scheduled_ = true;
491 bool had_enough_memory_to_schedule_tiles_needed_now = true;
493 MemoryUsage hard_memory_limit(global_state_.hard_memory_limit_in_bytes,
494 global_state_.num_resources_limit);
495 MemoryUsage soft_memory_limit(global_state_.soft_memory_limit_in_bytes,
496 global_state_.num_resources_limit);
497 MemoryUsage memory_usage(resource_pool_->acquired_memory_usage_bytes(),
498 resource_pool_->acquired_resource_count());
500 scoped_ptr<EvictionTilePriorityQueue> eviction_priority_queue;
501 for (; !raster_priority_queue->IsEmpty(); raster_priority_queue->Pop()) {
502 Tile* tile = raster_priority_queue->Top();
503 TilePriority priority = tile->combined_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 TileDrawInfo& draw_info = tile->draw_info();
520 tile->scheduled_priority_ = schedule_priority++;
522 DCHECK_IMPLIES(draw_info.mode() != TileDrawInfo::OOM_MODE,
523 !draw_info.IsReadyToDraw());
525 // If the tile already has a raster_task, then the memory used by it is
526 // already accounted for in memory_usage. Otherwise, we'll have to acquire
527 // more memory to create a raster task.
528 MemoryUsage memory_required_by_tile_to_be_scheduled;
529 if (!tile->raster_task_.get()) {
530 memory_required_by_tile_to_be_scheduled = MemoryUsage::FromConfig(
531 tile->desired_texture_size(), tile_task_runner_->GetResourceFormat());
534 bool tile_is_needed_now = priority.priority_bin == TilePriority::NOW;
536 // This is the memory limit that will be used by this tile. Depending on
537 // the tile priority, it will be one of hard_memory_limit or
538 // soft_memory_limit.
539 MemoryUsage& tile_memory_limit =
540 tile_is_needed_now ? hard_memory_limit : soft_memory_limit;
542 const MemoryUsage& scheduled_tile_memory_limit =
543 tile_memory_limit - memory_required_by_tile_to_be_scheduled;
544 eviction_priority_queue =
545 FreeTileResourcesWithLowerPriorityUntilUsageIsWithinLimit(
546 eviction_priority_queue.Pass(), scheduled_tile_memory_limit,
547 priority, &memory_usage);
548 bool memory_usage_is_within_limit =
549 !memory_usage.Exceeds(scheduled_tile_memory_limit);
551 // If we couldn't fit the tile into our current memory limit, then we're
552 // done.
553 if (!memory_usage_is_within_limit) {
554 if (tile_is_needed_now)
555 had_enough_memory_to_schedule_tiles_needed_now = false;
556 all_tiles_that_need_to_be_rasterized_are_scheduled_ = false;
557 break;
560 memory_usage += memory_required_by_tile_to_be_scheduled;
561 tiles_that_need_to_be_rasterized->push_back(tile);
564 // Note that we should try and further reduce memory in case the above loop
565 // didn't reduce memory. This ensures that we always release as many resources
566 // as possible to stay within the memory limit.
567 eviction_priority_queue = FreeTileResourcesUntilUsageIsWithinLimit(
568 eviction_priority_queue.Pass(), hard_memory_limit, &memory_usage);
570 UMA_HISTOGRAM_BOOLEAN("TileManager.ExceededMemoryBudget",
571 !had_enough_memory_to_schedule_tiles_needed_now);
572 did_oom_on_last_assign_ = !had_enough_memory_to_schedule_tiles_needed_now;
574 memory_stats_from_last_assign_.total_budget_in_bytes =
575 global_state_.hard_memory_limit_in_bytes;
576 memory_stats_from_last_assign_.total_bytes_used = memory_usage.memory_bytes();
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_.Pass());
593 void TileManager::FreeResourcesForTileAndNotifyClientIfTileWasReadyToDraw(
594 Tile* tile) {
595 bool was_ready_to_draw = tile->IsReadyToDraw();
596 FreeResourcesForTile(tile);
597 if (was_ready_to_draw)
598 client_->NotifyTileStateChanged(tile);
601 void TileManager::ScheduleTasks(
602 const TileVector& tiles_that_need_to_be_rasterized) {
603 TRACE_EVENT1("cc",
604 "TileManager::ScheduleTasks",
605 "count",
606 tiles_that_need_to_be_rasterized.size());
608 DCHECK(did_check_for_completed_tasks_since_last_schedule_tasks_);
610 raster_queue_.Reset();
612 // Build a new task queue containing all task currently needed. Tasks
613 // are added in order of priority, highest priority task first.
614 for (TileVector::const_iterator it = tiles_that_need_to_be_rasterized.begin();
615 it != tiles_that_need_to_be_rasterized.end();
616 ++it) {
617 Tile* tile = *it;
618 TileDrawInfo& draw_info = tile->draw_info();
620 DCHECK(draw_info.requires_resource());
621 DCHECK(!draw_info.resource_);
623 if (!tile->raster_task_.get())
624 tile->raster_task_ = CreateRasterTask(tile);
626 TaskSetCollection task_sets;
627 if (tile->required_for_activation())
628 task_sets.set(REQUIRED_FOR_ACTIVATION);
629 if (tile->required_for_draw())
630 task_sets.set(REQUIRED_FOR_DRAW);
631 task_sets.set(ALL);
632 raster_queue_.items.push_back(
633 TileTaskQueue::Item(tile->raster_task_.get(), task_sets));
636 // We must reduce the amount of unused resoruces before calling
637 // ScheduleTasks to prevent usage from rising above limits.
638 resource_pool_->ReduceResourceUsage();
640 // Schedule running of |raster_queue_|. This replaces any previously
641 // scheduled tasks and effectively cancels all tasks not present
642 // in |raster_queue_|.
643 tile_task_runner_->ScheduleTasks(&raster_queue_);
645 // It's now safe to clean up orphan tasks as raster worker pool is not
646 // allowed to keep around unreferenced raster tasks after ScheduleTasks() has
647 // been called.
648 orphan_raster_tasks_.clear();
650 did_check_for_completed_tasks_since_last_schedule_tasks_ = false;
653 scoped_refptr<ImageDecodeTask> TileManager::CreateImageDecodeTask(
654 Tile* tile,
655 SkPixelRef* pixel_ref) {
656 return make_scoped_refptr(new ImageDecodeTaskImpl(
657 pixel_ref,
658 base::Bind(&TileManager::OnImageDecodeTaskCompleted,
659 base::Unretained(this),
660 tile->layer_id(),
661 base::Unretained(pixel_ref))));
664 scoped_refptr<RasterTask> TileManager::CreateRasterTask(Tile* tile) {
665 scoped_ptr<ScopedResource> resource =
666 resource_pool_->AcquireResource(tile->desired_texture_size(),
667 tile_task_runner_->GetResourceFormat());
668 const ScopedResource* const_resource = resource.get();
670 // Create and queue all image decode tasks that this tile depends on.
671 ImageDecodeTask::Vector decode_tasks;
672 PixelRefTaskMap& existing_pixel_refs = image_decode_tasks_[tile->layer_id()];
673 std::vector<SkPixelRef*> pixel_refs;
674 tile->raster_source()->GatherPixelRefs(
675 tile->content_rect(), tile->contents_scale(), &pixel_refs);
676 for (SkPixelRef* pixel_ref : pixel_refs) {
677 uint32_t id = pixel_ref->getGenerationID();
679 // Append existing image decode task if available.
680 PixelRefTaskMap::iterator decode_task_it = existing_pixel_refs.find(id);
681 if (decode_task_it != existing_pixel_refs.end()) {
682 decode_tasks.push_back(decode_task_it->second);
683 continue;
686 // Create and append new image decode task for this pixel ref.
687 scoped_refptr<ImageDecodeTask> decode_task =
688 CreateImageDecodeTask(tile, pixel_ref);
689 decode_tasks.push_back(decode_task);
690 existing_pixel_refs[id] = decode_task;
693 return make_scoped_refptr(new RasterTaskImpl(
694 const_resource, tile->raster_source(), tile->content_rect(),
695 tile->contents_scale(), tile->combined_priority().resolution,
696 tile->layer_id(), static_cast<const void*>(tile),
697 tile->source_frame_number(), tile->use_picture_analysis(),
698 base::Bind(&TileManager::OnRasterTaskCompleted, base::Unretained(this),
699 tile->id(), base::Passed(&resource)),
700 &decode_tasks));
703 void TileManager::OnImageDecodeTaskCompleted(int layer_id,
704 SkPixelRef* pixel_ref,
705 bool was_canceled) {
706 // If the task was canceled, we need to clean it up
707 // from |image_decode_tasks_|.
708 if (!was_canceled)
709 return;
711 LayerPixelRefTaskMap::iterator layer_it = image_decode_tasks_.find(layer_id);
712 if (layer_it == image_decode_tasks_.end())
713 return;
715 PixelRefTaskMap& pixel_ref_tasks = layer_it->second;
716 PixelRefTaskMap::iterator task_it =
717 pixel_ref_tasks.find(pixel_ref->getGenerationID());
719 if (task_it != pixel_ref_tasks.end())
720 pixel_ref_tasks.erase(task_it);
723 void TileManager::OnRasterTaskCompleted(
724 Tile::Id tile_id,
725 scoped_ptr<ScopedResource> resource,
726 const RasterSource::SolidColorAnalysis& analysis,
727 bool was_canceled) {
728 DCHECK(tiles_.find(tile_id) != tiles_.end());
730 Tile* tile = tiles_[tile_id];
731 DCHECK(tile->raster_task_.get());
732 orphan_raster_tasks_.push_back(tile->raster_task_);
733 tile->raster_task_ = nullptr;
735 if (was_canceled) {
736 ++update_visible_tiles_stats_.canceled_count;
737 resource_pool_->ReleaseResource(resource.Pass());
738 return;
741 UpdateTileDrawInfo(tile, resource.Pass(), analysis);
744 void TileManager::UpdateTileDrawInfo(
745 Tile* tile,
746 scoped_ptr<ScopedResource> resource,
747 const RasterSource::SolidColorAnalysis& analysis) {
748 TileDrawInfo& draw_info = tile->draw_info();
750 ++update_visible_tiles_stats_.completed_count;
752 if (analysis.is_solid_color) {
753 draw_info.set_solid_color(analysis.solid_color);
754 if (resource)
755 resource_pool_->ReleaseResource(resource.Pass());
756 } else {
757 DCHECK(resource);
758 draw_info.set_use_resource();
759 draw_info.resource_ = resource.Pass();
762 client_->NotifyTileStateChanged(tile);
765 scoped_refptr<Tile> TileManager::CreateTile(
766 RasterSource* raster_source,
767 const gfx::Size& desired_texture_size,
768 const gfx::Rect& content_rect,
769 float contents_scale,
770 int layer_id,
771 int source_frame_number,
772 int flags) {
773 scoped_refptr<Tile> tile = make_scoped_refptr(
774 new Tile(this, raster_source, desired_texture_size, content_rect,
775 contents_scale, layer_id, source_frame_number, flags));
776 DCHECK(tiles_.find(tile->id()) == tiles_.end());
778 tiles_[tile->id()] = tile.get();
779 used_layer_counts_[tile->layer_id()]++;
780 return tile;
783 void TileManager::SetTileTaskRunnerForTesting(
784 TileTaskRunner* tile_task_runner) {
785 tile_task_runner_ = tile_task_runner;
786 tile_task_runner_->SetClient(this);
789 bool TileManager::AreRequiredTilesReadyToDraw(
790 RasterTilePriorityQueue::Type type) const {
791 scoped_ptr<RasterTilePriorityQueue> raster_priority_queue(
792 client_->BuildRasterQueue(global_state_.tree_priority, type));
793 // It is insufficient to check whether the raster queue we constructed is
794 // empty. The reason for this is that there are situations (rasterize on
795 // demand) when the tile both needs raster and it's ready to draw. Hence, we
796 // have to iterate the queue to check whether the required tiles are ready to
797 // draw.
798 for (; !raster_priority_queue->IsEmpty(); raster_priority_queue->Pop()) {
799 if (!raster_priority_queue->Top()->IsReadyToDraw())
800 return false;
802 return true;
804 bool TileManager::IsReadyToActivate() const {
805 TRACE_EVENT0("cc", "TileManager::IsReadyToActivate");
806 return AreRequiredTilesReadyToDraw(
807 RasterTilePriorityQueue::Type::REQUIRED_FOR_ACTIVATION);
810 bool TileManager::IsReadyToDraw() const {
811 TRACE_EVENT0("cc", "TileManager::IsReadyToDraw");
812 return AreRequiredTilesReadyToDraw(
813 RasterTilePriorityQueue::Type::REQUIRED_FOR_DRAW);
816 void TileManager::NotifyReadyToActivate() {
817 TRACE_EVENT0("cc", "TileManager::NotifyReadyToActivate");
818 if (did_notify_ready_to_activate_)
819 return;
820 client_->NotifyReadyToActivate();
821 did_notify_ready_to_activate_ = true;
824 void TileManager::NotifyReadyToDraw() {
825 TRACE_EVENT0("cc", "TileManager::NotifyReadyToDraw");
826 if (did_notify_ready_to_draw_)
827 return;
828 client_->NotifyReadyToDraw();
829 did_notify_ready_to_draw_ = true;
832 void TileManager::CheckIfReadyToActivate() {
833 TRACE_EVENT0("cc", "TileManager::CheckIfReadyToActivate");
835 tile_task_runner_->CheckForCompletedTasks();
836 did_check_for_completed_tasks_since_last_schedule_tasks_ = true;
838 if (did_notify_ready_to_activate_)
839 return;
840 if (!IsReadyToActivate())
841 return;
843 NotifyReadyToActivate();
846 void TileManager::CheckIfReadyToDraw() {
847 TRACE_EVENT0("cc", "TileManager::CheckIfReadyToDraw");
849 tile_task_runner_->CheckForCompletedTasks();
850 did_check_for_completed_tasks_since_last_schedule_tasks_ = true;
852 if (did_notify_ready_to_draw_)
853 return;
854 if (!IsReadyToDraw())
855 return;
857 NotifyReadyToDraw();
860 void TileManager::CheckIfMoreTilesNeedToBePrepared() {
861 tile_task_runner_->CheckForCompletedTasks();
862 did_check_for_completed_tasks_since_last_schedule_tasks_ = true;
864 // When OOM, keep re-assigning memory until we reach a steady state
865 // where top-priority tiles are initialized.
866 TileVector tiles_that_need_to_be_rasterized;
867 scoped_ptr<RasterTilePriorityQueue> raster_priority_queue(
868 client_->BuildRasterQueue(global_state_.tree_priority,
869 RasterTilePriorityQueue::Type::ALL));
870 AssignGpuMemoryToTiles(raster_priority_queue.get(),
871 scheduled_raster_task_limit_,
872 &tiles_that_need_to_be_rasterized);
874 // Inform the client that will likely require a draw if the highest priority
875 // tile that will be rasterized is required for draw.
876 client_->SetIsLikelyToRequireADraw(
877 !tiles_that_need_to_be_rasterized.empty() &&
878 (*tiles_that_need_to_be_rasterized.begin())->required_for_draw());
880 // |tiles_that_need_to_be_rasterized| will be empty when we reach a
881 // steady memory state. Keep scheduling tasks until we reach this state.
882 if (!tiles_that_need_to_be_rasterized.empty()) {
883 ScheduleTasks(tiles_that_need_to_be_rasterized);
884 return;
887 FreeResourcesForReleasedTiles();
889 resource_pool_->ReduceResourceUsage();
891 // We don't reserve memory for required-for-activation tiles during
892 // accelerated gestures, so we just postpone activation when we don't
893 // have these tiles, and activate after the accelerated gesture.
894 // Likewise if we don't allow any tiles (as is the case when we're
895 // invisible), if we have tiles that aren't ready, then we shouldn't
896 // activate as activation can cause checkerboards.
897 bool wait_for_all_required_tiles =
898 global_state_.tree_priority == SMOOTHNESS_TAKES_PRIORITY ||
899 global_state_.memory_limit_policy == ALLOW_NOTHING;
901 // Mark any required-for-activation tiles that have not been been assigned
902 // memory after reaching a steady memory state as OOM. This ensures that we
903 // activate even when OOM. Note that we can't reuse the queue we used for
904 // AssignGpuMemoryToTiles, since the AssignGpuMemoryToTiles call could have
905 // evicted some tiles that would not be picked up by the old raster queue.
906 scoped_ptr<RasterTilePriorityQueue> required_for_activation_queue(
907 client_->BuildRasterQueue(
908 global_state_.tree_priority,
909 RasterTilePriorityQueue::Type::REQUIRED_FOR_ACTIVATION));
911 // If we have tiles left to raster for activation, and we don't allow
912 // activating without them, then skip activation and return early.
913 if (!required_for_activation_queue->IsEmpty() && wait_for_all_required_tiles)
914 return;
916 // Mark required tiles as OOM so that we can activate without them.
917 for (; !required_for_activation_queue->IsEmpty();
918 required_for_activation_queue->Pop()) {
919 Tile* tile = required_for_activation_queue->Top();
920 tile->draw_info().set_oom();
921 client_->NotifyTileStateChanged(tile);
924 DCHECK(IsReadyToActivate());
925 ready_to_activate_check_notifier_.Schedule();
928 TileManager::MemoryUsage::MemoryUsage() : memory_bytes_(0), resource_count_(0) {
931 TileManager::MemoryUsage::MemoryUsage(int64 memory_bytes, int resource_count)
932 : memory_bytes_(memory_bytes), resource_count_(resource_count) {
935 // static
936 TileManager::MemoryUsage TileManager::MemoryUsage::FromConfig(
937 const gfx::Size& size,
938 ResourceFormat format) {
939 return MemoryUsage(Resource::MemorySizeBytes(size, format), 1);
942 // static
943 TileManager::MemoryUsage TileManager::MemoryUsage::FromTile(const Tile* tile) {
944 const TileDrawInfo& draw_info = tile->draw_info();
945 if (draw_info.resource_) {
946 return MemoryUsage::FromConfig(draw_info.resource_->size(),
947 draw_info.resource_->format());
949 return MemoryUsage();
952 TileManager::MemoryUsage& TileManager::MemoryUsage::operator+=(
953 const MemoryUsage& other) {
954 memory_bytes_ += other.memory_bytes_;
955 resource_count_ += other.resource_count_;
956 return *this;
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 MemoryUsage result = *this;
969 result -= other;
970 return result;
973 bool TileManager::MemoryUsage::Exceeds(const MemoryUsage& limit) const {
974 return memory_bytes_ > limit.memory_bytes_ ||
975 resource_count_ > limit.resource_count_;
978 } // namespace cc