• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 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/raster_worker_pool.h"
6 
7 #include "base/json/json_writer.h"
8 #include "base/metrics/histogram.h"
9 #include "base/values.h"
10 #include "cc/debug/devtools_instrumentation.h"
11 #include "cc/debug/traced_value.h"
12 #include "cc/resources/picture_pile_impl.h"
13 #include "skia/ext/lazy_pixel_ref.h"
14 #include "skia/ext/paint_simplifier.h"
15 #include "third_party/skia/include/core/SkBitmap.h"
16 
17 namespace cc {
18 
19 namespace {
20 
21 // Subclass of Allocator that takes a suitably allocated pointer and uses
22 // it as the pixel memory for the bitmap.
23 class IdentityAllocator : public SkBitmap::Allocator {
24  public:
IdentityAllocator(void * buffer)25   explicit IdentityAllocator(void* buffer) : buffer_(buffer) {}
allocPixelRef(SkBitmap * dst,SkColorTable *)26   virtual bool allocPixelRef(SkBitmap* dst, SkColorTable*) OVERRIDE {
27     dst->setPixels(buffer_);
28     return true;
29   }
30  private:
31   void* buffer_;
32 };
33 
34 // Flag to indicate whether we should try and detect that
35 // a tile is of solid color.
36 const bool kUseColorEstimator = true;
37 
38 class DisableLCDTextFilter : public SkDrawFilter {
39  public:
40   // SkDrawFilter interface.
filter(SkPaint * paint,SkDrawFilter::Type type)41   virtual bool filter(SkPaint* paint, SkDrawFilter::Type type) OVERRIDE {
42     if (type != SkDrawFilter::kText_Type)
43       return true;
44 
45     paint->setLCDRenderText(false);
46     return true;
47   }
48 };
49 
50 class RasterWorkerPoolTaskImpl : public internal::RasterWorkerPoolTask {
51  public:
RasterWorkerPoolTaskImpl(const Resource * resource,PicturePileImpl * picture_pile,gfx::Rect content_rect,float contents_scale,RasterMode raster_mode,TileResolution tile_resolution,int layer_id,const void * tile_id,int source_frame_number,RenderingStatsInstrumentation * rendering_stats,const RasterWorkerPool::RasterTask::Reply & reply,TaskVector * dependencies)52   RasterWorkerPoolTaskImpl(const Resource* resource,
53                            PicturePileImpl* picture_pile,
54                            gfx::Rect content_rect,
55                            float contents_scale,
56                            RasterMode raster_mode,
57                            TileResolution tile_resolution,
58                            int layer_id,
59                            const void* tile_id,
60                            int source_frame_number,
61                            RenderingStatsInstrumentation* rendering_stats,
62                            const RasterWorkerPool::RasterTask::Reply& reply,
63                            TaskVector* dependencies)
64       : internal::RasterWorkerPoolTask(resource, dependencies),
65         picture_pile_(picture_pile),
66         content_rect_(content_rect),
67         contents_scale_(contents_scale),
68         raster_mode_(raster_mode),
69         tile_resolution_(tile_resolution),
70         layer_id_(layer_id),
71         tile_id_(tile_id),
72         source_frame_number_(source_frame_number),
73         rendering_stats_(rendering_stats),
74         reply_(reply) {}
75 
RunAnalysisOnThread(unsigned thread_index)76   void RunAnalysisOnThread(unsigned thread_index) {
77     TRACE_EVENT1("cc",
78                  "RasterWorkerPoolTaskImpl::RunAnalysisOnThread",
79                  "data",
80                  TracedValue::FromValue(DataAsValue().release()));
81 
82     DCHECK(picture_pile_.get());
83     DCHECK(rendering_stats_);
84 
85     PicturePileImpl* picture_clone =
86         picture_pile_->GetCloneForDrawingOnThread(thread_index);
87 
88     DCHECK(picture_clone);
89 
90     picture_clone->AnalyzeInRect(
91         content_rect_, contents_scale_, &analysis_, rendering_stats_);
92 
93     // Record the solid color prediction.
94     UMA_HISTOGRAM_BOOLEAN("Renderer4.SolidColorTilesAnalyzed",
95                           analysis_.is_solid_color);
96 
97     // Clear the flag if we're not using the estimator.
98     analysis_.is_solid_color &= kUseColorEstimator;
99   }
100 
RunRasterOnThread(unsigned thread_index,void * buffer,gfx::Size size,int stride)101   bool RunRasterOnThread(unsigned thread_index,
102                          void* buffer,
103                          gfx::Size size,
104                          int stride) {
105     TRACE_EVENT2(
106         "cc", "RasterWorkerPoolTaskImpl::RunRasterOnThread",
107         "data",
108         TracedValue::FromValue(DataAsValue().release()),
109         "raster_mode",
110         TracedValue::FromValue(RasterModeAsValue(raster_mode_).release()));
111 
112     devtools_instrumentation::ScopedLayerTask raster_task(
113         devtools_instrumentation::kRasterTask, layer_id_);
114 
115     DCHECK(picture_pile_.get());
116     DCHECK(buffer);
117 
118     if (analysis_.is_solid_color)
119       return false;
120 
121     PicturePileImpl* picture_clone =
122         picture_pile_->GetCloneForDrawingOnThread(thread_index);
123 
124     SkBitmap bitmap;
125     switch (resource()->format()) {
126       case RGBA_4444:
127         // Use the default stride if we will eventually convert this
128         // bitmap to 4444.
129         bitmap.setConfig(SkBitmap::kARGB_8888_Config,
130                          size.width(),
131                          size.height());
132         bitmap.allocPixels();
133         break;
134       case RGBA_8888:
135       case BGRA_8888:
136         bitmap.setConfig(SkBitmap::kARGB_8888_Config,
137                          size.width(),
138                          size.height(),
139                          stride);
140         bitmap.setPixels(buffer);
141         break;
142       case LUMINANCE_8:
143       case RGB_565:
144       case ETC1:
145         NOTREACHED();
146         break;
147     }
148 
149     SkBitmapDevice device(bitmap);
150     SkCanvas canvas(&device);
151     skia::RefPtr<SkDrawFilter> draw_filter;
152     switch (raster_mode_) {
153       case LOW_QUALITY_RASTER_MODE:
154         draw_filter = skia::AdoptRef(new skia::PaintSimplifier);
155         break;
156       case HIGH_QUALITY_NO_LCD_RASTER_MODE:
157         draw_filter = skia::AdoptRef(new DisableLCDTextFilter);
158         break;
159       case HIGH_QUALITY_RASTER_MODE:
160         break;
161       case NUM_RASTER_MODES:
162       default:
163         NOTREACHED();
164     }
165 
166     canvas.setDrawFilter(draw_filter.get());
167 
168     base::TimeDelta prev_rasterize_time =
169         rendering_stats_->impl_thread_rendering_stats().rasterize_time;
170 
171     // Only record rasterization time for highres tiles, because
172     // lowres tiles are not required for activation and therefore
173     // introduce noise in the measurement (sometimes they get rasterized
174     // before we draw and sometimes they aren't)
175     if (tile_resolution_ == HIGH_RESOLUTION) {
176       picture_clone->RasterToBitmap(
177           &canvas, content_rect_, contents_scale_, rendering_stats_);
178     } else {
179       picture_clone->RasterToBitmap(
180           &canvas, content_rect_, contents_scale_, NULL);
181     }
182 
183     if (rendering_stats_->record_rendering_stats()) {
184       base::TimeDelta current_rasterize_time =
185           rendering_stats_->impl_thread_rendering_stats().rasterize_time;
186       HISTOGRAM_CUSTOM_COUNTS(
187           "Renderer4.PictureRasterTimeUS",
188           (current_rasterize_time - prev_rasterize_time).InMicroseconds(),
189           0,
190           100000,
191           100);
192     }
193 
194     ChangeBitmapConfigIfNeeded(bitmap, buffer);
195 
196     return true;
197   }
198 
199   // Overridden from internal::RasterWorkerPoolTask:
RunOnWorkerThread(unsigned thread_index,void * buffer,gfx::Size size,int stride)200   virtual bool RunOnWorkerThread(unsigned thread_index,
201                                  void* buffer,
202                                  gfx::Size size,
203                                  int stride)
204       OVERRIDE {
205     RunAnalysisOnThread(thread_index);
206     return RunRasterOnThread(thread_index, buffer, size, stride);
207   }
CompleteOnOriginThread()208   virtual void CompleteOnOriginThread() OVERRIDE {
209     reply_.Run(analysis_, !HasFinishedRunning() || WasCanceled());
210   }
211 
212  protected:
~RasterWorkerPoolTaskImpl()213   virtual ~RasterWorkerPoolTaskImpl() {}
214 
215  private:
DataAsValue() const216   scoped_ptr<base::Value> DataAsValue() const {
217     scoped_ptr<base::DictionaryValue> res(new base::DictionaryValue());
218     res->Set("tile_id", TracedValue::CreateIDRef(tile_id_).release());
219     res->Set("resolution", TileResolutionAsValue(tile_resolution_).release());
220     res->SetInteger("source_frame_number", source_frame_number_);
221     res->SetInteger("layer_id", layer_id_);
222     return res.PassAs<base::Value>();
223   }
224 
ChangeBitmapConfigIfNeeded(const SkBitmap & bitmap,void * buffer)225   void ChangeBitmapConfigIfNeeded(const SkBitmap& bitmap,
226                                   void* buffer) {
227     TRACE_EVENT0("cc", "RasterWorkerPoolTaskImpl::ChangeBitmapConfigIfNeeded");
228     SkBitmap::Config config = SkBitmapConfig(resource()->format());
229     if (bitmap.getConfig() != config) {
230       SkBitmap bitmap_dest;
231       IdentityAllocator allocator(buffer);
232       bitmap.copyTo(&bitmap_dest, config, &allocator);
233       // TODO(kaanb): The GL pipeline assumes a 4-byte alignment for the
234       // bitmap data. This check will be removed once crbug.com/293728 is fixed.
235       CHECK_EQ(0u, bitmap_dest.rowBytes() % 4);
236     }
237   }
238 
239   PicturePileImpl::Analysis analysis_;
240   scoped_refptr<PicturePileImpl> picture_pile_;
241   gfx::Rect content_rect_;
242   float contents_scale_;
243   RasterMode raster_mode_;
244   TileResolution tile_resolution_;
245   int layer_id_;
246   const void* tile_id_;
247   int source_frame_number_;
248   RenderingStatsInstrumentation* rendering_stats_;
249   const RasterWorkerPool::RasterTask::Reply reply_;
250 
251   DISALLOW_COPY_AND_ASSIGN(RasterWorkerPoolTaskImpl);
252 };
253 
254 class ImageDecodeWorkerPoolTaskImpl : public internal::WorkerPoolTask {
255  public:
ImageDecodeWorkerPoolTaskImpl(skia::LazyPixelRef * pixel_ref,int layer_id,RenderingStatsInstrumentation * rendering_stats,const RasterWorkerPool::Task::Reply & reply)256   ImageDecodeWorkerPoolTaskImpl(skia::LazyPixelRef* pixel_ref,
257                                 int layer_id,
258                                 RenderingStatsInstrumentation* rendering_stats,
259                                 const RasterWorkerPool::Task::Reply& reply)
260       : pixel_ref_(skia::SharePtr(pixel_ref)),
261         layer_id_(layer_id),
262         rendering_stats_(rendering_stats),
263         reply_(reply) {}
264 
265   // Overridden from internal::WorkerPoolTask:
RunOnWorkerThread(unsigned thread_index)266   virtual void RunOnWorkerThread(unsigned thread_index) OVERRIDE {
267     TRACE_EVENT0("cc", "ImageDecodeWorkerPoolTaskImpl::RunOnWorkerThread");
268     devtools_instrumentation::ScopedImageDecodeTask image_decode_task(
269         pixel_ref_.get());
270     pixel_ref_->Decode();
271   }
CompleteOnOriginThread()272   virtual void CompleteOnOriginThread() OVERRIDE {
273     reply_.Run(!HasFinishedRunning());
274   }
275 
276  protected:
~ImageDecodeWorkerPoolTaskImpl()277   virtual ~ImageDecodeWorkerPoolTaskImpl() {}
278 
279  private:
280   skia::RefPtr<skia::LazyPixelRef> pixel_ref_;
281   int layer_id_;
282   RenderingStatsInstrumentation* rendering_stats_;
283   const RasterWorkerPool::Task::Reply reply_;
284 
285   DISALLOW_COPY_AND_ASSIGN(ImageDecodeWorkerPoolTaskImpl);
286 };
287 
288 class RasterFinishedWorkerPoolTaskImpl : public internal::WorkerPoolTask {
289  public:
290   typedef base::Callback<void(const internal::WorkerPoolTask* source)>
291       Callback;
292 
RasterFinishedWorkerPoolTaskImpl(const Callback & on_raster_finished_callback)293   RasterFinishedWorkerPoolTaskImpl(
294       const Callback& on_raster_finished_callback)
295       : origin_loop_(base::MessageLoopProxy::current().get()),
296         on_raster_finished_callback_(on_raster_finished_callback) {
297   }
298 
299   // Overridden from internal::WorkerPoolTask:
RunOnWorkerThread(unsigned thread_index)300   virtual void RunOnWorkerThread(unsigned thread_index) OVERRIDE {
301     TRACE_EVENT0("cc", "RasterFinishedWorkerPoolTaskImpl::RunOnWorkerThread");
302     origin_loop_->PostTask(
303         FROM_HERE,
304         base::Bind(&RasterFinishedWorkerPoolTaskImpl::RunOnOriginThread,
305                    this));
306   }
CompleteOnOriginThread()307   virtual void CompleteOnOriginThread() OVERRIDE {}
308 
309  private:
~RasterFinishedWorkerPoolTaskImpl()310   virtual ~RasterFinishedWorkerPoolTaskImpl() {}
311 
RunOnOriginThread() const312   void RunOnOriginThread() const {
313     on_raster_finished_callback_.Run(this);
314   }
315 
316   scoped_refptr<base::MessageLoopProxy> origin_loop_;
317   const Callback on_raster_finished_callback_;
318 
319   DISALLOW_COPY_AND_ASSIGN(RasterFinishedWorkerPoolTaskImpl);
320 };
321 
322 const char* kWorkerThreadNamePrefix = "CompositorRaster";
323 
324 }  // namespace
325 
326 namespace internal {
327 
RasterWorkerPoolTask(const Resource * resource,TaskVector * dependencies)328 RasterWorkerPoolTask::RasterWorkerPoolTask(
329     const Resource* resource, TaskVector* dependencies)
330     : did_run_(false),
331       did_complete_(false),
332       was_canceled_(false),
333       resource_(resource) {
334   dependencies_.swap(*dependencies);
335 }
336 
~RasterWorkerPoolTask()337 RasterWorkerPoolTask::~RasterWorkerPoolTask() {
338 }
339 
DidRun(bool was_canceled)340 void RasterWorkerPoolTask::DidRun(bool was_canceled) {
341   DCHECK(!did_run_);
342   did_run_ = true;
343   was_canceled_ = was_canceled;
344 }
345 
HasFinishedRunning() const346 bool RasterWorkerPoolTask::HasFinishedRunning() const {
347   return did_run_;
348 }
349 
WasCanceled() const350 bool RasterWorkerPoolTask::WasCanceled() const {
351   return was_canceled_;
352 }
353 
WillComplete()354 void RasterWorkerPoolTask::WillComplete() {
355   DCHECK(!did_complete_);
356 }
357 
DidComplete()358 void RasterWorkerPoolTask::DidComplete() {
359   DCHECK(!did_complete_);
360   did_complete_ = true;
361 }
362 
HasCompleted() const363 bool RasterWorkerPoolTask::HasCompleted() const {
364   return did_complete_;
365 }
366 
367 }  // namespace internal
368 
Set()369 RasterWorkerPool::Task::Set::Set() {
370 }
371 
~Set()372 RasterWorkerPool::Task::Set::~Set() {
373 }
374 
Insert(const Task & task)375 void RasterWorkerPool::Task::Set::Insert(const Task& task) {
376   DCHECK(!task.is_null());
377   tasks_.push_back(task.internal_);
378 }
379 
Task()380 RasterWorkerPool::Task::Task() {
381 }
382 
Task(internal::WorkerPoolTask * internal)383 RasterWorkerPool::Task::Task(internal::WorkerPoolTask* internal)
384     : internal_(internal) {
385 }
386 
~Task()387 RasterWorkerPool::Task::~Task() {
388 }
389 
Reset()390 void RasterWorkerPool::Task::Reset() {
391   internal_ = NULL;
392 }
393 
Queue()394 RasterWorkerPool::RasterTask::Queue::Queue() {
395 }
396 
~Queue()397 RasterWorkerPool::RasterTask::Queue::~Queue() {
398 }
399 
Append(const RasterTask & task,bool required_for_activation)400 void RasterWorkerPool::RasterTask::Queue::Append(
401     const RasterTask& task, bool required_for_activation) {
402   DCHECK(!task.is_null());
403   tasks_.push_back(task.internal_);
404   if (required_for_activation)
405     tasks_required_for_activation_.insert(task.internal_.get());
406 }
407 
RasterTask()408 RasterWorkerPool::RasterTask::RasterTask() {
409 }
410 
RasterTask(internal::RasterWorkerPoolTask * internal)411 RasterWorkerPool::RasterTask::RasterTask(
412     internal::RasterWorkerPoolTask* internal)
413     : internal_(internal) {
414 }
415 
Reset()416 void RasterWorkerPool::RasterTask::Reset() {
417   internal_ = NULL;
418 }
419 
~RasterTask()420 RasterWorkerPool::RasterTask::~RasterTask() {
421 }
422 
423 // static
CreateRasterTask(const Resource * resource,PicturePileImpl * picture_pile,gfx::Rect content_rect,float contents_scale,RasterMode raster_mode,TileResolution tile_resolution,int layer_id,const void * tile_id,int source_frame_number,RenderingStatsInstrumentation * rendering_stats,const RasterTask::Reply & reply,Task::Set * dependencies)424 RasterWorkerPool::RasterTask RasterWorkerPool::CreateRasterTask(
425     const Resource* resource,
426     PicturePileImpl* picture_pile,
427     gfx::Rect content_rect,
428     float contents_scale,
429     RasterMode raster_mode,
430     TileResolution tile_resolution,
431     int layer_id,
432     const void* tile_id,
433     int source_frame_number,
434     RenderingStatsInstrumentation* rendering_stats,
435     const RasterTask::Reply& reply,
436     Task::Set* dependencies) {
437   return RasterTask(
438       new RasterWorkerPoolTaskImpl(resource,
439                                    picture_pile,
440                                    content_rect,
441                                    contents_scale,
442                                    raster_mode,
443                                    tile_resolution,
444                                    layer_id,
445                                    tile_id,
446                                    source_frame_number,
447                                    rendering_stats,
448                                    reply,
449                                    &dependencies->tasks_));
450 }
451 
452 // static
CreateImageDecodeTask(skia::LazyPixelRef * pixel_ref,int layer_id,RenderingStatsInstrumentation * stats_instrumentation,const Task::Reply & reply)453 RasterWorkerPool::Task RasterWorkerPool::CreateImageDecodeTask(
454     skia::LazyPixelRef* pixel_ref,
455     int layer_id,
456     RenderingStatsInstrumentation* stats_instrumentation,
457     const Task::Reply& reply) {
458   return Task(new ImageDecodeWorkerPoolTaskImpl(pixel_ref,
459                                                 layer_id,
460                                                 stats_instrumentation,
461                                                 reply));
462 }
463 
RasterWorkerPool(ResourceProvider * resource_provider,size_t num_threads)464 RasterWorkerPool::RasterWorkerPool(ResourceProvider* resource_provider,
465                                    size_t num_threads)
466     : WorkerPool(num_threads, kWorkerThreadNamePrefix),
467       client_(NULL),
468       resource_provider_(resource_provider),
469       weak_ptr_factory_(this) {
470 }
471 
~RasterWorkerPool()472 RasterWorkerPool::~RasterWorkerPool() {
473 }
474 
SetClient(RasterWorkerPoolClient * client)475 void RasterWorkerPool::SetClient(RasterWorkerPoolClient* client) {
476   client_ = client;
477 }
478 
Shutdown()479 void RasterWorkerPool::Shutdown() {
480   raster_tasks_.clear();
481   TaskGraph empty;
482   SetTaskGraph(&empty);
483   WorkerPool::Shutdown();
484   weak_ptr_factory_.InvalidateWeakPtrs();
485 }
486 
SetRasterTasks(RasterTask::Queue * queue)487 void RasterWorkerPool::SetRasterTasks(RasterTask::Queue* queue) {
488   raster_tasks_.swap(queue->tasks_);
489   raster_tasks_required_for_activation_.swap(
490       queue->tasks_required_for_activation_);
491 }
492 
IsRasterTaskRequiredForActivation(internal::RasterWorkerPoolTask * task) const493 bool RasterWorkerPool::IsRasterTaskRequiredForActivation(
494     internal::RasterWorkerPoolTask* task) const {
495   return
496       raster_tasks_required_for_activation_.find(task) !=
497       raster_tasks_required_for_activation_.end();
498 }
499 
500 scoped_refptr<internal::WorkerPoolTask>
CreateRasterFinishedTask()501     RasterWorkerPool::CreateRasterFinishedTask() {
502   return make_scoped_refptr(
503       new RasterFinishedWorkerPoolTaskImpl(
504           base::Bind(&RasterWorkerPool::OnRasterFinished,
505                      weak_ptr_factory_.GetWeakPtr())));
506 }
507 
508 scoped_refptr<internal::WorkerPoolTask>
CreateRasterRequiredForActivationFinishedTask()509     RasterWorkerPool::CreateRasterRequiredForActivationFinishedTask() {
510   return make_scoped_refptr(
511       new RasterFinishedWorkerPoolTaskImpl(
512           base::Bind(&RasterWorkerPool::OnRasterRequiredForActivationFinished,
513                      weak_ptr_factory_.GetWeakPtr())));
514 }
515 
OnRasterFinished(const internal::WorkerPoolTask * source)516 void RasterWorkerPool::OnRasterFinished(
517     const internal::WorkerPoolTask* source) {
518   TRACE_EVENT0("cc", "RasterWorkerPool::OnRasterFinished");
519 
520   // Early out if current |raster_finished_task_| is not the source.
521   if (source != raster_finished_task_.get())
522     return;
523 
524   OnRasterTasksFinished();
525 }
526 
OnRasterRequiredForActivationFinished(const internal::WorkerPoolTask * source)527 void RasterWorkerPool::OnRasterRequiredForActivationFinished(
528     const internal::WorkerPoolTask* source) {
529   TRACE_EVENT0("cc", "RasterWorkerPool::OnRasterRequiredForActivationFinished");
530 
531   // Early out if current |raster_required_for_activation_finished_task_|
532   // is not the source.
533   if (source != raster_required_for_activation_finished_task_.get())
534     return;
535 
536   OnRasterTasksRequiredForActivationFinished();
537 }
538 
ScheduledStateAsValue() const539 scoped_ptr<base::Value> RasterWorkerPool::ScheduledStateAsValue() const {
540   scoped_ptr<base::DictionaryValue> scheduled_state(new base::DictionaryValue);
541   scheduled_state->SetInteger("task_count", raster_tasks_.size());
542   scheduled_state->SetInteger("task_required_for_activation_count",
543                               raster_tasks_required_for_activation_.size());
544   return scheduled_state.PassAs<base::Value>();
545 }
546 
547 // static
CreateGraphNodeForTask(internal::WorkerPoolTask * task,unsigned priority,TaskGraph * graph)548 internal::GraphNode* RasterWorkerPool::CreateGraphNodeForTask(
549     internal::WorkerPoolTask* task,
550     unsigned priority,
551     TaskGraph* graph) {
552   internal::GraphNode* node = new internal::GraphNode(task, priority);
553   DCHECK(graph->find(task) == graph->end());
554   graph->set(task, make_scoped_ptr(node));
555   return node;
556 }
557 
558 // static
CreateGraphNodeForRasterTask(internal::WorkerPoolTask * raster_task,const TaskVector & decode_tasks,unsigned priority,TaskGraph * graph)559 internal::GraphNode* RasterWorkerPool::CreateGraphNodeForRasterTask(
560     internal::WorkerPoolTask* raster_task,
561     const TaskVector& decode_tasks,
562     unsigned priority,
563     TaskGraph* graph) {
564   DCHECK(!raster_task->HasCompleted());
565 
566   internal::GraphNode* raster_node = CreateGraphNodeForTask(
567       raster_task, priority, graph);
568 
569   // Insert image decode tasks.
570   for (TaskVector::const_iterator it = decode_tasks.begin();
571        it != decode_tasks.end(); ++it) {
572     internal::WorkerPoolTask* decode_task = it->get();
573 
574     // Skip if already decoded.
575     if (decode_task->HasCompleted())
576       continue;
577 
578     raster_node->add_dependency();
579 
580     // Check if decode task already exists in graph.
581     GraphNodeMap::iterator decode_it = graph->find(decode_task);
582     if (decode_it != graph->end()) {
583       internal::GraphNode* decode_node = decode_it->second;
584       decode_node->add_dependent(raster_node);
585       continue;
586     }
587 
588     internal::GraphNode* decode_node = CreateGraphNodeForTask(
589         decode_task, priority, graph);
590     decode_node->add_dependent(raster_node);
591   }
592 
593   return raster_node;
594 }
595 
596 }  // namespace cc
597