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.
5 #include "cc/resources/raster_worker_pool.h"
9 #include "base/debug/trace_event_synthetic_delay.h"
10 #include "base/json/json_writer.h"
11 #include "base/lazy_instance.h"
12 #include "base/metrics/histogram.h"
13 #include "base/strings/stringprintf.h"
14 #include "base/values.h"
15 #include "cc/debug/devtools_instrumentation.h"
16 #include "cc/debug/traced_value.h"
17 #include "cc/resources/picture_pile_impl.h"
18 #include "cc/resources/resource.h"
19 #include "cc/resources/resource_provider.h"
20 #include "gpu/command_buffer/client/gles2_interface.h"
21 #include "skia/ext/paint_simplifier.h"
22 #include "third_party/skia/include/core/SkBitmap.h"
23 #include "third_party/skia/include/core/SkPixelRef.h"
24 #include "third_party/skia/include/gpu/GrContext.h"
29 // Subclass of Allocator that takes a suitably allocated pointer and uses
30 // it as the pixel memory for the bitmap.
31 class IdentityAllocator : public SkBitmap::Allocator {
33 explicit IdentityAllocator(void* buffer) : buffer_(buffer) {}
34 virtual bool allocPixelRef(SkBitmap* dst, SkColorTable*) OVERRIDE {
35 dst->setPixels(buffer_);
43 // Flag to indicate whether we should try and detect that
44 // a tile is of solid color.
45 const bool kUseColorEstimator = true;
47 // Synthetic delay for raster tasks that are required for activation. Global to
48 // avoid static initializer on critical path.
49 struct RasterRequiredForActivationSyntheticDelayInitializer {
50 RasterRequiredForActivationSyntheticDelayInitializer()
51 : delay(base::debug::TraceEventSyntheticDelay::Lookup(
52 "cc.RasterRequiredForActivation")) {}
53 base::debug::TraceEventSyntheticDelay* delay;
55 static base::LazyInstance<RasterRequiredForActivationSyntheticDelayInitializer>
56 g_raster_required_for_activation_delay = LAZY_INSTANCE_INITIALIZER;
58 class DisableLCDTextFilter : public SkDrawFilter {
60 // SkDrawFilter interface.
61 virtual bool filter(SkPaint* paint, SkDrawFilter::Type type) OVERRIDE {
62 if (type != SkDrawFilter::kText_Type)
65 paint->setLCDRenderText(false);
70 class RasterWorkerPoolTaskImpl : public internal::RasterWorkerPoolTask {
72 RasterWorkerPoolTaskImpl(const Resource* resource,
73 PicturePileImpl* picture_pile,
74 const gfx::Rect& content_rect,
76 RasterMode raster_mode,
77 TileResolution tile_resolution,
80 int source_frame_number,
81 RenderingStatsInstrumentation* rendering_stats,
82 const RasterWorkerPool::RasterTask::Reply& reply,
83 internal::WorkerPoolTask::Vector* dependencies,
84 ContextProvider* context_provider)
85 : internal::RasterWorkerPoolTask(resource, dependencies),
86 picture_pile_(picture_pile),
87 content_rect_(content_rect),
88 contents_scale_(contents_scale),
89 raster_mode_(raster_mode),
90 tile_resolution_(tile_resolution),
93 source_frame_number_(source_frame_number),
94 rendering_stats_(rendering_stats),
96 context_provider_(context_provider),
99 // Overridden from internal::Task:
100 virtual void RunOnWorkerThread(unsigned thread_index) OVERRIDE {
101 TRACE_EVENT0("cc", "RasterWorkerPoolTaskImpl::RunOnWorkerThread");
103 DCHECK(picture_pile_);
104 Analyze(picture_pile_->GetCloneForDrawingOnThread(thread_index));
105 if (!canvas_ || analysis_.is_solid_color)
107 Raster(picture_pile_->GetCloneForDrawingOnThread(thread_index));
110 // Overridden from internal::WorkerPoolTask:
111 virtual void ScheduleOnOriginThread(internal::WorkerPoolTaskClient* client)
114 canvas_ = client->AcquireCanvasForRaster(this);
116 virtual void RunOnOriginThread() OVERRIDE {
117 TRACE_EVENT0("cc", "RasterWorkerPoolTaskImpl::RunOnOriginThread");
119 Analyze(picture_pile_);
120 if (!canvas_ || analysis_.is_solid_color)
122 // TODO(alokp): Use a trace macro to push/pop markers.
123 // Using push/pop functions directly incurs cost to evaluate function
124 // arguments even when tracing is disabled.
125 DCHECK(context_provider_);
126 context_provider_->ContextGL()->PushGroupMarkerEXT(
129 "Raster-%d-%d-%p", source_frame_number_, layer_id_, tile_id_)
131 Raster(picture_pile_);
132 context_provider_->ContextGL()->PopGroupMarkerEXT();
134 virtual void CompleteOnOriginThread(internal::WorkerPoolTaskClient* client)
137 client->OnRasterCompleted(this, analysis_);
139 virtual void RunReplyOnOriginThread() OVERRIDE {
141 reply_.Run(analysis_, !HasFinishedRunning());
145 virtual ~RasterWorkerPoolTaskImpl() { DCHECK(!canvas_); }
148 scoped_ptr<base::Value> DataAsValue() const {
149 scoped_ptr<base::DictionaryValue> res(new base::DictionaryValue());
150 res->Set("tile_id", TracedValue::CreateIDRef(tile_id_).release());
151 res->Set("resolution", TileResolutionAsValue(tile_resolution_).release());
152 res->SetInteger("source_frame_number", source_frame_number_);
153 res->SetInteger("layer_id", layer_id_);
154 return res.PassAs<base::Value>();
157 void Analyze(PicturePileImpl* picture_pile) {
159 "RasterWorkerPoolTaskImpl::Analyze",
161 TracedValue::FromValue(DataAsValue().release()));
163 DCHECK(picture_pile);
165 picture_pile->AnalyzeInRect(
166 content_rect_, contents_scale_, &analysis_, rendering_stats_);
168 // Record the solid color prediction.
169 UMA_HISTOGRAM_BOOLEAN("Renderer4.SolidColorTilesAnalyzed",
170 analysis_.is_solid_color);
172 // Clear the flag if we're not using the estimator.
173 analysis_.is_solid_color &= kUseColorEstimator;
176 void Raster(PicturePileImpl* picture_pile) {
179 "RasterWorkerPoolTaskImpl::Raster",
181 TracedValue::FromValue(DataAsValue().release()),
183 TracedValue::FromValue(RasterModeAsValue(raster_mode_).release()));
185 devtools_instrumentation::ScopedLayerTask raster_task(
186 devtools_instrumentation::kRasterTask, layer_id_);
188 skia::RefPtr<SkDrawFilter> draw_filter;
189 switch (raster_mode_) {
190 case LOW_QUALITY_RASTER_MODE:
191 draw_filter = skia::AdoptRef(new skia::PaintSimplifier);
193 case HIGH_QUALITY_NO_LCD_RASTER_MODE:
194 draw_filter = skia::AdoptRef(new DisableLCDTextFilter);
196 case HIGH_QUALITY_RASTER_MODE:
198 case NUM_RASTER_MODES:
202 canvas_->setDrawFilter(draw_filter.get());
204 base::TimeDelta prev_rasterize_time =
205 rendering_stats_->impl_thread_rendering_stats().rasterize_time;
207 // Only record rasterization time for highres tiles, because
208 // lowres tiles are not required for activation and therefore
209 // introduce noise in the measurement (sometimes they get rasterized
210 // before we draw and sometimes they aren't)
211 RenderingStatsInstrumentation* stats =
212 tile_resolution_ == HIGH_RESOLUTION ? rendering_stats_ : NULL;
213 DCHECK(picture_pile);
214 picture_pile->RasterToBitmap(
215 canvas_, content_rect_, contents_scale_, stats);
217 if (rendering_stats_->record_rendering_stats()) {
218 base::TimeDelta current_rasterize_time =
219 rendering_stats_->impl_thread_rendering_stats().rasterize_time;
220 HISTOGRAM_CUSTOM_COUNTS(
221 "Renderer4.PictureRasterTimeUS",
222 (current_rasterize_time - prev_rasterize_time).InMicroseconds(),
229 PicturePileImpl::Analysis analysis_;
230 scoped_refptr<PicturePileImpl> picture_pile_;
231 gfx::Rect content_rect_;
232 float contents_scale_;
233 RasterMode raster_mode_;
234 TileResolution tile_resolution_;
236 const void* tile_id_;
237 int source_frame_number_;
238 RenderingStatsInstrumentation* rendering_stats_;
239 const RasterWorkerPool::RasterTask::Reply reply_;
240 ContextProvider* context_provider_;
243 DISALLOW_COPY_AND_ASSIGN(RasterWorkerPoolTaskImpl);
246 class ImageDecodeWorkerPoolTaskImpl : public internal::WorkerPoolTask {
248 ImageDecodeWorkerPoolTaskImpl(SkPixelRef* pixel_ref,
250 RenderingStatsInstrumentation* rendering_stats,
251 const RasterWorkerPool::Task::Reply& reply)
252 : pixel_ref_(skia::SharePtr(pixel_ref)),
254 rendering_stats_(rendering_stats),
257 // Overridden from internal::Task:
258 virtual void RunOnWorkerThread(unsigned thread_index) OVERRIDE {
259 TRACE_EVENT0("cc", "ImageDecodeWorkerPoolTaskImpl::RunOnWorkerThread");
263 // Overridden from internal::WorkerPoolTask:
264 virtual void ScheduleOnOriginThread(internal::WorkerPoolTaskClient* client)
266 virtual void RunOnOriginThread() OVERRIDE {
267 TRACE_EVENT0("cc", "ImageDecodeWorkerPoolTaskImpl::RunOnOriginThread");
270 virtual void CompleteOnOriginThread(internal::WorkerPoolTaskClient* client)
272 client->OnImageDecodeCompleted(this);
274 virtual void RunReplyOnOriginThread() OVERRIDE {
275 reply_.Run(!HasFinishedRunning());
279 virtual ~ImageDecodeWorkerPoolTaskImpl() {}
283 devtools_instrumentation::ScopedImageDecodeTask image_decode_task(
285 // This will cause the image referred to by pixel ref to be decoded.
286 pixel_ref_->lockPixels();
287 pixel_ref_->unlockPixels();
290 skia::RefPtr<SkPixelRef> pixel_ref_;
292 RenderingStatsInstrumentation* rendering_stats_;
293 const RasterWorkerPool::Task::Reply reply_;
295 DISALLOW_COPY_AND_ASSIGN(ImageDecodeWorkerPoolTaskImpl);
298 class RasterFinishedWorkerPoolTaskImpl : public internal::WorkerPoolTask {
300 typedef base::Callback<void(const internal::WorkerPoolTask* source)> Callback;
302 explicit RasterFinishedWorkerPoolTaskImpl(
303 const Callback& on_raster_finished_callback)
304 : origin_loop_(base::MessageLoopProxy::current().get()),
305 on_raster_finished_callback_(on_raster_finished_callback) {}
307 // Overridden from internal::Task:
308 virtual void RunOnWorkerThread(unsigned thread_index) OVERRIDE {
309 TRACE_EVENT0("cc", "RasterFinishedWorkerPoolTaskImpl::RunOnWorkerThread");
313 // Overridden from internal::WorkerPoolTask:
314 virtual void ScheduleOnOriginThread(internal::WorkerPoolTaskClient* client)
316 virtual void RunOnOriginThread() OVERRIDE {
317 TRACE_EVENT0("cc", "RasterFinishedWorkerPoolTaskImpl::RunOnOriginThread");
320 virtual void CompleteOnOriginThread(internal::WorkerPoolTaskClient* client)
322 virtual void RunReplyOnOriginThread() OVERRIDE {}
325 virtual ~RasterFinishedWorkerPoolTaskImpl() {}
327 void RasterFinished() {
328 origin_loop_->PostTask(
331 &RasterFinishedWorkerPoolTaskImpl::OnRasterFinishedOnOriginThread,
336 void OnRasterFinishedOnOriginThread() const {
337 on_raster_finished_callback_.Run(this);
340 scoped_refptr<base::MessageLoopProxy> origin_loop_;
341 const Callback on_raster_finished_callback_;
343 DISALLOW_COPY_AND_ASSIGN(RasterFinishedWorkerPoolTaskImpl);
346 class RasterRequiredForActivationFinishedWorkerPoolTaskImpl
347 : public RasterFinishedWorkerPoolTaskImpl {
349 RasterRequiredForActivationFinishedWorkerPoolTaskImpl(
350 const Callback& on_raster_finished_callback,
351 size_t tasks_required_for_activation_count)
352 : RasterFinishedWorkerPoolTaskImpl(on_raster_finished_callback),
353 tasks_required_for_activation_count_(
354 tasks_required_for_activation_count) {
355 if (tasks_required_for_activation_count_) {
356 g_raster_required_for_activation_delay.Get().delay->BeginParallel(
357 &activation_delay_end_time_);
361 // Overridden from internal::Task:
362 virtual void RunOnWorkerThread(unsigned thread_index) OVERRIDE {
364 "RasterRequiredForActivationFinishedWorkerPoolTaskImpl::"
365 "RunOnWorkerThread");
369 // Overridden from internal::WorkerPoolTask:
370 virtual void RunOnOriginThread() OVERRIDE {
372 "RasterRequiredForActivationFinishedWorkerPoolTaskImpl::"
373 "RunOnOriginThread");
378 virtual ~RasterRequiredForActivationFinishedWorkerPoolTaskImpl() {}
380 void RunRasterFinished() {
381 if (tasks_required_for_activation_count_) {
382 g_raster_required_for_activation_delay.Get().delay->EndParallel(
383 activation_delay_end_time_);
388 base::TimeTicks activation_delay_end_time_;
389 const size_t tasks_required_for_activation_count_;
391 DISALLOW_COPY_AND_ASSIGN(
392 RasterRequiredForActivationFinishedWorkerPoolTaskImpl);
395 class RasterTaskGraphRunner : public internal::TaskGraphRunner {
397 RasterTaskGraphRunner()
398 : internal::TaskGraphRunner(RasterWorkerPool::GetNumRasterThreads(),
399 "CompositorRaster") {}
401 base::LazyInstance<RasterTaskGraphRunner>::Leaky g_task_graph_runner =
402 LAZY_INSTANCE_INITIALIZER;
404 const int kDefaultNumRasterThreads = 1;
406 int g_num_raster_threads = 0;
412 WorkerPoolTask::WorkerPoolTask() : did_schedule_(false), did_complete_(false) {}
414 WorkerPoolTask::~WorkerPoolTask() {
415 DCHECK(!did_schedule_);
416 DCHECK(!did_run_ || did_complete_);
419 void WorkerPoolTask::WillSchedule() { DCHECK(!did_schedule_); }
421 void WorkerPoolTask::DidSchedule() {
422 did_schedule_ = true;
423 did_complete_ = false;
426 bool WorkerPoolTask::HasBeenScheduled() const { return did_schedule_; }
428 void WorkerPoolTask::WillComplete() { DCHECK(!did_complete_); }
430 void WorkerPoolTask::DidComplete() {
431 DCHECK(did_schedule_);
432 DCHECK(!did_complete_);
433 did_schedule_ = false;
434 did_complete_ = true;
437 bool WorkerPoolTask::HasCompleted() const { return did_complete_; }
439 RasterWorkerPoolTask::RasterWorkerPoolTask(
440 const Resource* resource,
441 internal::WorkerPoolTask::Vector* dependencies)
442 : resource_(resource) {
443 dependencies_.swap(*dependencies);
446 RasterWorkerPoolTask::~RasterWorkerPoolTask() {}
448 } // namespace internal
450 RasterWorkerPool::Task::Set::Set() {}
452 RasterWorkerPool::Task::Set::~Set() {}
454 void RasterWorkerPool::Task::Set::Insert(const Task& task) {
455 DCHECK(!task.is_null());
456 tasks_.push_back(task.internal_);
459 RasterWorkerPool::Task::Task() {}
461 RasterWorkerPool::Task::Task(internal::WorkerPoolTask* internal)
462 : internal_(internal) {}
464 RasterWorkerPool::Task::~Task() {}
466 void RasterWorkerPool::Task::Reset() { internal_ = NULL; }
468 RasterWorkerPool::RasterTask::Queue::QueuedTask::QueuedTask(
469 internal::RasterWorkerPoolTask* task,
470 bool required_for_activation)
471 : task(task), required_for_activation(required_for_activation) {}
473 RasterWorkerPool::RasterTask::Queue::QueuedTask::~QueuedTask() {}
475 RasterWorkerPool::RasterTask::Queue::Queue()
476 : required_for_activation_count_(0u) {}
478 RasterWorkerPool::RasterTask::Queue::~Queue() {}
480 void RasterWorkerPool::RasterTask::Queue::Reset() {
482 required_for_activation_count_ = 0u;
485 void RasterWorkerPool::RasterTask::Queue::Append(const RasterTask& task,
486 bool required_for_activation) {
487 DCHECK(!task.is_null());
488 tasks_.push_back(QueuedTask(task.internal_, required_for_activation));
489 required_for_activation_count_ += required_for_activation;
492 void RasterWorkerPool::RasterTask::Queue::Swap(Queue* other) {
493 tasks_.swap(other->tasks_);
494 std::swap(required_for_activation_count_,
495 other->required_for_activation_count_);
498 RasterWorkerPool::RasterTask::RasterTask() {}
500 RasterWorkerPool::RasterTask::RasterTask(
501 internal::RasterWorkerPoolTask* internal)
502 : internal_(internal) {}
504 void RasterWorkerPool::RasterTask::Reset() { internal_ = NULL; }
506 RasterWorkerPool::RasterTask::~RasterTask() {}
508 // This allows an external rasterize on-demand system to run raster tasks
509 // with highest priority using the same task graph runner instance.
510 unsigned RasterWorkerPool::kOnDemandRasterTaskPriority = 0u;
511 // Task priorities that make sure raster finished tasks run before any
512 // remaining raster tasks.
513 unsigned RasterWorkerPool::kRasterFinishedTaskPriority = 2u;
514 unsigned RasterWorkerPool::kRasterRequiredForActivationFinishedTaskPriority =
516 unsigned RasterWorkerPool::kRasterTaskPriorityBase = 3u;
518 RasterWorkerPool::RasterWorkerPool(internal::TaskGraphRunner* task_graph_runner,
519 ResourceProvider* resource_provider)
520 : task_graph_runner_(task_graph_runner),
522 resource_provider_(resource_provider),
523 weak_ptr_factory_(this) {
524 if (task_graph_runner_)
525 namespace_token_ = task_graph_runner_->GetNamespaceToken();
528 RasterWorkerPool::~RasterWorkerPool() {}
531 void RasterWorkerPool::SetNumRasterThreads(int num_threads) {
532 DCHECK_LT(0, num_threads);
533 DCHECK_EQ(0, g_num_raster_threads);
535 g_num_raster_threads = num_threads;
539 int RasterWorkerPool::GetNumRasterThreads() {
540 if (!g_num_raster_threads)
541 g_num_raster_threads = kDefaultNumRasterThreads;
543 return g_num_raster_threads;
547 internal::TaskGraphRunner* RasterWorkerPool::GetTaskGraphRunner() {
548 return g_task_graph_runner.Pointer();
552 RasterWorkerPool::RasterTask RasterWorkerPool::CreateRasterTask(
553 const Resource* resource,
554 PicturePileImpl* picture_pile,
555 const gfx::Rect& content_rect,
556 float contents_scale,
557 RasterMode raster_mode,
558 TileResolution tile_resolution,
561 int source_frame_number,
562 RenderingStatsInstrumentation* rendering_stats,
563 const RasterTask::Reply& reply,
564 Task::Set* dependencies,
565 ContextProvider* context_provider) {
566 return RasterTask(new RasterWorkerPoolTaskImpl(resource,
577 &dependencies->tasks_,
582 RasterWorkerPool::Task RasterWorkerPool::CreateImageDecodeTask(
583 SkPixelRef* pixel_ref,
585 RenderingStatsInstrumentation* rendering_stats,
586 const Task::Reply& reply) {
587 return Task(new ImageDecodeWorkerPoolTaskImpl(
588 pixel_ref, layer_id, rendering_stats, reply));
591 void RasterWorkerPool::SetClient(RasterWorkerPoolClient* client) {
595 void RasterWorkerPool::Shutdown() {
596 TRACE_EVENT0("cc", "RasterWorkerPool::Shutdown");
598 if (task_graph_runner_) {
599 internal::TaskGraph empty;
600 task_graph_runner_->SetTaskGraph(namespace_token_, &empty);
601 task_graph_runner_->WaitForTasksToFinishRunning(namespace_token_);
604 weak_ptr_factory_.InvalidateWeakPtrs();
607 void RasterWorkerPool::SetTaskGraph(internal::TaskGraph* graph) {
608 TRACE_EVENT0("cc", "RasterWorkerPool::SetTaskGraph");
610 DCHECK(task_graph_runner_);
611 for (internal::TaskGraph::Node::Vector::iterator it = graph->nodes.begin();
612 it != graph->nodes.end();
614 internal::TaskGraph::Node& node = *it;
615 internal::WorkerPoolTask* task =
616 static_cast<internal::WorkerPoolTask*>(node.task);
618 if (!task->HasBeenScheduled()) {
619 task->WillSchedule();
620 task->ScheduleOnOriginThread(this);
625 task_graph_runner_->SetTaskGraph(namespace_token_, graph);
628 void RasterWorkerPool::CollectCompletedWorkerPoolTasks(
629 internal::Task::Vector* completed_tasks) {
630 DCHECK(task_graph_runner_);
631 task_graph_runner_->CollectCompletedTasks(namespace_token_, completed_tasks);
634 scoped_refptr<internal::WorkerPoolTask>
635 RasterWorkerPool::CreateRasterFinishedTask() {
636 return make_scoped_refptr(new RasterFinishedWorkerPoolTaskImpl(base::Bind(
637 &RasterWorkerPool::OnRasterFinished, weak_ptr_factory_.GetWeakPtr())));
640 scoped_refptr<internal::WorkerPoolTask>
641 RasterWorkerPool::CreateRasterRequiredForActivationFinishedTask(
642 size_t tasks_required_for_activation_count) {
643 return make_scoped_refptr(
644 new RasterRequiredForActivationFinishedWorkerPoolTaskImpl(
645 base::Bind(&RasterWorkerPool::OnRasterRequiredForActivationFinished,
646 weak_ptr_factory_.GetWeakPtr()),
647 tasks_required_for_activation_count));
650 void RasterWorkerPool::RunTaskOnOriginThread(internal::WorkerPoolTask* task) {
651 task->WillSchedule();
652 task->ScheduleOnOriginThread(this);
656 task->RunOnOriginThread();
659 task->WillComplete();
660 task->CompleteOnOriginThread(this);
664 void RasterWorkerPool::OnRasterFinished(
665 const internal::WorkerPoolTask* source) {
666 TRACE_EVENT0("cc", "RasterWorkerPool::OnRasterFinished");
668 // Early out if current |raster_finished_task_| is not the source.
669 if (source != raster_finished_task_.get())
672 OnRasterTasksFinished();
675 void RasterWorkerPool::OnRasterRequiredForActivationFinished(
676 const internal::WorkerPoolTask* source) {
677 TRACE_EVENT0("cc", "RasterWorkerPool::OnRasterRequiredForActivationFinished");
679 // Early out if current |raster_required_for_activation_finished_task_|
680 // is not the source.
681 if (source != raster_required_for_activation_finished_task_.get())
684 OnRasterTasksRequiredForActivationFinished();
688 void RasterWorkerPool::InsertNodeForTask(internal::TaskGraph* graph,
689 internal::WorkerPoolTask* task,
691 size_t dependencies) {
692 DCHECK(std::find_if(graph->nodes.begin(),
694 internal::TaskGraph::Node::TaskComparator(task)) ==
696 graph->nodes.push_back(
697 internal::TaskGraph::Node(task, priority, dependencies));
701 void RasterWorkerPool::InsertNodeForRasterTask(
702 internal::TaskGraph* graph,
703 internal::WorkerPoolTask* raster_task,
704 const internal::WorkerPoolTask::Vector& decode_tasks,
706 size_t dependencies = 0u;
708 // Insert image decode tasks.
709 for (internal::WorkerPoolTask::Vector::const_iterator it =
710 decode_tasks.begin();
711 it != decode_tasks.end();
713 internal::WorkerPoolTask* decode_task = it->get();
715 // Skip if already decoded.
716 if (decode_task->HasCompleted())
721 // Add decode task if it doesn't already exists in graph.
722 internal::TaskGraph::Node::Vector::iterator decode_it =
723 std::find_if(graph->nodes.begin(),
725 internal::TaskGraph::Node::TaskComparator(decode_task));
726 if (decode_it == graph->nodes.end())
727 InsertNodeForTask(graph, decode_task, priority, 0u);
729 graph->edges.push_back(internal::TaskGraph::Edge(decode_task, raster_task));
732 InsertNodeForTask(graph, raster_task, priority, dependencies);