• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2017 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #ifndef SkTaskGroup2D_DEFINED
9 #define SkTaskGroup2D_DEFINED
10 
11 #include "SkTaskGroup.h"
12 
13 #include <mutex>
14 #include <vector>
15 
16 // A 2D grid (height rows x width columns) of tasks.
17 //
18 // The task on row i and column j is abstracted as Work2D(i, j). We guarantee that the task on the
19 // same row will be executed in order (i.e., Work2D(1, 1) is guaranteed to finish before calling
20 // Work2D(1, 2)). Tasks in different rows can happen in any order.
21 //
22 // The height (number of rows) is fixed. The width (number of columns) may be dynamically expanded.
23 //
24 // The tasks will eventually be executed on the executor with threadCnt number of hardware threads.
25 class SkTaskGroup2D {
26 public:
27     using Work2D = std::function<void(int, int)>;
28 
SkTaskGroup2D(Work2D && work,int height,SkExecutor * executor,int threadCnt)29     SkTaskGroup2D(Work2D&& work, int height, SkExecutor* executor, int threadCnt)
30             : fWork(work), fHeight(height), fThreadCnt(threadCnt), fIsFinishing(false), fWidth(0)
31             , fThreadsGroup(new SkTaskGroup(*executor)) {}
32 
~SkTaskGroup2D()33     virtual ~SkTaskGroup2D() {}
34 
35     virtual void addColumn(); // Add a new column of tasks.
36 
37     void start(); // start threads to execute tasks
38     void finish(); // wait and finish all tasks (no more tasks can be added after calling this)
39 
isFinishing()40     SK_ALWAYS_INLINE bool isFinishing() const {
41         return fIsFinishing.load(std::memory_order_relaxed);
42     }
43 
44 protected:
45     static constexpr int MAX_CACHE_LINE = 64;
46 
47     // Finish all tasks on the threadId and then return.
48     virtual void work(int threadId) = 0;
49 
50     Work2D      fWork; // fWork(i, j) is the task to be done on row i and column j
51     const int   fHeight;
52     const int   fThreadCnt;
53 
54     std::atomic<bool>   fIsFinishing;
55     std::atomic<int>    fWidth;
56 
57     std::unique_ptr<SkTaskGroup> fThreadsGroup;
58 };
59 
60 // A simple spinning task group that assumes height equals threadCnt.
61 class SkSpinningTaskGroup2D final : public SkTaskGroup2D {
62 public:
SkSpinningTaskGroup2D(Work2D && w,int h,SkExecutor * x,int t)63     SkSpinningTaskGroup2D(Work2D&& w, int h, SkExecutor* x, int t)
64             : SkTaskGroup2D(std::move(w), h, x, t), fRowData(h) {
65         SkASSERT(h == t); // height must be equal to threadCnt
66     }
67 
68 protected:
69     void work(int threadId) override;
70 
71 private:
72     // alignas(MAX_CACHE_LINE) to avoid false sharing by cache lines
73     struct alignas(MAX_CACHE_LINE) RowData {
RowDataRowData74         RowData() : fNextColumn(0) {}
75 
76         int fNextColumn; // next column index to be executed
77     };
78 
79     std::vector<RowData>  fRowData;
80 };
81 
82 class SkFlexibleTaskGroup2D final : public SkTaskGroup2D {
83 public:
84     SkFlexibleTaskGroup2D(Work2D&&, int, SkExecutor*, int);
85 
86 protected:
87     void work(int threadId) override;
88 
89 private:
90     // alignas(MAX_CACHE_LINE) to avoid false sharing by cache lines
91     struct alignas(MAX_CACHE_LINE) RowData {
RowDataRowData92         RowData() : fNextColumn(0) {}
93 
94         int         fNextColumn; // next column index to be executed
95         std::mutex  fMutex;      // the mutex for the thread to acquire
96     };
97 
98     struct alignas(MAX_CACHE_LINE) ThreadData {
ThreadDataThreadData99         ThreadData() : fRowIndex(0) {}
100 
101         int fRowIndex; // the row that the current thread is working on
102     };
103 
104     std::vector<RowData>    fRowData;
105     std::vector<ThreadData> fThreadData;
106 };
107 
108 #endif//SkTaskGroup2D_DEFINED
109