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 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 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 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: 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 { 74 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 { 92 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 { 99 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