OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "cc/resources/tile_manager.h" | |
6 | |
7 #include <algorithm> | |
8 #include <limits> | |
9 #include <string> | |
10 | |
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" | |
24 | |
25 namespace cc { | |
26 namespace { | |
27 | |
28 // Flag to indicate whether we should try and detect that | |
29 // a tile is of solid color. | |
30 const bool kUseColorEstimator = true; | |
31 | |
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) {} | |
57 | |
58 // Overridden from Task: | |
59 void RunOnWorkerThread() override { | |
60 TRACE_EVENT0("cc", "RasterizerTaskImpl::RunOnWorkerThread"); | |
61 | |
62 DCHECK(raster_source_.get()); | |
63 DCHECK(raster_buffer_); | |
64 | |
65 if (analyze_picture_) { | |
66 Analyze(raster_source_.get()); | |
67 if (analysis_.is_solid_color) | |
68 return; | |
69 } | |
70 | |
71 Raster(raster_source_.get()); | |
72 } | |
73 | |
74 // Overridden from TileTask: | |
75 void ScheduleOnOriginThread(TileTaskClient* client) override { | |
76 DCHECK(!raster_buffer_); | |
77 raster_buffer_ = client->AcquireBufferForRaster(resource()); | |
78 } | |
79 void CompleteOnOriginThread(TileTaskClient* client) override { | |
80 client->ReleaseBufferForRaster(raster_buffer_.Pass()); | |
81 } | |
82 void RunReplyOnOriginThread() override { | |
83 DCHECK(!raster_buffer_); | |
84 reply_.Run(analysis_, !HasFinishedRunning()); | |
85 } | |
86 | |
87 protected: | |
88 ~RasterTaskImpl() override { DCHECK(!raster_buffer_); } | |
89 | |
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_); | |
94 | |
95 DCHECK(raster_source); | |
96 | |
97 raster_source->PerformSolidColorAnalysis(content_rect_, contents_scale_, | |
98 &analysis_); | |
99 // Clear the flag if we're not using the estimator. | |
100 analysis_.is_solid_color &= kUseColorEstimator; | |
101 } | |
102 | |
103 void Raster(const RasterSource* raster_source) { | |
104 frame_viewer_instrumentation::ScopedRasterTask raster_task( | |
105 tile_id_, tile_resolution_, source_frame_number_, layer_id_); | |
106 | |
107 DCHECK(raster_source); | |
108 | |
109 raster_buffer_->Playback(raster_source_.get(), content_rect_, | |
110 contents_scale_); | |
111 } | |
112 | |
113 RasterSource::SolidColorAnalysis analysis_; | |
114 scoped_refptr<RasterSource> raster_source_; | |
115 gfx::Rect content_rect_; | |
116 float contents_scale_; | |
117 TileResolution tile_resolution_; | |
118 int layer_id_; | |
119 const void* tile_id_; | |
120 int source_frame_number_; | |
121 bool analyze_picture_; | |
122 const base::Callback<void(const RasterSource::SolidColorAnalysis&, bool)> | |
123 reply_; | |
124 scoped_ptr<RasterBuffer> raster_buffer_; | |
125 | |
126 DISALLOW_COPY_AND_ASSIGN(RasterTaskImpl); | |
127 }; | |
128 | |
129 class ImageDecodeTaskImpl : public ImageDecodeTask { | |
130 public: | |
131 ImageDecodeTaskImpl(SkPixelRef* pixel_ref, | |
132 const base::Callback<void(bool was_canceled)>& reply) | |
133 : pixel_ref_(skia::SharePtr(pixel_ref)), | |
134 reply_(reply) {} | |
135 | |
136 // Overridden from Task: | |
137 void RunOnWorkerThread() override { | |
138 TRACE_EVENT0("cc", "ImageDecodeTaskImpl::RunOnWorkerThread"); | |
139 | |
140 devtools_instrumentation::ScopedImageDecodeTask image_decode_task( | |
141 pixel_ref_.get()); | |
142 // This will cause the image referred to by pixel ref to be decoded. | |
143 pixel_ref_->lockPixels(); | |
144 pixel_ref_->unlockPixels(); | |
145 | |
146 // Release the reference after decoding image to ensure that it is not | |
147 // kept alive unless needed. | |
148 pixel_ref_.clear(); | |
149 } | |
150 | |
151 // Overridden from TileTask: | |
152 void ScheduleOnOriginThread(TileTaskClient* client) override {} | |
153 void CompleteOnOriginThread(TileTaskClient* client) override {} | |
154 void RunReplyOnOriginThread() override { reply_.Run(!HasFinishedRunning()); } | |
155 | |
156 protected: | |
157 ~ImageDecodeTaskImpl() override {} | |
158 | |
159 private: | |
160 skia::RefPtr<SkPixelRef> pixel_ref_; | |
161 const base::Callback<void(bool was_canceled)> reply_; | |
162 | |
163 DISALLOW_COPY_AND_ASSIGN(ImageDecodeTaskImpl); | |
164 }; | |
165 | |
166 const char* TaskSetName(TaskSet task_set) { | |
167 switch (task_set) { | |
168 case TileManager::ALL: | |
169 return "ALL"; | |
170 case TileManager::REQUIRED_FOR_ACTIVATION: | |
171 return "REQUIRED_FOR_ACTIVATION"; | |
172 case TileManager::REQUIRED_FOR_DRAW: | |
173 return "REQUIRED_FOR_DRAW"; | |
174 } | |
175 | |
176 NOTREACHED(); | |
177 return "Invalid TaskSet"; | |
178 } | |
179 | |
180 } // namespace | |
181 | |
182 RasterTaskCompletionStats::RasterTaskCompletionStats() | |
183 : completed_count(0u), canceled_count(0u) {} | |
184 | |
185 scoped_refptr<base::trace_event::ConvertableToTraceFormat> | |
186 RasterTaskCompletionStatsAsValue(const RasterTaskCompletionStats& stats) { | |
187 scoped_refptr<base::trace_event::TracedValue> state = | |
188 new base::trace_event::TracedValue(); | |
189 state->SetInteger("completed_count", stats.completed_count); | |
190 state->SetInteger("canceled_count", stats.canceled_count); | |
191 return state; | |
192 } | |
193 | |
194 // static | |
195 scoped_ptr<TileManager> TileManager::Create( | |
196 TileManagerClient* client, | |
197 base::SequencedTaskRunner* task_runner, | |
198 ResourcePool* resource_pool, | |
199 TileTaskRunner* tile_task_runner, | |
200 size_t scheduled_raster_task_limit) { | |
201 return make_scoped_ptr(new TileManager(client, task_runner, resource_pool, | |
202 tile_task_runner, | |
203 scheduled_raster_task_limit)); | |
204 } | |
205 | |
206 TileManager::TileManager( | |
207 TileManagerClient* client, | |
208 const scoped_refptr<base::SequencedTaskRunner>& task_runner, | |
209 ResourcePool* resource_pool, | |
210 TileTaskRunner* tile_task_runner, | |
211 size_t scheduled_raster_task_limit) | |
212 : client_(client), | |
213 task_runner_(task_runner), | |
214 resource_pool_(resource_pool), | |
215 tile_task_runner_(tile_task_runner), | |
216 scheduled_raster_task_limit_(scheduled_raster_task_limit), | |
217 all_tiles_that_need_to_be_rasterized_are_scheduled_(true), | |
218 did_check_for_completed_tasks_since_last_schedule_tasks_(true), | |
219 did_oom_on_last_assign_(false), | |
220 ready_to_activate_check_notifier_( | |
221 task_runner_.get(), | |
222 base::Bind(&TileManager::CheckIfReadyToActivate, | |
223 base::Unretained(this))), | |
224 ready_to_draw_check_notifier_( | |
225 task_runner_.get(), | |
226 base::Bind(&TileManager::CheckIfReadyToDraw, base::Unretained(this))), | |
227 more_tiles_need_prepare_check_notifier_( | |
228 task_runner_.get(), | |
229 base::Bind(&TileManager::CheckIfMoreTilesNeedToBePrepared, | |
230 base::Unretained(this))), | |
231 did_notify_ready_to_activate_(false), | |
232 did_notify_ready_to_draw_(false) { | |
233 tile_task_runner_->SetClient(this); | |
234 } | |
235 | |
236 TileManager::~TileManager() { | |
237 // Reset global state and manage. This should cause | |
238 // our memory usage to drop to zero. | |
239 global_state_ = GlobalStateThatImpactsTilePriority(); | |
240 | |
241 TileTaskQueue empty; | |
242 tile_task_runner_->ScheduleTasks(&empty); | |
243 orphan_raster_tasks_.clear(); | |
244 | |
245 // This should finish all pending tasks and release any uninitialized | |
246 // resources. | |
247 tile_task_runner_->Shutdown(); | |
248 tile_task_runner_->CheckForCompletedTasks(); | |
249 | |
250 FreeResourcesForReleasedTiles(); | |
251 CleanUpReleasedTiles(); | |
252 } | |
253 | |
254 void TileManager::Release(Tile* tile) { | |
255 released_tiles_.push_back(tile); | |
256 } | |
257 | |
258 TaskSetCollection TileManager::TasksThatShouldBeForcedToComplete() const { | |
259 TaskSetCollection tasks_that_should_be_forced_to_complete; | |
260 if (global_state_.tree_priority != SMOOTHNESS_TAKES_PRIORITY) | |
261 tasks_that_should_be_forced_to_complete[REQUIRED_FOR_ACTIVATION] = true; | |
262 return tasks_that_should_be_forced_to_complete; | |
263 } | |
264 | |
265 void TileManager::FreeResourcesForReleasedTiles() { | |
266 for (auto* tile : released_tiles_) | |
267 FreeResourcesForTile(tile); | |
268 } | |
269 | |
270 void TileManager::CleanUpReleasedTiles() { | |
271 std::vector<Tile*> tiles_to_retain; | |
272 for (auto* tile : released_tiles_) { | |
273 if (tile->HasRasterTask()) { | |
274 tiles_to_retain.push_back(tile); | |
275 continue; | |
276 } | |
277 | |
278 DCHECK(!tile->draw_info().has_resource()); | |
279 DCHECK(tiles_.find(tile->id()) != tiles_.end()); | |
280 tiles_.erase(tile->id()); | |
281 | |
282 LayerCountMap::iterator layer_it = | |
283 used_layer_counts_.find(tile->layer_id()); | |
284 DCHECK_GT(layer_it->second, 0); | |
285 if (--layer_it->second == 0) { | |
286 used_layer_counts_.erase(layer_it); | |
287 image_decode_tasks_.erase(tile->layer_id()); | |
288 } | |
289 | |
290 delete tile; | |
291 } | |
292 released_tiles_.swap(tiles_to_retain); | |
293 } | |
294 | |
295 void TileManager::DidFinishRunningTileTasks(TaskSet task_set) { | |
296 TRACE_EVENT1("cc", "TileManager::DidFinishRunningTileTasks", "task_set", | |
297 TaskSetName(task_set)); | |
298 | |
299 switch (task_set) { | |
300 case ALL: { | |
301 bool memory_usage_above_limit = | |
302 resource_pool_->total_memory_usage_bytes() > | |
303 global_state_.soft_memory_limit_in_bytes; | |
304 | |
305 if (all_tiles_that_need_to_be_rasterized_are_scheduled_ && | |
306 !memory_usage_above_limit) | |
307 return; | |
308 | |
309 more_tiles_need_prepare_check_notifier_.Schedule(); | |
310 return; | |
311 } | |
312 case REQUIRED_FOR_ACTIVATION: | |
313 ready_to_activate_check_notifier_.Schedule(); | |
314 return; | |
315 case REQUIRED_FOR_DRAW: | |
316 ready_to_draw_check_notifier_.Schedule(); | |
317 return; | |
318 } | |
319 | |
320 NOTREACHED(); | |
321 } | |
322 | |
323 void TileManager::PrepareTiles( | |
324 const GlobalStateThatImpactsTilePriority& state) { | |
325 TRACE_EVENT0("cc", "TileManager::PrepareTiles"); | |
326 | |
327 global_state_ = state; | |
328 | |
329 // We need to call CheckForCompletedTasks() once in-between each call | |
330 // to ScheduleTasks() to prevent canceled tasks from being scheduled. | |
331 if (!did_check_for_completed_tasks_since_last_schedule_tasks_) { | |
332 tile_task_runner_->CheckForCompletedTasks(); | |
333 did_check_for_completed_tasks_since_last_schedule_tasks_ = true; | |
334 } | |
335 | |
336 FreeResourcesForReleasedTiles(); | |
337 CleanUpReleasedTiles(); | |
338 | |
339 PrioritizedTileVector tiles_that_need_to_be_rasterized; | |
340 scoped_ptr<RasterTilePriorityQueue> raster_priority_queue( | |
341 client_->BuildRasterQueue(global_state_.tree_priority, | |
342 RasterTilePriorityQueue::Type::ALL)); | |
343 AssignGpuMemoryToTiles(raster_priority_queue.get(), | |
344 scheduled_raster_task_limit_, | |
345 &tiles_that_need_to_be_rasterized); | |
346 | |
347 // Inform the client that will likely require a draw if the highest priority | |
348 // tile that will be rasterized is required for draw. | |
349 client_->SetIsLikelyToRequireADraw( | |
350 !tiles_that_need_to_be_rasterized.empty() && | |
351 tiles_that_need_to_be_rasterized.front().tile()->required_for_draw()); | |
352 | |
353 // Schedule tile tasks. | |
354 ScheduleTasks(tiles_that_need_to_be_rasterized); | |
355 | |
356 did_notify_ready_to_activate_ = false; | |
357 did_notify_ready_to_draw_ = false; | |
358 | |
359 TRACE_EVENT_INSTANT1("cc", "DidPrepareTiles", TRACE_EVENT_SCOPE_THREAD, | |
360 "state", BasicStateAsValue()); | |
361 | |
362 TRACE_COUNTER_ID1("cc", "unused_memory_bytes", this, | |
363 resource_pool_->total_memory_usage_bytes() - | |
364 resource_pool_->acquired_memory_usage_bytes()); | |
365 } | |
366 | |
367 void TileManager::UpdateVisibleTiles( | |
368 const GlobalStateThatImpactsTilePriority& state) { | |
369 TRACE_EVENT0("cc", "TileManager::UpdateVisibleTiles"); | |
370 | |
371 tile_task_runner_->CheckForCompletedTasks(); | |
372 | |
373 did_check_for_completed_tasks_since_last_schedule_tasks_ = true; | |
374 | |
375 TRACE_EVENT_INSTANT1( | |
376 "cc", | |
377 "DidUpdateVisibleTiles", | |
378 TRACE_EVENT_SCOPE_THREAD, | |
379 "stats", | |
380 RasterTaskCompletionStatsAsValue(update_visible_tiles_stats_)); | |
381 update_visible_tiles_stats_ = RasterTaskCompletionStats(); | |
382 } | |
383 | |
384 scoped_refptr<base::trace_event::ConvertableToTraceFormat> | |
385 TileManager::BasicStateAsValue() const { | |
386 scoped_refptr<base::trace_event::TracedValue> value = | |
387 new base::trace_event::TracedValue(); | |
388 BasicStateAsValueInto(value.get()); | |
389 return value; | |
390 } | |
391 | |
392 void TileManager::BasicStateAsValueInto( | |
393 base::trace_event::TracedValue* state) const { | |
394 state->SetInteger("tile_count", tiles_.size()); | |
395 state->SetBoolean("did_oom_on_last_assign", did_oom_on_last_assign_); | |
396 state->BeginDictionary("global_state"); | |
397 global_state_.AsValueInto(state); | |
398 state->EndDictionary(); | |
399 } | |
400 | |
401 scoped_ptr<EvictionTilePriorityQueue> | |
402 TileManager::FreeTileResourcesUntilUsageIsWithinLimit( | |
403 scoped_ptr<EvictionTilePriorityQueue> eviction_priority_queue, | |
404 const MemoryUsage& limit, | |
405 MemoryUsage* usage) { | |
406 while (usage->Exceeds(limit)) { | |
407 if (!eviction_priority_queue) { | |
408 eviction_priority_queue = | |
409 client_->BuildEvictionQueue(global_state_.tree_priority); | |
410 } | |
411 if (eviction_priority_queue->IsEmpty()) | |
412 break; | |
413 | |
414 Tile* tile = eviction_priority_queue->Top().tile(); | |
415 *usage -= MemoryUsage::FromTile(tile); | |
416 FreeResourcesForTileAndNotifyClientIfTileWasReadyToDraw(tile); | |
417 eviction_priority_queue->Pop(); | |
418 } | |
419 return eviction_priority_queue; | |
420 } | |
421 | |
422 scoped_ptr<EvictionTilePriorityQueue> | |
423 TileManager::FreeTileResourcesWithLowerPriorityUntilUsageIsWithinLimit( | |
424 scoped_ptr<EvictionTilePriorityQueue> eviction_priority_queue, | |
425 const MemoryUsage& limit, | |
426 const TilePriority& other_priority, | |
427 MemoryUsage* usage) { | |
428 while (usage->Exceeds(limit)) { | |
429 if (!eviction_priority_queue) { | |
430 eviction_priority_queue = | |
431 client_->BuildEvictionQueue(global_state_.tree_priority); | |
432 } | |
433 if (eviction_priority_queue->IsEmpty()) | |
434 break; | |
435 | |
436 const PrioritizedTile& prioritized_tile = eviction_priority_queue->Top(); | |
437 if (!other_priority.IsHigherPriorityThan(prioritized_tile.priority())) | |
438 break; | |
439 | |
440 Tile* tile = prioritized_tile.tile(); | |
441 *usage -= MemoryUsage::FromTile(tile); | |
442 FreeResourcesForTileAndNotifyClientIfTileWasReadyToDraw(tile); | |
443 eviction_priority_queue->Pop(); | |
444 } | |
445 return eviction_priority_queue; | |
446 } | |
447 | |
448 bool TileManager::TilePriorityViolatesMemoryPolicy( | |
449 const TilePriority& priority) { | |
450 switch (global_state_.memory_limit_policy) { | |
451 case ALLOW_NOTHING: | |
452 return true; | |
453 case ALLOW_ABSOLUTE_MINIMUM: | |
454 return priority.priority_bin > TilePriority::NOW; | |
455 case ALLOW_PREPAINT_ONLY: | |
456 return priority.priority_bin > TilePriority::SOON; | |
457 case ALLOW_ANYTHING: | |
458 return priority.distance_to_visible == | |
459 std::numeric_limits<float>::infinity(); | |
460 } | |
461 NOTREACHED(); | |
462 return true; | |
463 } | |
464 | |
465 void TileManager::AssignGpuMemoryToTiles( | |
466 RasterTilePriorityQueue* raster_priority_queue, | |
467 size_t scheduled_raster_task_limit, | |
468 PrioritizedTileVector* tiles_that_need_to_be_rasterized) { | |
469 TRACE_EVENT_BEGIN0("cc", "TileManager::AssignGpuMemoryToTiles"); | |
470 | |
471 // Maintain the list of released resources that can potentially be re-used | |
472 // or deleted. If this operation becomes expensive too, only do this after | |
473 // some resource(s) was returned. Note that in that case, one also need to | |
474 // invalidate when releasing some resource from the pool. | |
475 resource_pool_->CheckBusyResources(false); | |
476 | |
477 // Now give memory out to the tiles until we're out, and build | |
478 // the needs-to-be-rasterized queue. | |
479 unsigned schedule_priority = 1u; | |
480 all_tiles_that_need_to_be_rasterized_are_scheduled_ = true; | |
481 bool had_enough_memory_to_schedule_tiles_needed_now = true; | |
482 | |
483 MemoryUsage hard_memory_limit(global_state_.hard_memory_limit_in_bytes, | |
484 global_state_.num_resources_limit); | |
485 MemoryUsage soft_memory_limit(global_state_.soft_memory_limit_in_bytes, | |
486 global_state_.num_resources_limit); | |
487 MemoryUsage memory_usage(resource_pool_->acquired_memory_usage_bytes(), | |
488 resource_pool_->acquired_resource_count()); | |
489 | |
490 scoped_ptr<EvictionTilePriorityQueue> eviction_priority_queue; | |
491 for (; !raster_priority_queue->IsEmpty(); raster_priority_queue->Pop()) { | |
492 const PrioritizedTile& prioritized_tile = raster_priority_queue->Top(); | |
493 Tile* tile = prioritized_tile.tile(); | |
494 TilePriority priority = prioritized_tile.priority(); | |
495 | |
496 if (TilePriorityViolatesMemoryPolicy(priority)) { | |
497 TRACE_EVENT_INSTANT0( | |
498 "cc", "TileManager::AssignGpuMemory tile violates memory policy", | |
499 TRACE_EVENT_SCOPE_THREAD); | |
500 break; | |
501 } | |
502 | |
503 // We won't be able to schedule this tile, so break out early. | |
504 if (tiles_that_need_to_be_rasterized->size() >= | |
505 scheduled_raster_task_limit) { | |
506 all_tiles_that_need_to_be_rasterized_are_scheduled_ = false; | |
507 break; | |
508 } | |
509 | |
510 tile->scheduled_priority_ = schedule_priority++; | |
511 | |
512 DCHECK_IMPLIES(tile->draw_info().mode() != TileDrawInfo::OOM_MODE, | |
513 !tile->draw_info().IsReadyToDraw()); | |
514 | |
515 // If the tile already has a raster_task, then the memory used by it is | |
516 // already accounted for in memory_usage. Otherwise, we'll have to acquire | |
517 // more memory to create a raster task. | |
518 MemoryUsage memory_required_by_tile_to_be_scheduled; | |
519 if (!tile->raster_task_.get()) { | |
520 memory_required_by_tile_to_be_scheduled = MemoryUsage::FromConfig( | |
521 tile->desired_texture_size(), tile_task_runner_->GetResourceFormat()); | |
522 } | |
523 | |
524 bool tile_is_needed_now = priority.priority_bin == TilePriority::NOW; | |
525 | |
526 // This is the memory limit that will be used by this tile. Depending on | |
527 // the tile priority, it will be one of hard_memory_limit or | |
528 // soft_memory_limit. | |
529 MemoryUsage& tile_memory_limit = | |
530 tile_is_needed_now ? hard_memory_limit : soft_memory_limit; | |
531 | |
532 const MemoryUsage& scheduled_tile_memory_limit = | |
533 tile_memory_limit - memory_required_by_tile_to_be_scheduled; | |
534 eviction_priority_queue = | |
535 FreeTileResourcesWithLowerPriorityUntilUsageIsWithinLimit( | |
536 eviction_priority_queue.Pass(), scheduled_tile_memory_limit, | |
537 priority, &memory_usage); | |
538 bool memory_usage_is_within_limit = | |
539 !memory_usage.Exceeds(scheduled_tile_memory_limit); | |
540 | |
541 // If we couldn't fit the tile into our current memory limit, then we're | |
542 // done. | |
543 if (!memory_usage_is_within_limit) { | |
544 if (tile_is_needed_now) | |
545 had_enough_memory_to_schedule_tiles_needed_now = false; | |
546 all_tiles_that_need_to_be_rasterized_are_scheduled_ = false; | |
547 break; | |
548 } | |
549 | |
550 memory_usage += memory_required_by_tile_to_be_scheduled; | |
551 tiles_that_need_to_be_rasterized->push_back(prioritized_tile); | |
552 } | |
553 | |
554 // Note that we should try and further reduce memory in case the above loop | |
555 // didn't reduce memory. This ensures that we always release as many resources | |
556 // as possible to stay within the memory limit. | |
557 eviction_priority_queue = FreeTileResourcesUntilUsageIsWithinLimit( | |
558 eviction_priority_queue.Pass(), hard_memory_limit, &memory_usage); | |
559 | |
560 UMA_HISTOGRAM_BOOLEAN("TileManager.ExceededMemoryBudget", | |
561 !had_enough_memory_to_schedule_tiles_needed_now); | |
562 did_oom_on_last_assign_ = !had_enough_memory_to_schedule_tiles_needed_now; | |
563 | |
564 memory_stats_from_last_assign_.total_budget_in_bytes = | |
565 global_state_.hard_memory_limit_in_bytes; | |
566 memory_stats_from_last_assign_.total_bytes_used = memory_usage.memory_bytes(); | |
567 memory_stats_from_last_assign_.had_enough_memory = | |
568 had_enough_memory_to_schedule_tiles_needed_now; | |
569 | |
570 TRACE_EVENT_END2("cc", "TileManager::AssignGpuMemoryToTiles", | |
571 "all_tiles_that_need_to_be_rasterized_are_scheduled", | |
572 all_tiles_that_need_to_be_rasterized_are_scheduled_, | |
573 "had_enough_memory_to_schedule_tiles_needed_now", | |
574 had_enough_memory_to_schedule_tiles_needed_now); | |
575 } | |
576 | |
577 void TileManager::FreeResourcesForTile(Tile* tile) { | |
578 TileDrawInfo& draw_info = tile->draw_info(); | |
579 if (draw_info.resource_) | |
580 resource_pool_->ReleaseResource(draw_info.resource_.Pass()); | |
581 } | |
582 | |
583 void TileManager::FreeResourcesForTileAndNotifyClientIfTileWasReadyToDraw( | |
584 Tile* tile) { | |
585 bool was_ready_to_draw = tile->draw_info().IsReadyToDraw(); | |
586 FreeResourcesForTile(tile); | |
587 if (was_ready_to_draw) | |
588 client_->NotifyTileStateChanged(tile); | |
589 } | |
590 | |
591 void TileManager::ScheduleTasks( | |
592 const PrioritizedTileVector& tiles_that_need_to_be_rasterized) { | |
593 TRACE_EVENT1("cc", | |
594 "TileManager::ScheduleTasks", | |
595 "count", | |
596 tiles_that_need_to_be_rasterized.size()); | |
597 | |
598 DCHECK(did_check_for_completed_tasks_since_last_schedule_tasks_); | |
599 | |
600 raster_queue_.Reset(); | |
601 | |
602 // Build a new task queue containing all task currently needed. Tasks | |
603 // are added in order of priority, highest priority task first. | |
604 for (auto& prioritized_tile : tiles_that_need_to_be_rasterized) { | |
605 Tile* tile = prioritized_tile.tile(); | |
606 | |
607 DCHECK(tile->draw_info().requires_resource()); | |
608 DCHECK(!tile->draw_info().resource_); | |
609 | |
610 if (!tile->raster_task_.get()) | |
611 tile->raster_task_ = CreateRasterTask(prioritized_tile); | |
612 | |
613 TaskSetCollection task_sets; | |
614 if (tile->required_for_activation()) | |
615 task_sets.set(REQUIRED_FOR_ACTIVATION); | |
616 if (tile->required_for_draw()) | |
617 task_sets.set(REQUIRED_FOR_DRAW); | |
618 task_sets.set(ALL); | |
619 raster_queue_.items.push_back( | |
620 TileTaskQueue::Item(tile->raster_task_.get(), task_sets)); | |
621 } | |
622 | |
623 // We must reduce the amount of unused resoruces before calling | |
624 // ScheduleTasks to prevent usage from rising above limits. | |
625 resource_pool_->ReduceResourceUsage(); | |
626 | |
627 // Schedule running of |raster_queue_|. This replaces any previously | |
628 // scheduled tasks and effectively cancels all tasks not present | |
629 // in |raster_queue_|. | |
630 tile_task_runner_->ScheduleTasks(&raster_queue_); | |
631 | |
632 // It's now safe to clean up orphan tasks as raster worker pool is not | |
633 // allowed to keep around unreferenced raster tasks after ScheduleTasks() has | |
634 // been called. | |
635 orphan_raster_tasks_.clear(); | |
636 | |
637 did_check_for_completed_tasks_since_last_schedule_tasks_ = false; | |
638 } | |
639 | |
640 scoped_refptr<ImageDecodeTask> TileManager::CreateImageDecodeTask( | |
641 Tile* tile, | |
642 SkPixelRef* pixel_ref) { | |
643 return make_scoped_refptr(new ImageDecodeTaskImpl( | |
644 pixel_ref, | |
645 base::Bind(&TileManager::OnImageDecodeTaskCompleted, | |
646 base::Unretained(this), | |
647 tile->layer_id(), | |
648 base::Unretained(pixel_ref)))); | |
649 } | |
650 | |
651 scoped_refptr<RasterTask> TileManager::CreateRasterTask( | |
652 const PrioritizedTile& prioritized_tile) { | |
653 Tile* tile = prioritized_tile.tile(); | |
654 scoped_ptr<ScopedResource> resource = | |
655 resource_pool_->AcquireResource(tile->desired_texture_size(), | |
656 tile_task_runner_->GetResourceFormat()); | |
657 const ScopedResource* const_resource = resource.get(); | |
658 | |
659 // Create and queue all image decode tasks that this tile depends on. | |
660 ImageDecodeTask::Vector decode_tasks; | |
661 PixelRefTaskMap& existing_pixel_refs = image_decode_tasks_[tile->layer_id()]; | |
662 std::vector<SkPixelRef*> pixel_refs; | |
663 prioritized_tile.raster_source()->GatherPixelRefs( | |
664 tile->content_rect(), tile->contents_scale(), &pixel_refs); | |
665 for (SkPixelRef* pixel_ref : pixel_refs) { | |
666 uint32_t id = pixel_ref->getGenerationID(); | |
667 | |
668 // Append existing image decode task if available. | |
669 PixelRefTaskMap::iterator decode_task_it = existing_pixel_refs.find(id); | |
670 if (decode_task_it != existing_pixel_refs.end()) { | |
671 decode_tasks.push_back(decode_task_it->second); | |
672 continue; | |
673 } | |
674 | |
675 // Create and append new image decode task for this pixel ref. | |
676 scoped_refptr<ImageDecodeTask> decode_task = | |
677 CreateImageDecodeTask(tile, pixel_ref); | |
678 decode_tasks.push_back(decode_task); | |
679 existing_pixel_refs[id] = decode_task; | |
680 } | |
681 | |
682 return make_scoped_refptr(new RasterTaskImpl( | |
683 const_resource, prioritized_tile.raster_source(), tile->content_rect(), | |
684 tile->contents_scale(), prioritized_tile.priority().resolution, | |
685 tile->layer_id(), static_cast<const void*>(tile), | |
686 tile->source_frame_number(), tile->use_picture_analysis(), | |
687 base::Bind(&TileManager::OnRasterTaskCompleted, base::Unretained(this), | |
688 tile->id(), base::Passed(&resource)), | |
689 &decode_tasks)); | |
690 } | |
691 | |
692 void TileManager::OnImageDecodeTaskCompleted(int layer_id, | |
693 SkPixelRef* pixel_ref, | |
694 bool was_canceled) { | |
695 // If the task was canceled, we need to clean it up | |
696 // from |image_decode_tasks_|. | |
697 if (!was_canceled) | |
698 return; | |
699 | |
700 LayerPixelRefTaskMap::iterator layer_it = image_decode_tasks_.find(layer_id); | |
701 if (layer_it == image_decode_tasks_.end()) | |
702 return; | |
703 | |
704 PixelRefTaskMap& pixel_ref_tasks = layer_it->second; | |
705 PixelRefTaskMap::iterator task_it = | |
706 pixel_ref_tasks.find(pixel_ref->getGenerationID()); | |
707 | |
708 if (task_it != pixel_ref_tasks.end()) | |
709 pixel_ref_tasks.erase(task_it); | |
710 } | |
711 | |
712 void TileManager::OnRasterTaskCompleted( | |
713 Tile::Id tile_id, | |
714 scoped_ptr<ScopedResource> resource, | |
715 const RasterSource::SolidColorAnalysis& analysis, | |
716 bool was_canceled) { | |
717 DCHECK(tiles_.find(tile_id) != tiles_.end()); | |
718 | |
719 Tile* tile = tiles_[tile_id]; | |
720 DCHECK(tile->raster_task_.get()); | |
721 orphan_raster_tasks_.push_back(tile->raster_task_); | |
722 tile->raster_task_ = nullptr; | |
723 | |
724 if (was_canceled) { | |
725 ++update_visible_tiles_stats_.canceled_count; | |
726 resource_pool_->ReleaseResource(resource.Pass()); | |
727 return; | |
728 } | |
729 | |
730 UpdateTileDrawInfo(tile, resource.Pass(), analysis); | |
731 } | |
732 | |
733 void TileManager::UpdateTileDrawInfo( | |
734 Tile* tile, | |
735 scoped_ptr<ScopedResource> resource, | |
736 const RasterSource::SolidColorAnalysis& analysis) { | |
737 TileDrawInfo& draw_info = tile->draw_info(); | |
738 | |
739 ++update_visible_tiles_stats_.completed_count; | |
740 | |
741 if (analysis.is_solid_color) { | |
742 draw_info.set_solid_color(analysis.solid_color); | |
743 if (resource) | |
744 resource_pool_->ReleaseResource(resource.Pass()); | |
745 } else { | |
746 DCHECK(resource); | |
747 draw_info.set_use_resource(); | |
748 draw_info.resource_ = resource.Pass(); | |
749 } | |
750 | |
751 client_->NotifyTileStateChanged(tile); | |
752 } | |
753 | |
754 ScopedTilePtr TileManager::CreateTile(const gfx::Size& desired_texture_size, | |
755 const gfx::Rect& content_rect, | |
756 float contents_scale, | |
757 int layer_id, | |
758 int source_frame_number, | |
759 int flags) { | |
760 ScopedTilePtr tile(new Tile(this, desired_texture_size, content_rect, | |
761 contents_scale, layer_id, source_frame_number, | |
762 flags)); | |
763 DCHECK(tiles_.find(tile->id()) == tiles_.end()); | |
764 | |
765 tiles_[tile->id()] = tile.get(); | |
766 used_layer_counts_[tile->layer_id()]++; | |
767 return tile; | |
768 } | |
769 | |
770 void TileManager::SetTileTaskRunnerForTesting( | |
771 TileTaskRunner* tile_task_runner) { | |
772 tile_task_runner_ = tile_task_runner; | |
773 tile_task_runner_->SetClient(this); | |
774 } | |
775 | |
776 bool TileManager::AreRequiredTilesReadyToDraw( | |
777 RasterTilePriorityQueue::Type type) const { | |
778 scoped_ptr<RasterTilePriorityQueue> raster_priority_queue( | |
779 client_->BuildRasterQueue(global_state_.tree_priority, type)); | |
780 // It is insufficient to check whether the raster queue we constructed is | |
781 // empty. The reason for this is that there are situations (rasterize on | |
782 // demand) when the tile both needs raster and it's ready to draw. Hence, we | |
783 // have to iterate the queue to check whether the required tiles are ready to | |
784 // draw. | |
785 for (; !raster_priority_queue->IsEmpty(); raster_priority_queue->Pop()) { | |
786 if (!raster_priority_queue->Top().tile()->draw_info().IsReadyToDraw()) | |
787 return false; | |
788 } | |
789 | |
790 #if DCHECK_IS_ON() | |
791 scoped_ptr<RasterTilePriorityQueue> all_queue( | |
792 client_->BuildRasterQueue(global_state_.tree_priority, type)); | |
793 for (; !all_queue->IsEmpty(); all_queue->Pop()) { | |
794 Tile* tile = all_queue->Top().tile(); | |
795 DCHECK_IMPLIES(tile->required_for_activation(), | |
796 tile->draw_info().IsReadyToDraw()); | |
797 } | |
798 #endif | |
799 return true; | |
800 } | |
801 bool TileManager::IsReadyToActivate() const { | |
802 TRACE_EVENT0("cc", "TileManager::IsReadyToActivate"); | |
803 return AreRequiredTilesReadyToDraw( | |
804 RasterTilePriorityQueue::Type::REQUIRED_FOR_ACTIVATION); | |
805 } | |
806 | |
807 bool TileManager::IsReadyToDraw() const { | |
808 TRACE_EVENT0("cc", "TileManager::IsReadyToDraw"); | |
809 return AreRequiredTilesReadyToDraw( | |
810 RasterTilePriorityQueue::Type::REQUIRED_FOR_DRAW); | |
811 } | |
812 | |
813 void TileManager::NotifyReadyToActivate() { | |
814 TRACE_EVENT0("cc", "TileManager::NotifyReadyToActivate"); | |
815 if (did_notify_ready_to_activate_) | |
816 return; | |
817 client_->NotifyReadyToActivate(); | |
818 did_notify_ready_to_activate_ = true; | |
819 } | |
820 | |
821 void TileManager::NotifyReadyToDraw() { | |
822 TRACE_EVENT0("cc", "TileManager::NotifyReadyToDraw"); | |
823 if (did_notify_ready_to_draw_) | |
824 return; | |
825 client_->NotifyReadyToDraw(); | |
826 did_notify_ready_to_draw_ = true; | |
827 } | |
828 | |
829 void TileManager::CheckIfReadyToActivate() { | |
830 TRACE_EVENT0("cc", "TileManager::CheckIfReadyToActivate"); | |
831 | |
832 tile_task_runner_->CheckForCompletedTasks(); | |
833 did_check_for_completed_tasks_since_last_schedule_tasks_ = true; | |
834 | |
835 if (did_notify_ready_to_activate_) | |
836 return; | |
837 if (!IsReadyToActivate()) | |
838 return; | |
839 | |
840 NotifyReadyToActivate(); | |
841 } | |
842 | |
843 void TileManager::CheckIfReadyToDraw() { | |
844 TRACE_EVENT0("cc", "TileManager::CheckIfReadyToDraw"); | |
845 | |
846 tile_task_runner_->CheckForCompletedTasks(); | |
847 did_check_for_completed_tasks_since_last_schedule_tasks_ = true; | |
848 | |
849 if (did_notify_ready_to_draw_) | |
850 return; | |
851 if (!IsReadyToDraw()) | |
852 return; | |
853 | |
854 NotifyReadyToDraw(); | |
855 } | |
856 | |
857 void TileManager::CheckIfMoreTilesNeedToBePrepared() { | |
858 tile_task_runner_->CheckForCompletedTasks(); | |
859 did_check_for_completed_tasks_since_last_schedule_tasks_ = true; | |
860 | |
861 // When OOM, keep re-assigning memory until we reach a steady state | |
862 // where top-priority tiles are initialized. | |
863 PrioritizedTileVector tiles_that_need_to_be_rasterized; | |
864 scoped_ptr<RasterTilePriorityQueue> raster_priority_queue( | |
865 client_->BuildRasterQueue(global_state_.tree_priority, | |
866 RasterTilePriorityQueue::Type::ALL)); | |
867 AssignGpuMemoryToTiles(raster_priority_queue.get(), | |
868 scheduled_raster_task_limit_, | |
869 &tiles_that_need_to_be_rasterized); | |
870 | |
871 // Inform the client that will likely require a draw if the highest priority | |
872 // tile that will be rasterized is required for draw. | |
873 client_->SetIsLikelyToRequireADraw( | |
874 !tiles_that_need_to_be_rasterized.empty() && | |
875 tiles_that_need_to_be_rasterized.front().tile()->required_for_draw()); | |
876 | |
877 // |tiles_that_need_to_be_rasterized| will be empty when we reach a | |
878 // steady memory state. Keep scheduling tasks until we reach this state. | |
879 if (!tiles_that_need_to_be_rasterized.empty()) { | |
880 ScheduleTasks(tiles_that_need_to_be_rasterized); | |
881 return; | |
882 } | |
883 | |
884 FreeResourcesForReleasedTiles(); | |
885 | |
886 resource_pool_->ReduceResourceUsage(); | |
887 | |
888 // We don't reserve memory for required-for-activation tiles during | |
889 // accelerated gestures, so we just postpone activation when we don't | |
890 // have these tiles, and activate after the accelerated gesture. | |
891 // Likewise if we don't allow any tiles (as is the case when we're | |
892 // invisible), if we have tiles that aren't ready, then we shouldn't | |
893 // activate as activation can cause checkerboards. | |
894 bool wait_for_all_required_tiles = | |
895 global_state_.tree_priority == SMOOTHNESS_TAKES_PRIORITY || | |
896 global_state_.memory_limit_policy == ALLOW_NOTHING; | |
897 | |
898 // Mark any required-for-activation tiles that have not been been assigned | |
899 // memory after reaching a steady memory state as OOM. This ensures that we | |
900 // activate even when OOM. Note that we can't reuse the queue we used for | |
901 // AssignGpuMemoryToTiles, since the AssignGpuMemoryToTiles call could have | |
902 // evicted some tiles that would not be picked up by the old raster queue. | |
903 scoped_ptr<RasterTilePriorityQueue> required_for_activation_queue( | |
904 client_->BuildRasterQueue( | |
905 global_state_.tree_priority, | |
906 RasterTilePriorityQueue::Type::REQUIRED_FOR_ACTIVATION)); | |
907 | |
908 // If we have tiles left to raster for activation, and we don't allow | |
909 // activating without them, then skip activation and return early. | |
910 if (!required_for_activation_queue->IsEmpty() && wait_for_all_required_tiles) | |
911 return; | |
912 | |
913 // Mark required tiles as OOM so that we can activate without them. | |
914 for (; !required_for_activation_queue->IsEmpty(); | |
915 required_for_activation_queue->Pop()) { | |
916 Tile* tile = required_for_activation_queue->Top().tile(); | |
917 tile->draw_info().set_oom(); | |
918 client_->NotifyTileStateChanged(tile); | |
919 } | |
920 | |
921 DCHECK(IsReadyToActivate()); | |
922 ready_to_activate_check_notifier_.Schedule(); | |
923 } | |
924 | |
925 TileManager::MemoryUsage::MemoryUsage() : memory_bytes_(0), resource_count_(0) { | |
926 } | |
927 | |
928 TileManager::MemoryUsage::MemoryUsage(int64 memory_bytes, int resource_count) | |
929 : memory_bytes_(memory_bytes), resource_count_(resource_count) { | |
930 } | |
931 | |
932 // static | |
933 TileManager::MemoryUsage TileManager::MemoryUsage::FromConfig( | |
934 const gfx::Size& size, | |
935 ResourceFormat format) { | |
936 return MemoryUsage(Resource::MemorySizeBytes(size, format), 1); | |
937 } | |
938 | |
939 // static | |
940 TileManager::MemoryUsage TileManager::MemoryUsage::FromTile(const Tile* tile) { | |
941 const TileDrawInfo& draw_info = tile->draw_info(); | |
942 if (draw_info.resource_) { | |
943 return MemoryUsage::FromConfig(draw_info.resource_->size(), | |
944 draw_info.resource_->format()); | |
945 } | |
946 return MemoryUsage(); | |
947 } | |
948 | |
949 TileManager::MemoryUsage& TileManager::MemoryUsage::operator+=( | |
950 const MemoryUsage& other) { | |
951 memory_bytes_ += other.memory_bytes_; | |
952 resource_count_ += other.resource_count_; | |
953 return *this; | |
954 } | |
955 | |
956 TileManager::MemoryUsage& TileManager::MemoryUsage::operator-=( | |
957 const MemoryUsage& other) { | |
958 memory_bytes_ -= other.memory_bytes_; | |
959 resource_count_ -= other.resource_count_; | |
960 return *this; | |
961 } | |
962 | |
963 TileManager::MemoryUsage TileManager::MemoryUsage::operator-( | |
964 const MemoryUsage& other) { | |
965 MemoryUsage result = *this; | |
966 result -= other; | |
967 return result; | |
968 } | |
969 | |
970 bool TileManager::MemoryUsage::Exceeds(const MemoryUsage& limit) const { | |
971 return memory_bytes_ > limit.memory_bytes_ || | |
972 resource_count_ > limit.resource_count_; | |
973 } | |
974 | |
975 } // namespace cc | |
OLD | NEW |