xref: /aosp_15_r20/frameworks/rs/toolkit/TaskProcessor.h (revision e1eccf28f96817838ad6867f7f39d2351ec11f56)
1*e1eccf28SAndroid Build Coastguard Worker /*
2*e1eccf28SAndroid Build Coastguard Worker  * Copyright (C) 2021 The Android Open Source Project
3*e1eccf28SAndroid Build Coastguard Worker  *
4*e1eccf28SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*e1eccf28SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*e1eccf28SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*e1eccf28SAndroid Build Coastguard Worker  *
8*e1eccf28SAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*e1eccf28SAndroid Build Coastguard Worker  *
10*e1eccf28SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*e1eccf28SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*e1eccf28SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*e1eccf28SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*e1eccf28SAndroid Build Coastguard Worker  * limitations under the License.
15*e1eccf28SAndroid Build Coastguard Worker  */
16*e1eccf28SAndroid Build Coastguard Worker 
17*e1eccf28SAndroid Build Coastguard Worker #ifndef ANDROID_RENDERSCRIPT_TOOLKIT_TASKPROCESSOR_H
18*e1eccf28SAndroid Build Coastguard Worker #define ANDROID_RENDERSCRIPT_TOOLKIT_TASKPROCESSOR_H
19*e1eccf28SAndroid Build Coastguard Worker 
20*e1eccf28SAndroid Build Coastguard Worker #include <android-base/thread_annotations.h>
21*e1eccf28SAndroid Build Coastguard Worker 
22*e1eccf28SAndroid Build Coastguard Worker #include <atomic>
23*e1eccf28SAndroid Build Coastguard Worker #include <condition_variable>
24*e1eccf28SAndroid Build Coastguard Worker #include <cstddef>
25*e1eccf28SAndroid Build Coastguard Worker #include <mutex>
26*e1eccf28SAndroid Build Coastguard Worker #include <thread>
27*e1eccf28SAndroid Build Coastguard Worker #include <vector>
28*e1eccf28SAndroid Build Coastguard Worker 
29*e1eccf28SAndroid Build Coastguard Worker namespace android {
30*e1eccf28SAndroid Build Coastguard Worker namespace renderscript {
31*e1eccf28SAndroid Build Coastguard Worker 
32*e1eccf28SAndroid Build Coastguard Worker /**
33*e1eccf28SAndroid Build Coastguard Worker  * Description of the data to be processed for one Toolkit method call, e.g. one blur or one
34*e1eccf28SAndroid Build Coastguard Worker  * blend operation.
35*e1eccf28SAndroid Build Coastguard Worker  *
36*e1eccf28SAndroid Build Coastguard Worker  * The data to be processed is a 2D array of cells. Each cell is a vector of 1 to 4 unsigned bytes.
37*e1eccf28SAndroid Build Coastguard Worker  * The most typical configuration is a 2D array of uchar4 used to represent RGBA images.
38*e1eccf28SAndroid Build Coastguard Worker  *
39*e1eccf28SAndroid Build Coastguard Worker  * This is a base class. There will be a subclass for each Toolkit op.
40*e1eccf28SAndroid Build Coastguard Worker  *
41*e1eccf28SAndroid Build Coastguard Worker  * Typical usage of a derived class would look like:
42*e1eccf28SAndroid Build Coastguard Worker  *    BlurTask task(in, out, sizeX, sizeY, vectorSize, etc);
43*e1eccf28SAndroid Build Coastguard Worker  *    processor->doTask(&task);
44*e1eccf28SAndroid Build Coastguard Worker  *
45*e1eccf28SAndroid Build Coastguard Worker  * The TaskProcessor should call setTiling() and setUsesSimd() once, before calling processTile().
46*e1eccf28SAndroid Build Coastguard Worker  * Other classes should not call setTiling(), setUsesSimd(), and processTile().
47*e1eccf28SAndroid Build Coastguard Worker  */
48*e1eccf28SAndroid Build Coastguard Worker class Task {
49*e1eccf28SAndroid Build Coastguard Worker    protected:
50*e1eccf28SAndroid Build Coastguard Worker     /**
51*e1eccf28SAndroid Build Coastguard Worker      * Number of cells in the X direction.
52*e1eccf28SAndroid Build Coastguard Worker      */
53*e1eccf28SAndroid Build Coastguard Worker     const size_t mSizeX;
54*e1eccf28SAndroid Build Coastguard Worker     /**
55*e1eccf28SAndroid Build Coastguard Worker      * Number of cells in the Y direction.
56*e1eccf28SAndroid Build Coastguard Worker      */
57*e1eccf28SAndroid Build Coastguard Worker     const size_t mSizeY;
58*e1eccf28SAndroid Build Coastguard Worker     /**
59*e1eccf28SAndroid Build Coastguard Worker      * Number of elements in a vector (cell). From 1-4.
60*e1eccf28SAndroid Build Coastguard Worker      */
61*e1eccf28SAndroid Build Coastguard Worker     const size_t mVectorSize;
62*e1eccf28SAndroid Build Coastguard Worker     /**
63*e1eccf28SAndroid Build Coastguard Worker      * Whether the task prefers the processData call to represent the work to be done as
64*e1eccf28SAndroid Build Coastguard Worker      * one line rather than a rectangle. This would be the case for work that don't involve
65*e1eccf28SAndroid Build Coastguard Worker      * vertical neighbors, e.g. blend or histogram. A task would prefer this to minimize the
66*e1eccf28SAndroid Build Coastguard Worker      * number of SIMD calls to make, i.e. have one call that covers all the rows.
67*e1eccf28SAndroid Build Coastguard Worker      *
68*e1eccf28SAndroid Build Coastguard Worker      * This setting will be used only when a tile covers the entire width of the data to be
69*e1eccf28SAndroid Build Coastguard Worker      * processed.
70*e1eccf28SAndroid Build Coastguard Worker      */
71*e1eccf28SAndroid Build Coastguard Worker     const bool mPrefersDataAsOneRow;
72*e1eccf28SAndroid Build Coastguard Worker     /**
73*e1eccf28SAndroid Build Coastguard Worker      * Whether the processor we're working on supports SIMD operations.
74*e1eccf28SAndroid Build Coastguard Worker      */
75*e1eccf28SAndroid Build Coastguard Worker     bool mUsesSimd = false;
76*e1eccf28SAndroid Build Coastguard Worker 
77*e1eccf28SAndroid Build Coastguard Worker    private:
78*e1eccf28SAndroid Build Coastguard Worker     /**
79*e1eccf28SAndroid Build Coastguard Worker      * If not null, we'll process a subset of the whole 2D array. This specifies the restriction.
80*e1eccf28SAndroid Build Coastguard Worker      */
81*e1eccf28SAndroid Build Coastguard Worker     const struct Restriction* mRestriction;
82*e1eccf28SAndroid Build Coastguard Worker 
83*e1eccf28SAndroid Build Coastguard Worker     /**
84*e1eccf28SAndroid Build Coastguard Worker      * We'll divide the work into rectangular tiles. See setTiling().
85*e1eccf28SAndroid Build Coastguard Worker      */
86*e1eccf28SAndroid Build Coastguard Worker 
87*e1eccf28SAndroid Build Coastguard Worker     /**
88*e1eccf28SAndroid Build Coastguard Worker      * Size of a tile in the X direction, as a number of cells.
89*e1eccf28SAndroid Build Coastguard Worker      */
90*e1eccf28SAndroid Build Coastguard Worker     size_t mCellsPerTileX = 0;
91*e1eccf28SAndroid Build Coastguard Worker     /**
92*e1eccf28SAndroid Build Coastguard Worker      * Size of a tile in the Y direction, as a number of cells.
93*e1eccf28SAndroid Build Coastguard Worker      */
94*e1eccf28SAndroid Build Coastguard Worker     size_t mCellsPerTileY = 0;
95*e1eccf28SAndroid Build Coastguard Worker     /**
96*e1eccf28SAndroid Build Coastguard Worker      * Number of tiles per row of the restricted area we're working on.
97*e1eccf28SAndroid Build Coastguard Worker      */
98*e1eccf28SAndroid Build Coastguard Worker     size_t mTilesPerRow = 0;
99*e1eccf28SAndroid Build Coastguard Worker     /**
100*e1eccf28SAndroid Build Coastguard Worker      * Number of tiles per column of the restricted area we're working on.
101*e1eccf28SAndroid Build Coastguard Worker      */
102*e1eccf28SAndroid Build Coastguard Worker     size_t mTilesPerColumn = 0;
103*e1eccf28SAndroid Build Coastguard Worker 
104*e1eccf28SAndroid Build Coastguard Worker    public:
105*e1eccf28SAndroid Build Coastguard Worker     /**
106*e1eccf28SAndroid Build Coastguard Worker      * Construct a task.
107*e1eccf28SAndroid Build Coastguard Worker      *
108*e1eccf28SAndroid Build Coastguard Worker      * sizeX and sizeY should be greater than 0. vectorSize should be between 1 and 4.
109*e1eccf28SAndroid Build Coastguard Worker      * The restriction should outlive this instance. The Toolkit validates the
110*e1eccf28SAndroid Build Coastguard Worker      * arguments so we won't do that again here.
111*e1eccf28SAndroid Build Coastguard Worker      */
Task(size_t sizeX,size_t sizeY,size_t vectorSize,bool prefersDataAsOneRow,const Restriction * restriction)112*e1eccf28SAndroid Build Coastguard Worker     Task(size_t sizeX, size_t sizeY, size_t vectorSize, bool prefersDataAsOneRow,
113*e1eccf28SAndroid Build Coastguard Worker          const Restriction* restriction)
114*e1eccf28SAndroid Build Coastguard Worker         : mSizeX{sizeX},
115*e1eccf28SAndroid Build Coastguard Worker           mSizeY{sizeY},
116*e1eccf28SAndroid Build Coastguard Worker           mVectorSize{vectorSize},
117*e1eccf28SAndroid Build Coastguard Worker           mPrefersDataAsOneRow{prefersDataAsOneRow},
118*e1eccf28SAndroid Build Coastguard Worker           mRestriction{restriction} {}
~Task()119*e1eccf28SAndroid Build Coastguard Worker     virtual ~Task() {}
120*e1eccf28SAndroid Build Coastguard Worker 
setUsesSimd(bool uses)121*e1eccf28SAndroid Build Coastguard Worker     void setUsesSimd(bool uses) { mUsesSimd = uses; }
122*e1eccf28SAndroid Build Coastguard Worker 
123*e1eccf28SAndroid Build Coastguard Worker     /**
124*e1eccf28SAndroid Build Coastguard Worker      * Divide the work into a number of tiles that can be distributed to the various threads.
125*e1eccf28SAndroid Build Coastguard Worker      * A tile will be a rectangular region. To be robust, we'll want to handle regular cases
126*e1eccf28SAndroid Build Coastguard Worker      * like 400x300 but also unusual ones like 1x120000, 120000x1, 1x1.
127*e1eccf28SAndroid Build Coastguard Worker      *
128*e1eccf28SAndroid Build Coastguard Worker      * We have a target size for the tiles, which corresponds roughly to how much data a thread
129*e1eccf28SAndroid Build Coastguard Worker      * will want to process before checking for more work. If the target is set too low, we'll spend
130*e1eccf28SAndroid Build Coastguard Worker      * more time in synchronization. If it's too large, some cores may not be used as efficiently.
131*e1eccf28SAndroid Build Coastguard Worker      *
132*e1eccf28SAndroid Build Coastguard Worker      * This method returns the number of tiles.
133*e1eccf28SAndroid Build Coastguard Worker      *
134*e1eccf28SAndroid Build Coastguard Worker      * @param targetTileSizeInBytes Target size. Values less than 1000 will be treated as 1000.
135*e1eccf28SAndroid Build Coastguard Worker      */
136*e1eccf28SAndroid Build Coastguard Worker     int setTiling(unsigned int targetTileSizeInBytes);
137*e1eccf28SAndroid Build Coastguard Worker 
138*e1eccf28SAndroid Build Coastguard Worker     /**
139*e1eccf28SAndroid Build Coastguard Worker      * This is called by the TaskProcessor to instruct the task to process a tile.
140*e1eccf28SAndroid Build Coastguard Worker      *
141*e1eccf28SAndroid Build Coastguard Worker      * @param threadIndex The index of the thread that's processing the tile.
142*e1eccf28SAndroid Build Coastguard Worker      * @param tileIndex The index of the tile to process.
143*e1eccf28SAndroid Build Coastguard Worker      */
144*e1eccf28SAndroid Build Coastguard Worker     void processTile(unsigned int threadIndex, size_t tileIndex);
145*e1eccf28SAndroid Build Coastguard Worker 
146*e1eccf28SAndroid Build Coastguard Worker    private:
147*e1eccf28SAndroid Build Coastguard Worker     /**
148*e1eccf28SAndroid Build Coastguard Worker      * Call to the derived class to process the data bounded by the rectangle specified
149*e1eccf28SAndroid Build Coastguard Worker      * by (startX, startY) and (endX, endY). The end values are EXCLUDED. This rectangle
150*e1eccf28SAndroid Build Coastguard Worker      * will be contained with the restriction, if one is provided.
151*e1eccf28SAndroid Build Coastguard Worker      */
152*e1eccf28SAndroid Build Coastguard Worker     virtual void processData(int threadIndex, size_t startX, size_t startY, size_t endX,
153*e1eccf28SAndroid Build Coastguard Worker                              size_t endY) = 0;
154*e1eccf28SAndroid Build Coastguard Worker };
155*e1eccf28SAndroid Build Coastguard Worker 
156*e1eccf28SAndroid Build Coastguard Worker /**
157*e1eccf28SAndroid Build Coastguard Worker  * There's one instance of the task processor for the Toolkit. This class owns the thread pool,
158*e1eccf28SAndroid Build Coastguard Worker  * and dispatches the tiles of work to the threads.
159*e1eccf28SAndroid Build Coastguard Worker  */
160*e1eccf28SAndroid Build Coastguard Worker class TaskProcessor {
161*e1eccf28SAndroid Build Coastguard Worker     /**
162*e1eccf28SAndroid Build Coastguard Worker      * Does this processor support SIMD-like instructions?
163*e1eccf28SAndroid Build Coastguard Worker      */
164*e1eccf28SAndroid Build Coastguard Worker     const bool mUsesSimd;
165*e1eccf28SAndroid Build Coastguard Worker     /**
166*e1eccf28SAndroid Build Coastguard Worker      * The number of separate threads we'll spawn. It's one less than the number of threads that
167*e1eccf28SAndroid Build Coastguard Worker      * do the work as the client thread that starts the work will also be used.
168*e1eccf28SAndroid Build Coastguard Worker      */
169*e1eccf28SAndroid Build Coastguard Worker     const unsigned int mNumberOfPoolThreads;
170*e1eccf28SAndroid Build Coastguard Worker     /**
171*e1eccf28SAndroid Build Coastguard Worker      * Ensures that only one task is done at a time.
172*e1eccf28SAndroid Build Coastguard Worker      */
173*e1eccf28SAndroid Build Coastguard Worker     std::mutex mTaskMutex;
174*e1eccf28SAndroid Build Coastguard Worker     /**
175*e1eccf28SAndroid Build Coastguard Worker      * Ensures consistent access to the shared queue state.
176*e1eccf28SAndroid Build Coastguard Worker      */
177*e1eccf28SAndroid Build Coastguard Worker     std::mutex mQueueMutex;
178*e1eccf28SAndroid Build Coastguard Worker     /**
179*e1eccf28SAndroid Build Coastguard Worker      * The thread pool workers.
180*e1eccf28SAndroid Build Coastguard Worker      */
181*e1eccf28SAndroid Build Coastguard Worker     std::vector<std::thread> mPoolThreads;
182*e1eccf28SAndroid Build Coastguard Worker     /**
183*e1eccf28SAndroid Build Coastguard Worker      * The task being processed, if any. We only do one task at a time. We could create a queue
184*e1eccf28SAndroid Build Coastguard Worker      * of tasks but using a mTaskMutex is sufficient for now.
185*e1eccf28SAndroid Build Coastguard Worker      */
186*e1eccf28SAndroid Build Coastguard Worker     Task* mCurrentTask GUARDED_BY(mTaskMutex) = nullptr;
187*e1eccf28SAndroid Build Coastguard Worker     /**
188*e1eccf28SAndroid Build Coastguard Worker      * Signals that the mPoolThreads should terminate.
189*e1eccf28SAndroid Build Coastguard Worker      */
190*e1eccf28SAndroid Build Coastguard Worker     bool mStopThreads GUARDED_BY(mQueueMutex) = false;
191*e1eccf28SAndroid Build Coastguard Worker     /**
192*e1eccf28SAndroid Build Coastguard Worker      * Signaled when work is available or the mPoolThreads need to shut down. mStopThreads is used
193*e1eccf28SAndroid Build Coastguard Worker      * to distinguish between the two.
194*e1eccf28SAndroid Build Coastguard Worker      */
195*e1eccf28SAndroid Build Coastguard Worker     std::condition_variable mWorkAvailableOrStop;
196*e1eccf28SAndroid Build Coastguard Worker     /**
197*e1eccf28SAndroid Build Coastguard Worker      * Signaled when the work for the task is finished.
198*e1eccf28SAndroid Build Coastguard Worker      */
199*e1eccf28SAndroid Build Coastguard Worker     std::condition_variable mWorkIsFinished;
200*e1eccf28SAndroid Build Coastguard Worker     /**
201*e1eccf28SAndroid Build Coastguard Worker      * A user task, e.g. a blend or a blur, is split into a number of tiles. When a thread starts
202*e1eccf28SAndroid Build Coastguard Worker      * working on a new tile, it uses this count to identify which tile to work on. The tile
203*e1eccf28SAndroid Build Coastguard Worker      * number is sufficient to determine the boundaries of the data to process.
204*e1eccf28SAndroid Build Coastguard Worker      *
205*e1eccf28SAndroid Build Coastguard Worker      * The number of tiles left to process.
206*e1eccf28SAndroid Build Coastguard Worker      */
207*e1eccf28SAndroid Build Coastguard Worker     int mTilesNotYetStarted GUARDED_BY(mQueueMutex) = 0;
208*e1eccf28SAndroid Build Coastguard Worker     /**
209*e1eccf28SAndroid Build Coastguard Worker      * The number of tiles currently being processed. Must not be greater than
210*e1eccf28SAndroid Build Coastguard Worker      * mNumberOfPoolThreads + 1.
211*e1eccf28SAndroid Build Coastguard Worker      */
212*e1eccf28SAndroid Build Coastguard Worker     int mTilesInProcess GUARDED_BY(mQueueMutex) = 0;
213*e1eccf28SAndroid Build Coastguard Worker 
214*e1eccf28SAndroid Build Coastguard Worker     /**
215*e1eccf28SAndroid Build Coastguard Worker      * Determines how we'll tile the work and signals the thread pool of available work.
216*e1eccf28SAndroid Build Coastguard Worker      *
217*e1eccf28SAndroid Build Coastguard Worker      * @param task The task to be performed.
218*e1eccf28SAndroid Build Coastguard Worker      */
219*e1eccf28SAndroid Build Coastguard Worker     void startWork(Task* task) REQUIRES(mTaskMutex);
220*e1eccf28SAndroid Build Coastguard Worker 
221*e1eccf28SAndroid Build Coastguard Worker     /**
222*e1eccf28SAndroid Build Coastguard Worker      * Tells the thread to start processing work off the queue.
223*e1eccf28SAndroid Build Coastguard Worker      *
224*e1eccf28SAndroid Build Coastguard Worker      * The flag is used for prevent the main thread from blocking forever if the work is
225*e1eccf28SAndroid Build Coastguard Worker      * so trivial that the worker threads complete the work before the main thread calls this
226*e1eccf28SAndroid Build Coastguard Worker      * method.
227*e1eccf28SAndroid Build Coastguard Worker      *
228*e1eccf28SAndroid Build Coastguard Worker      * @param threadIndex The index number (0..mNumberOfPoolThreads) this thread will referred by.
229*e1eccf28SAndroid Build Coastguard Worker      * @param returnWhenNoWork If there's no work, return immediately.
230*e1eccf28SAndroid Build Coastguard Worker      */
231*e1eccf28SAndroid Build Coastguard Worker     void processTilesOfWork(int threadIndex, bool returnWhenNoWork);
232*e1eccf28SAndroid Build Coastguard Worker 
233*e1eccf28SAndroid Build Coastguard Worker     /**
234*e1eccf28SAndroid Build Coastguard Worker      * Wait for the pool workers to complete the work on the current task.
235*e1eccf28SAndroid Build Coastguard Worker      */
236*e1eccf28SAndroid Build Coastguard Worker     void waitForPoolWorkersToComplete();
237*e1eccf28SAndroid Build Coastguard Worker 
238*e1eccf28SAndroid Build Coastguard Worker    public:
239*e1eccf28SAndroid Build Coastguard Worker     /**
240*e1eccf28SAndroid Build Coastguard Worker      * Create the processor.
241*e1eccf28SAndroid Build Coastguard Worker      *
242*e1eccf28SAndroid Build Coastguard Worker      * @param numThreads The total number of threads to use. If 0, we'll decided based on system
243*e1eccf28SAndroid Build Coastguard Worker      * properties.
244*e1eccf28SAndroid Build Coastguard Worker      */
245*e1eccf28SAndroid Build Coastguard Worker     explicit TaskProcessor(unsigned int numThreads = 0);
246*e1eccf28SAndroid Build Coastguard Worker 
247*e1eccf28SAndroid Build Coastguard Worker     ~TaskProcessor();
248*e1eccf28SAndroid Build Coastguard Worker 
249*e1eccf28SAndroid Build Coastguard Worker     /**
250*e1eccf28SAndroid Build Coastguard Worker      * Do the specified task. Returns only after the task has been completed.
251*e1eccf28SAndroid Build Coastguard Worker      */
252*e1eccf28SAndroid Build Coastguard Worker     void doTask(Task* task);
253*e1eccf28SAndroid Build Coastguard Worker 
254*e1eccf28SAndroid Build Coastguard Worker     /**
255*e1eccf28SAndroid Build Coastguard Worker      * Some Tasks need to allocate temporary storage for each worker thread.
256*e1eccf28SAndroid Build Coastguard Worker      * This provides the number of threads.
257*e1eccf28SAndroid Build Coastguard Worker      */
getNumberOfThreads()258*e1eccf28SAndroid Build Coastguard Worker     unsigned int getNumberOfThreads() const { return mNumberOfPoolThreads + 1; }
259*e1eccf28SAndroid Build Coastguard Worker };
260*e1eccf28SAndroid Build Coastguard Worker 
261*e1eccf28SAndroid Build Coastguard Worker }  // namespace renderscript
262*e1eccf28SAndroid Build Coastguard Worker }  // namespace android
263*e1eccf28SAndroid Build Coastguard Worker 
264*e1eccf28SAndroid Build Coastguard Worker #endif  // ANDROID_RENDERSCRIPT_TOOLKIT_TASKPROCESSOR_H
265