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