/* * Copyright 2017 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef SkTaskGroup2D_DEFINED #define SkTaskGroup2D_DEFINED #include "SkTaskGroup.h" #include <mutex> #include <vector> // A 2D grid (height rows x width columns) of tasks. // // The task on row i and column j is abstracted as Work2D(i, j). We guarantee that the task on the // same row will be executed in order (i.e., Work2D(1, 1) is guaranteed to finish before calling // Work2D(1, 2)). Tasks in different rows can happen in any order. // // The height (number of rows) is fixed. The width (number of columns) may be dynamically expanded. // // The tasks will eventually be executed on the executor with threadCnt number of hardware threads. class SkTaskGroup2D { public: using Work2D = std::function<void(int, int)>; SkTaskGroup2D(Work2D&& work, int height, SkExecutor* executor, int threadCnt) : fWork(work), fHeight(height), fThreadCnt(threadCnt), fIsFinishing(false), fWidth(0) , fThreadsGroup(new SkTaskGroup(*executor)) {} virtual ~SkTaskGroup2D() {} virtual void addColumn(); // Add a new column of tasks. void start(); // start threads to execute tasks void finish(); // wait and finish all tasks (no more tasks can be added after calling this) SK_ALWAYS_INLINE bool isFinishing() const { return fIsFinishing.load(std::memory_order_relaxed); } protected: static constexpr int MAX_CACHE_LINE = 64; // Finish all tasks on the threadId and then return. virtual void work(int threadId) = 0; Work2D fWork; // fWork(i, j) is the task to be done on row i and column j const int fHeight; const int fThreadCnt; std::atomic<bool> fIsFinishing; std::atomic<int> fWidth; std::unique_ptr<SkTaskGroup> fThreadsGroup; }; // A simple spinning task group that assumes height equals threadCnt. class SkSpinningTaskGroup2D final : public SkTaskGroup2D { public: SkSpinningTaskGroup2D(Work2D&& w, int h, SkExecutor* x, int t) : SkTaskGroup2D(std::move(w), h, x, t), fRowData(h) { SkASSERT(h == t); // height must be equal to threadCnt } protected: void work(int threadId) override; private: // alignas(MAX_CACHE_LINE) to avoid false sharing by cache lines struct alignas(MAX_CACHE_LINE) RowData { RowData() : fNextColumn(0) {} int fNextColumn; // next column index to be executed }; std::vector<RowData> fRowData; }; class SkFlexibleTaskGroup2D final : public SkTaskGroup2D { public: SkFlexibleTaskGroup2D(Work2D&&, int, SkExecutor*, int); protected: void work(int threadId) override; private: // alignas(MAX_CACHE_LINE) to avoid false sharing by cache lines struct alignas(MAX_CACHE_LINE) RowData { RowData() : fNextColumn(0) {} int fNextColumn; // next column index to be executed std::mutex fMutex; // the mutex for the thread to acquire }; struct alignas(MAX_CACHE_LINE) ThreadData { ThreadData() : fRowIndex(0) {} int fRowIndex; // the row that the current thread is working on }; std::vector<RowData> fRowData; std::vector<ThreadData> fThreadData; }; #endif//SkTaskGroup2D_DEFINED