1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "rsCpuCore.h"
18 #include "rsCpuScript.h"
19 #include "rsCpuScriptGroup.h"
20 #include "rsCpuScriptGroup2.h"
21 
22 #include <malloc.h>
23 #include "rsContext.h"
24 
25 #include <sys/types.h>
26 #include <sys/resource.h>
27 #include <sched.h>
28 #include <sys/syscall.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <unistd.h>
32 
33 #define REDUCE_ALOGV(mtls, level, ...) do { if ((mtls)->logReduce >= (level)) ALOGV(__VA_ARGS__); } while(0)
34 
35 static pthread_key_t gThreadTLSKey = 0;
36 static uint32_t gThreadTLSKeyCount = 0;
37 static pthread_mutex_t gInitMutex = PTHREAD_MUTEX_INITIALIZER;
38 
39 namespace android {
40 namespace renderscript {
41 
42 bool gArchUseSIMD = false;
43 
~RsdCpuReference()44 RsdCpuReference::~RsdCpuReference() {
45 }
46 
create(Context * rsc,uint32_t version_major,uint32_t version_minor,sym_lookup_t lfn,script_lookup_t slfn,RSSelectRTCallback pSelectRTCallback,const char * pBccPluginName)47 RsdCpuReference * RsdCpuReference::create(Context *rsc, uint32_t version_major,
48         uint32_t version_minor, sym_lookup_t lfn, script_lookup_t slfn
49         , RSSelectRTCallback pSelectRTCallback,
50         const char *pBccPluginName
51         ) {
52 
53     RsdCpuReferenceImpl *cpu = new RsdCpuReferenceImpl(rsc);
54     if (!cpu) {
55         return nullptr;
56     }
57     if (!cpu->init(version_major, version_minor, lfn, slfn)) {
58         delete cpu;
59         return nullptr;
60     }
61 
62     cpu->setSelectRTCallback(pSelectRTCallback);
63     if (pBccPluginName) {
64         cpu->setBccPluginName(pBccPluginName);
65     }
66 
67     return cpu;
68 }
69 
70 
getTlsContext()71 Context * RsdCpuReference::getTlsContext() {
72     ScriptTLSStruct * tls = (ScriptTLSStruct *)pthread_getspecific(gThreadTLSKey);
73     return tls->mContext;
74 }
75 
getTlsScript()76 const Script * RsdCpuReference::getTlsScript() {
77     ScriptTLSStruct * tls = (ScriptTLSStruct *)pthread_getspecific(gThreadTLSKey);
78     return tls->mScript;
79 }
80 
getThreadTLSKey()81 pthread_key_t RsdCpuReference::getThreadTLSKey(){ return gThreadTLSKey; }
82 
83 ////////////////////////////////////////////////////////////
84 ///
85 
RsdCpuReferenceImpl(Context * rsc)86 RsdCpuReferenceImpl::RsdCpuReferenceImpl(Context *rsc) {
87     mRSC = rsc;
88 
89     version_major = 0;
90     version_minor = 0;
91     mInKernel = false;
92     memset(&mWorkers, 0, sizeof(mWorkers));
93     memset(&mTlsStruct, 0, sizeof(mTlsStruct));
94     mExit = false;
95     mSelectRTCallback = nullptr;
96     mEmbedGlobalInfo = true;
97     mEmbedGlobalInfoSkipConstant = true;
98 }
99 
100 
helperThreadProc(void * vrsc)101 void * RsdCpuReferenceImpl::helperThreadProc(void *vrsc) {
102     RsdCpuReferenceImpl *dc = (RsdCpuReferenceImpl *)vrsc;
103 
104     uint32_t idx = __sync_fetch_and_add(&dc->mWorkers.mLaunchCount, 1);
105 
106     //ALOGV("RS helperThread starting %p idx=%i", dc, idx);
107 
108     dc->mWorkers.mLaunchSignals[idx].init();
109     dc->mWorkers.mNativeThreadId[idx] = gettid();
110 
111     memset(&dc->mTlsStruct, 0, sizeof(dc->mTlsStruct));
112     int status = pthread_setspecific(gThreadTLSKey, &dc->mTlsStruct);
113     if (status) {
114         ALOGE("pthread_setspecific %i", status);
115     }
116 
117 #if 0
118     typedef struct {uint64_t bits[1024 / 64]; } cpu_set_t;
119     cpu_set_t cpuset;
120     memset(&cpuset, 0, sizeof(cpuset));
121     cpuset.bits[idx / 64] |= 1ULL << (idx % 64);
122     int ret = syscall(241, rsc->mWorkers.mNativeThreadId[idx],
123               sizeof(cpuset), &cpuset);
124     ALOGE("SETAFFINITY ret = %i %s", ret, EGLUtils::strerror(ret));
125 #endif
126 
127     while (!dc->mExit) {
128         dc->mWorkers.mLaunchSignals[idx].wait();
129         if (dc->mWorkers.mLaunchCallback) {
130            // idx +1 is used because the calling thread is always worker 0.
131            dc->mWorkers.mLaunchCallback(dc->mWorkers.mLaunchData, idx+1);
132         }
133         __sync_fetch_and_sub(&dc->mWorkers.mRunningCount, 1);
134         dc->mWorkers.mCompleteSignal.set();
135     }
136 
137     //ALOGV("RS helperThread exited %p idx=%i", dc, idx);
138     return nullptr;
139 }
140 
141 // Launch a kernel.
142 // The callback function is called to execute the kernel.
launchThreads(WorkerCallback_t cbk,void * data)143 void RsdCpuReferenceImpl::launchThreads(WorkerCallback_t cbk, void *data) {
144     mWorkers.mLaunchData = data;
145     mWorkers.mLaunchCallback = cbk;
146 
147     // fast path for very small launches
148     MTLaunchStructCommon *mtls = (MTLaunchStructCommon *)data;
149     if (mtls && mtls->dimPtr->y <= 1 && mtls->end.x <= mtls->start.x + mtls->mSliceSize) {
150         if (mWorkers.mLaunchCallback) {
151             mWorkers.mLaunchCallback(mWorkers.mLaunchData, 0);
152         }
153         return;
154     }
155 
156     mWorkers.mRunningCount = mWorkers.mCount;
157     __sync_synchronize();
158 
159     for (uint32_t ct = 0; ct < mWorkers.mCount; ct++) {
160         mWorkers.mLaunchSignals[ct].set();
161     }
162 
163     // We use the calling thread as one of the workers so we can start without
164     // the delay of the thread wakeup.
165     if (mWorkers.mLaunchCallback) {
166         mWorkers.mLaunchCallback(mWorkers.mLaunchData, 0);
167     }
168 
169     while (__sync_fetch_and_or(&mWorkers.mRunningCount, 0) != 0) {
170         mWorkers.mCompleteSignal.wait();
171     }
172 }
173 
174 
lockMutex()175 void RsdCpuReferenceImpl::lockMutex() {
176     pthread_mutex_lock(&gInitMutex);
177 }
178 
unlockMutex()179 void RsdCpuReferenceImpl::unlockMutex() {
180     pthread_mutex_unlock(&gInitMutex);
181 }
182 
183 // Determine if the CPU we're running on supports SIMD instructions.
GetCpuInfo()184 static void GetCpuInfo() {
185     // Read the CPU flags from /proc/cpuinfo.
186     FILE *cpuinfo = fopen("/proc/cpuinfo", "re");
187 
188     if (!cpuinfo) {
189         return;
190     }
191 
192     char cpuinfostr[4096];
193     // fgets() ends with newline or EOF, need to check the whole
194     // "cpuinfo" file to make sure we can use SIMD or not.
195     while (fgets(cpuinfostr, sizeof(cpuinfostr), cpuinfo)) {
196 #if defined(ARCH_ARM_HAVE_VFP) || defined(ARCH_ARM_USE_INTRINSICS)
197         gArchUseSIMD = strstr(cpuinfostr, " neon") || strstr(cpuinfostr, " asimd");
198 #elif defined(ARCH_X86_HAVE_SSSE3)
199         gArchUseSIMD = strstr(cpuinfostr, " ssse3");
200 #endif
201         if (gArchUseSIMD) {
202             break;
203         }
204     }
205     fclose(cpuinfo);
206 }
207 
init(uint32_t version_major,uint32_t version_minor,sym_lookup_t lfn,script_lookup_t slfn)208 bool RsdCpuReferenceImpl::init(uint32_t version_major, uint32_t version_minor,
209                                sym_lookup_t lfn, script_lookup_t slfn) {
210     mSymLookupFn = lfn;
211     mScriptLookupFn = slfn;
212 
213     lockMutex();
214     if (!gThreadTLSKeyCount) {
215         int status = pthread_key_create(&gThreadTLSKey, nullptr);
216         if (status) {
217             ALOGE("Failed to init thread tls key.");
218             unlockMutex();
219             return false;
220         }
221     }
222     gThreadTLSKeyCount++;
223     unlockMutex();
224 
225     mTlsStruct.mContext = mRSC;
226     mTlsStruct.mScript = nullptr;
227     int status = pthread_setspecific(gThreadTLSKey, &mTlsStruct);
228     if (status) {
229         ALOGE("pthread_setspecific %i", status);
230     }
231 
232     mPageSize = sysconf(_SC_PAGE_SIZE);
233     // ALOGV("page size = %ld", mPageSize);
234 
235     GetCpuInfo();
236 
237     int cpu = sysconf(_SC_NPROCESSORS_CONF);
238     if(mRSC->props.mDebugMaxThreads) {
239         cpu = mRSC->props.mDebugMaxThreads;
240     }
241     if (cpu < 2) {
242         mWorkers.mCount = 0;
243         return true;
244     }
245 
246     // Subtract one from the cpu count because we also use the command thread as a worker.
247     mWorkers.mCount = (uint32_t)(cpu - 1);
248 
249     if (mRSC->props.mLogScripts) {
250       ALOGV("%p Launching thread(s), CPUs %i", mRSC, mWorkers.mCount + 1);
251     }
252 
253     mWorkers.mThreadId = (pthread_t *) calloc(mWorkers.mCount, sizeof(pthread_t));
254     mWorkers.mNativeThreadId = (pid_t *) calloc(mWorkers.mCount, sizeof(pid_t));
255     mWorkers.mLaunchSignals = new Signal[mWorkers.mCount];
256     mWorkers.mLaunchCallback = nullptr;
257 
258     mWorkers.mCompleteSignal.init();
259 
260     mWorkers.mRunningCount = mWorkers.mCount;
261     mWorkers.mLaunchCount = 0;
262     __sync_synchronize();
263 
264     pthread_attr_t threadAttr;
265     status = pthread_attr_init(&threadAttr);
266     if (status) {
267         ALOGE("Failed to init thread attribute.");
268         return false;
269     }
270 
271     for (uint32_t ct=0; ct < mWorkers.mCount; ct++) {
272         status = pthread_create(&mWorkers.mThreadId[ct], &threadAttr, helperThreadProc, this);
273         if (status) {
274             mWorkers.mCount = ct;
275             ALOGE("Created fewer than expected number of RS threads.");
276             break;
277         }
278     }
279     while (__sync_fetch_and_or(&mWorkers.mRunningCount, 0) != 0) {
280         usleep(100);
281     }
282 
283     pthread_attr_destroy(&threadAttr);
284     return true;
285 }
286 
287 
setPriority(int32_t priority)288 void RsdCpuReferenceImpl::setPriority(int32_t priority) {
289     for (uint32_t ct=0; ct < mWorkers.mCount; ct++) {
290         setpriority(PRIO_PROCESS, mWorkers.mNativeThreadId[ct], priority);
291     }
292 }
293 
~RsdCpuReferenceImpl()294 RsdCpuReferenceImpl::~RsdCpuReferenceImpl() {
295     mExit = true;
296     mWorkers.mLaunchData = nullptr;
297     mWorkers.mLaunchCallback = nullptr;
298     mWorkers.mRunningCount = mWorkers.mCount;
299     __sync_synchronize();
300     for (uint32_t ct = 0; ct < mWorkers.mCount; ct++) {
301         mWorkers.mLaunchSignals[ct].set();
302     }
303     void *res;
304     for (uint32_t ct = 0; ct < mWorkers.mCount; ct++) {
305         pthread_join(mWorkers.mThreadId[ct], &res);
306     }
307     // b/23109602
308     // TODO: Refactor the implementation with threadpool to
309     // fix the race condition in the destuctor.
310     // rsAssert(__sync_fetch_and_or(&mWorkers.mRunningCount, 0) == 0);
311     free(mWorkers.mThreadId);
312     free(mWorkers.mNativeThreadId);
313     delete[] mWorkers.mLaunchSignals;
314 
315     // Global structure cleanup.
316     lockMutex();
317     --gThreadTLSKeyCount;
318     if (!gThreadTLSKeyCount) {
319         pthread_key_delete(gThreadTLSKey);
320     }
321     unlockMutex();
322 
323 }
324 
325 // Set up the appropriate input and output pointers to the kernel driver info structure.
326 // Inputs:
327 //   mtls - The MTLaunchStruct holding information about the kernel launch
328 //   fep - The forEach parameters (driver info structure)
329 //   x, y, z, lod, face, a1, a2, a3, a4 - The start offsets into each dimension
FepPtrSetup(const MTLaunchStructForEach * mtls,RsExpandKernelDriverInfo * fep,uint32_t x,uint32_t y,uint32_t z=0,uint32_t lod=0,RsAllocationCubemapFace face=RS_ALLOCATION_CUBEMAP_FACE_POSITIVE_X,uint32_t a1=0,uint32_t a2=0,uint32_t a3=0,uint32_t a4=0)330 static inline void FepPtrSetup(const MTLaunchStructForEach *mtls, RsExpandKernelDriverInfo *fep,
331                                uint32_t x, uint32_t y,
332                                uint32_t z = 0, uint32_t lod = 0,
333                                RsAllocationCubemapFace face = RS_ALLOCATION_CUBEMAP_FACE_POSITIVE_X,
334                                uint32_t a1 = 0, uint32_t a2 = 0, uint32_t a3 = 0, uint32_t a4 = 0) {
335     // When rsForEach passes a null input allocation (as opposed to no input),
336     // fep->inLen can be 1 with mtls->ains[0] being null.
337     // This should only happen on old style kernels.
338     for (uint32_t i = 0; i < fep->inLen; i++) {
339         if (mtls->ains[i] == nullptr) {
340             rsAssert(fep->inLen == 1);
341             continue;
342         }
343         fep->inPtr[i] = (const uint8_t *)mtls->ains[i]->getPointerUnchecked(x, y, z, lod, face, a1, a2, a3, a4);
344     }
345     if (mtls->aout[0] != nullptr) {
346         fep->outPtr[0] = (uint8_t *)mtls->aout[0]->getPointerUnchecked(x, y, z, lod, face, a1, a2, a3, a4);
347     }
348 }
349 
350 // Set up the appropriate input and output pointers to the kernel driver info structure.
351 // Inputs:
352 //   mtls - The MTLaunchStruct holding information about the kernel launch
353 //   redp - The reduce parameters (driver info structure)
354 //   x, y, z - The start offsets into each dimension
RedpPtrSetup(const MTLaunchStructReduce * mtls,RsExpandKernelDriverInfo * redp,uint32_t x,uint32_t y,uint32_t z)355 static inline void RedpPtrSetup(const MTLaunchStructReduce *mtls, RsExpandKernelDriverInfo *redp,
356                                 uint32_t x, uint32_t y, uint32_t z) {
357     for (uint32_t i = 0; i < redp->inLen; i++) {
358         redp->inPtr[i] = (const uint8_t *)mtls->ains[i]->getPointerUnchecked(x, y, z);
359     }
360 }
361 
sliceInt(uint32_t * p,uint32_t val,uint32_t start,uint32_t end)362 static uint32_t sliceInt(uint32_t *p, uint32_t val, uint32_t start, uint32_t end) {
363     if (start >= end) {
364         *p = start;
365         return val;
366     }
367 
368     uint32_t div = end - start;
369 
370     uint32_t n = val / div;
371     *p = (val - (n * div)) + start;
372     return n;
373 }
374 
SelectOuterSlice(const MTLaunchStructCommon * mtls,RsExpandKernelDriverInfo * info,uint32_t sliceNum)375 static bool SelectOuterSlice(const MTLaunchStructCommon *mtls, RsExpandKernelDriverInfo* info, uint32_t sliceNum) {
376     uint32_t r = sliceNum;
377     r = sliceInt(&info->current.z, r, mtls->start.z, mtls->end.z);
378     r = sliceInt(&info->current.lod, r, mtls->start.lod, mtls->end.lod);
379     r = sliceInt(&info->current.face, r, mtls->start.face, mtls->end.face);
380     r = sliceInt(&info->current.array[0], r, mtls->start.array[0], mtls->end.array[0]);
381     r = sliceInt(&info->current.array[1], r, mtls->start.array[1], mtls->end.array[1]);
382     r = sliceInt(&info->current.array[2], r, mtls->start.array[2], mtls->end.array[2]);
383     r = sliceInt(&info->current.array[3], r, mtls->start.array[3], mtls->end.array[3]);
384     return r == 0;
385 }
386 
SelectZSlice(const MTLaunchStructCommon * mtls,RsExpandKernelDriverInfo * info,uint32_t sliceNum)387 static bool SelectZSlice(const MTLaunchStructCommon *mtls, RsExpandKernelDriverInfo* info, uint32_t sliceNum) {
388     return sliceInt(&info->current.z, sliceNum, mtls->start.z, mtls->end.z) == 0;
389 }
390 
walk_general_foreach(void * usr,uint32_t idx)391 static void walk_general_foreach(void *usr, uint32_t idx) {
392     MTLaunchStructForEach *mtls = (MTLaunchStructForEach *)usr;
393     RsExpandKernelDriverInfo fep = mtls->fep;
394     fep.lid = idx;
395     ForEachFunc_t fn = mtls->kernel;
396 
397     while(1) {
398         uint32_t slice = (uint32_t)__sync_fetch_and_add(&mtls->mSliceNum, 1);
399 
400         if (!SelectOuterSlice(mtls, &fep, slice)) {
401             return;
402         }
403 
404         for (fep.current.y = mtls->start.y; fep.current.y < mtls->end.y;
405              fep.current.y++) {
406 
407             FepPtrSetup(mtls, &fep, mtls->start.x,
408                         fep.current.y, fep.current.z, fep.current.lod,
409                         (RsAllocationCubemapFace)fep.current.face,
410                         fep.current.array[0], fep.current.array[1],
411                         fep.current.array[2], fep.current.array[3]);
412 
413             fn(&fep, mtls->start.x, mtls->end.x, mtls->fep.outStride[0]);
414         }
415     }
416 }
417 
walk_2d_foreach(void * usr,uint32_t idx)418 static void walk_2d_foreach(void *usr, uint32_t idx) {
419     MTLaunchStructForEach *mtls = (MTLaunchStructForEach *)usr;
420     RsExpandKernelDriverInfo fep = mtls->fep;
421     fep.lid = idx;
422     ForEachFunc_t fn = mtls->kernel;
423 
424     while (1) {
425         uint32_t slice  = (uint32_t)__sync_fetch_and_add(&mtls->mSliceNum, 1);
426         uint32_t yStart = mtls->start.y + slice * mtls->mSliceSize;
427         uint32_t yEnd   = yStart + mtls->mSliceSize;
428 
429         yEnd = rsMin(yEnd, mtls->end.y);
430 
431         if (yEnd <= yStart) {
432             return;
433         }
434 
435         for (fep.current.y = yStart; fep.current.y < yEnd; fep.current.y++) {
436             FepPtrSetup(mtls, &fep, mtls->start.x, fep.current.y);
437 
438             fn(&fep, mtls->start.x, mtls->end.x, fep.outStride[0]);
439         }
440     }
441 }
442 
walk_1d_foreach(void * usr,uint32_t idx)443 static void walk_1d_foreach(void *usr, uint32_t idx) {
444     MTLaunchStructForEach *mtls = (MTLaunchStructForEach *)usr;
445     RsExpandKernelDriverInfo fep = mtls->fep;
446     fep.lid = idx;
447     ForEachFunc_t fn = mtls->kernel;
448 
449     while (1) {
450         uint32_t slice  = (uint32_t)__sync_fetch_and_add(&mtls->mSliceNum, 1);
451         uint32_t xStart = mtls->start.x + slice * mtls->mSliceSize;
452         uint32_t xEnd   = xStart + mtls->mSliceSize;
453 
454         xEnd = rsMin(xEnd, mtls->end.x);
455 
456         if (xEnd <= xStart) {
457             return;
458         }
459 
460         FepPtrSetup(mtls, &fep, xStart, 0);
461 
462         fn(&fep, xStart, xEnd, fep.outStride[0]);
463     }
464 }
465 
466 // The function format_bytes() is an auxiliary function to assist in logging.
467 //
468 // Bytes are read from an input (inBuf) and written (as pairs of hex digits)
469 // to an output (outBuf).
470 //
471 // Output format:
472 // - starts with ": "
473 // - each input byte is translated to a pair of hex digits
474 // - bytes are separated by "." except that every fourth separator is "|"
475 // - if the input is sufficiently long, the output is truncated and terminated with "..."
476 //
477 // Arguments:
478 // - outBuf  -- Pointer to buffer of type "FormatBuf" into which output is written
479 // - inBuf   -- Pointer to bytes which are to be formatted into outBuf
480 // - inBytes -- Number of bytes in inBuf
481 //
482 // Constant:
483 // - kFormatInBytesMax -- Only min(kFormatInBytesMax, inBytes) bytes will be read
484 //                        from inBuf
485 //
486 // Return value:
487 // - pointer (const char *) to output (which is part of outBuf)
488 //
489 static const int kFormatInBytesMax = 16;
490 // ": " + 2 digits per byte + 1 separator between bytes + "..." + null
491 typedef char FormatBuf[2 + kFormatInBytesMax*2 + (kFormatInBytesMax - 1) + 3 + 1];
format_bytes(FormatBuf * outBuf,const uint8_t * inBuf,const int inBytes)492 static const char *format_bytes(FormatBuf *outBuf, const uint8_t *inBuf, const int inBytes) {
493   strlcpy(*outBuf, ": ", sizeof(*outBuf));
494   int pos = 2;
495   const int lim = std::min(kFormatInBytesMax, inBytes);
496   for (int i = 0; i < lim; ++i) {
497     if (i) {
498       sprintf(*outBuf + pos, (i % 4 ? "." : "|"));
499       ++pos;
500     }
501     sprintf(*outBuf + pos, "%02x", inBuf[i]);
502     pos += 2;
503   }
504   if (kFormatInBytesMax < inBytes)
505     strlcpy(*outBuf + pos, "...", sizeof(FormatBuf) - pos);
506   return *outBuf;
507 }
508 
reduce_get_accumulator(uint8_t * & accumPtr,MTLaunchStructReduce * mtls,const char * walkerName,uint32_t threadIdx)509 static void reduce_get_accumulator(uint8_t *&accumPtr, MTLaunchStructReduce *mtls,
510                                    const char *walkerName, uint32_t threadIdx) {
511   rsAssert(!accumPtr);
512 
513   uint32_t accumIdx = (uint32_t)__sync_fetch_and_add(&mtls->accumCount, 1);
514   if (mtls->outFunc) {
515     accumPtr = mtls->accumAlloc + mtls->accumStride * accumIdx;
516   } else {
517     if (accumIdx == 0) {
518       accumPtr = mtls->redp.outPtr[0];
519     } else {
520       accumPtr = mtls->accumAlloc + mtls->accumStride * (accumIdx - 1);
521     }
522   }
523   REDUCE_ALOGV(mtls, 2, "%s(%p): idx = %u got accumCount %u and accumPtr %p",
524                walkerName, mtls->accumFunc, threadIdx, accumIdx, accumPtr);
525   // initialize accumulator
526   if (mtls->initFunc) {
527     mtls->initFunc(accumPtr);
528   } else {
529     memset(accumPtr, 0, mtls->accumSize);
530   }
531 }
532 
walk_1d_reduce(void * usr,uint32_t idx)533 static void walk_1d_reduce(void *usr, uint32_t idx) {
534   MTLaunchStructReduce *mtls = (MTLaunchStructReduce *)usr;
535   RsExpandKernelDriverInfo redp = mtls->redp;
536 
537   // find accumulator
538   uint8_t *&accumPtr = mtls->accumPtr[idx];
539   if (!accumPtr) {
540     reduce_get_accumulator(accumPtr, mtls, __func__, idx);
541   }
542 
543   // accumulate
544   const ReduceAccumulatorFunc_t fn = mtls->accumFunc;
545   while (1) {
546     uint32_t slice  = (uint32_t)__sync_fetch_and_add(&mtls->mSliceNum, 1);
547     uint32_t xStart = mtls->start.x + slice * mtls->mSliceSize;
548     uint32_t xEnd   = xStart + mtls->mSliceSize;
549 
550     xEnd = rsMin(xEnd, mtls->end.x);
551 
552     if (xEnd <= xStart) {
553       return;
554     }
555 
556     RedpPtrSetup(mtls, &redp, xStart, 0, 0);
557     fn(&redp, xStart, xEnd, accumPtr);
558 
559     // Emit log line after slice has been run, so that we can include
560     // the results of the run on that line.
561     FormatBuf fmt;
562     if (mtls->logReduce >= 3) {
563       format_bytes(&fmt, accumPtr, mtls->accumSize);
564     } else {
565       fmt[0] = 0;
566     }
567     REDUCE_ALOGV(mtls, 2, "walk_1d_reduce(%p): idx = %u, x in [%u, %u)%s",
568                  mtls->accumFunc, idx, xStart, xEnd, fmt);
569   }
570 }
571 
walk_2d_reduce(void * usr,uint32_t idx)572 static void walk_2d_reduce(void *usr, uint32_t idx) {
573   MTLaunchStructReduce *mtls = (MTLaunchStructReduce *)usr;
574   RsExpandKernelDriverInfo redp = mtls->redp;
575 
576   // find accumulator
577   uint8_t *&accumPtr = mtls->accumPtr[idx];
578   if (!accumPtr) {
579     reduce_get_accumulator(accumPtr, mtls, __func__, idx);
580   }
581 
582   // accumulate
583   const ReduceAccumulatorFunc_t fn = mtls->accumFunc;
584   while (1) {
585     uint32_t slice  = (uint32_t)__sync_fetch_and_add(&mtls->mSliceNum, 1);
586     uint32_t yStart = mtls->start.y + slice * mtls->mSliceSize;
587     uint32_t yEnd   = yStart + mtls->mSliceSize;
588 
589     yEnd = rsMin(yEnd, mtls->end.y);
590 
591     if (yEnd <= yStart) {
592       return;
593     }
594 
595     for (redp.current.y = yStart; redp.current.y < yEnd; redp.current.y++) {
596       RedpPtrSetup(mtls, &redp, mtls->start.x, redp.current.y, 0);
597       fn(&redp, mtls->start.x, mtls->end.x, accumPtr);
598     }
599 
600     FormatBuf fmt;
601     if (mtls->logReduce >= 3) {
602       format_bytes(&fmt, accumPtr, mtls->accumSize);
603     } else {
604       fmt[0] = 0;
605     }
606     REDUCE_ALOGV(mtls, 2, "walk_2d_reduce(%p): idx = %u, y in [%u, %u)%s",
607                  mtls->accumFunc, idx, yStart, yEnd, fmt);
608   }
609 }
610 
walk_3d_reduce(void * usr,uint32_t idx)611 static void walk_3d_reduce(void *usr, uint32_t idx) {
612   MTLaunchStructReduce *mtls = (MTLaunchStructReduce *)usr;
613   RsExpandKernelDriverInfo redp = mtls->redp;
614 
615   // find accumulator
616   uint8_t *&accumPtr = mtls->accumPtr[idx];
617   if (!accumPtr) {
618     reduce_get_accumulator(accumPtr, mtls, __func__, idx);
619   }
620 
621   // accumulate
622   const ReduceAccumulatorFunc_t fn = mtls->accumFunc;
623   while (1) {
624     uint32_t slice  = (uint32_t)__sync_fetch_and_add(&mtls->mSliceNum, 1);
625 
626     if (!SelectZSlice(mtls, &redp, slice)) {
627       return;
628     }
629 
630     for (redp.current.y = mtls->start.y; redp.current.y < mtls->end.y; redp.current.y++) {
631       RedpPtrSetup(mtls, &redp, mtls->start.x, redp.current.y, redp.current.z);
632       fn(&redp, mtls->start.x, mtls->end.x, accumPtr);
633     }
634 
635     FormatBuf fmt;
636     if (mtls->logReduce >= 3) {
637       format_bytes(&fmt, accumPtr, mtls->accumSize);
638     } else {
639       fmt[0] = 0;
640     }
641     REDUCE_ALOGV(mtls, 2, "walk_3d_reduce(%p): idx = %u, z = %u%s",
642                  mtls->accumFunc, idx, redp.current.z, fmt);
643   }
644 }
645 
646 // Launch a general reduce-style kernel.
647 // Inputs:
648 //   ains[0..inLen-1]: Array of allocations that contain the inputs
649 //   aout:             The allocation that will hold the output
650 //   mtls:             Holds launch parameters
launchReduce(const Allocation ** ains,uint32_t inLen,Allocation * aout,MTLaunchStructReduce * mtls)651 void RsdCpuReferenceImpl::launchReduce(const Allocation ** ains,
652                                        uint32_t inLen,
653                                        Allocation * aout,
654                                        MTLaunchStructReduce *mtls) {
655   mtls->logReduce = mRSC->props.mLogReduce;
656   if ((mWorkers.mCount >= 1) && mtls->isThreadable && !mInKernel) {
657     launchReduceParallel(ains, inLen, aout, mtls);
658   } else {
659     launchReduceSerial(ains, inLen, aout, mtls);
660   }
661 }
662 
663 // Launch a general reduce-style kernel, single-threaded.
664 // Inputs:
665 //   ains[0..inLen-1]: Array of allocations that contain the inputs
666 //   aout:             The allocation that will hold the output
667 //   mtls:             Holds launch parameters
launchReduceSerial(const Allocation ** ains,uint32_t inLen,Allocation * aout,MTLaunchStructReduce * mtls)668 void RsdCpuReferenceImpl::launchReduceSerial(const Allocation ** ains,
669                                              uint32_t inLen,
670                                              Allocation * aout,
671                                              MTLaunchStructReduce *mtls) {
672   REDUCE_ALOGV(mtls, 1, "launchReduceSerial(%p): %u x %u x %u", mtls->accumFunc,
673                mtls->redp.dim.x, mtls->redp.dim.y, mtls->redp.dim.z);
674 
675   // In the presence of outconverter, we allocate temporary memory for
676   // the accumulator.
677   //
678   // In the absence of outconverter, we use the output allocation as the
679   // accumulator.
680   uint8_t *const accumPtr = (mtls->outFunc
681                              ? static_cast<uint8_t *>(malloc(mtls->accumSize))
682                              : mtls->redp.outPtr[0]);
683 
684   // initialize
685   if (mtls->initFunc) {
686     mtls->initFunc(accumPtr);
687   } else {
688     memset(accumPtr, 0, mtls->accumSize);
689   }
690 
691   // accumulate
692   const ReduceAccumulatorFunc_t fn = mtls->accumFunc;
693   uint32_t slice = 0;
694   while (SelectOuterSlice(mtls, &mtls->redp, slice++)) {
695     for (mtls->redp.current.y = mtls->start.y;
696          mtls->redp.current.y < mtls->end.y;
697          mtls->redp.current.y++) {
698       RedpPtrSetup(mtls, &mtls->redp, mtls->start.x, mtls->redp.current.y, mtls->redp.current.z);
699       fn(&mtls->redp, mtls->start.x, mtls->end.x, accumPtr);
700     }
701   }
702 
703   // outconvert
704   if (mtls->outFunc) {
705     mtls->outFunc(mtls->redp.outPtr[0], accumPtr);
706     free(accumPtr);
707   }
708 }
709 
710 // Launch a general reduce-style kernel, multi-threaded.
711 // Inputs:
712 //   ains[0..inLen-1]: Array of allocations that contain the inputs
713 //   aout:             The allocation that will hold the output
714 //   mtls:             Holds launch parameters
launchReduceParallel(const Allocation ** ains,uint32_t inLen,Allocation * aout,MTLaunchStructReduce * mtls)715 void RsdCpuReferenceImpl::launchReduceParallel(const Allocation ** ains,
716                                                uint32_t inLen,
717                                                Allocation * aout,
718                                                MTLaunchStructReduce *mtls) {
719   // For now, we don't know how to go parallel in the absence of a combiner.
720   if (!mtls->combFunc) {
721     launchReduceSerial(ains, inLen, aout, mtls);
722     return;
723   }
724 
725   // Number of threads = "main thread" + number of other (worker) threads
726   const uint32_t numThreads = mWorkers.mCount + 1;
727 
728   // In the absence of outconverter, we use the output allocation as
729   // an accumulator, and therefore need to allocate one fewer accumulator.
730   const uint32_t numAllocAccum = numThreads - (mtls->outFunc == nullptr);
731 
732   // If mDebugReduceSplitAccum, then we want each accumulator to start
733   // on a page boundary.  (TODO: Would some unit smaller than a page
734   // be sufficient to avoid false sharing?)
735   if (mRSC->props.mDebugReduceSplitAccum) {
736     // Round up accumulator size to an integral number of pages
737     mtls->accumStride =
738         (unsigned(mtls->accumSize) + unsigned(mPageSize)-1) &
739         ~(unsigned(mPageSize)-1);
740     // Each accumulator gets its own page.  Alternatively, if we just
741     // wanted to make sure no two accumulators are on the same page,
742     // we could instead do
743     //   allocSize = mtls->accumStride * (numAllocation - 1) + mtls->accumSize
744     const size_t allocSize = mtls->accumStride * numAllocAccum;
745     mtls->accumAlloc = static_cast<uint8_t *>(memalign(mPageSize, allocSize));
746   } else {
747     mtls->accumStride = mtls->accumSize;
748     mtls->accumAlloc = static_cast<uint8_t *>(malloc(mtls->accumStride * numAllocAccum));
749   }
750 
751   const size_t accumPtrArrayBytes = sizeof(uint8_t *) * numThreads;
752   mtls->accumPtr = static_cast<uint8_t **>(malloc(accumPtrArrayBytes));
753   memset(mtls->accumPtr, 0, accumPtrArrayBytes);
754 
755   mtls->accumCount = 0;
756 
757   rsAssert(!mInKernel);
758   mInKernel = true;
759   REDUCE_ALOGV(mtls, 1, "launchReduceParallel(%p): %u x %u x %u, %u threads, accumAlloc = %p",
760                mtls->accumFunc,
761                mtls->redp.dim.x, mtls->redp.dim.y, mtls->redp.dim.z,
762                numThreads, mtls->accumAlloc);
763   if (mtls->redp.dim.z > 1) {
764     mtls->mSliceSize = 1;
765     launchThreads(walk_3d_reduce, mtls);
766   } else if (mtls->redp.dim.y > 1) {
767     mtls->mSliceSize = rsMax(1U, mtls->redp.dim.y / (numThreads * 4));
768     launchThreads(walk_2d_reduce, mtls);
769   } else {
770     mtls->mSliceSize = rsMax(1U, mtls->redp.dim.x / (numThreads * 4));
771     launchThreads(walk_1d_reduce, mtls);
772   }
773   mInKernel = false;
774 
775   // Combine accumulators and identify final accumulator
776   uint8_t *finalAccumPtr = (mtls->outFunc ? nullptr : mtls->redp.outPtr[0]);
777   //   Loop over accumulators, combining into finalAccumPtr.  If finalAccumPtr
778   //   is null, then the first accumulator I find becomes finalAccumPtr.
779   for (unsigned idx = 0; idx < mtls->accumCount; ++idx) {
780     uint8_t *const thisAccumPtr = mtls->accumPtr[idx];
781     if (finalAccumPtr) {
782       if (finalAccumPtr != thisAccumPtr) {
783         if (mtls->combFunc) {
784           if (mtls->logReduce >= 3) {
785             FormatBuf fmt;
786             REDUCE_ALOGV(mtls, 3, "launchReduceParallel(%p): accumulating into%s",
787                          mtls->accumFunc,
788                          format_bytes(&fmt, finalAccumPtr, mtls->accumSize));
789             REDUCE_ALOGV(mtls, 3, "launchReduceParallel(%p):    accumulator[%d]%s",
790                          mtls->accumFunc, idx,
791                          format_bytes(&fmt, thisAccumPtr, mtls->accumSize));
792           }
793           mtls->combFunc(finalAccumPtr, thisAccumPtr);
794         } else {
795           rsAssert(!"expected combiner");
796         }
797       }
798     } else {
799       finalAccumPtr = thisAccumPtr;
800     }
801   }
802   rsAssert(finalAccumPtr != nullptr);
803   if (mtls->logReduce >= 3) {
804     FormatBuf fmt;
805     REDUCE_ALOGV(mtls, 3, "launchReduceParallel(%p): final accumulator%s",
806                  mtls->accumFunc, format_bytes(&fmt, finalAccumPtr, mtls->accumSize));
807   }
808 
809   // Outconvert
810   if (mtls->outFunc) {
811     mtls->outFunc(mtls->redp.outPtr[0], finalAccumPtr);
812     if (mtls->logReduce >= 3) {
813       FormatBuf fmt;
814       REDUCE_ALOGV(mtls, 3, "launchReduceParallel(%p): final outconverted result%s",
815                    mtls->accumFunc,
816                    format_bytes(&fmt, mtls->redp.outPtr[0], mtls->redp.outStride[0]));
817     }
818   }
819 
820   // Clean up
821   free(mtls->accumPtr);
822   free(mtls->accumAlloc);
823 }
824 
825 
launchForEach(const Allocation ** ains,uint32_t inLen,Allocation * aout,const RsScriptCall * sc,MTLaunchStructForEach * mtls)826 void RsdCpuReferenceImpl::launchForEach(const Allocation ** ains,
827                                         uint32_t inLen,
828                                         Allocation* aout,
829                                         const RsScriptCall* sc,
830                                         MTLaunchStructForEach* mtls) {
831 
832     //android::StopWatch kernel_time("kernel time");
833 
834     bool outerDims = (mtls->start.z != mtls->end.z) ||
835                      (mtls->start.face != mtls->end.face) ||
836                      (mtls->start.lod != mtls->end.lod) ||
837                      (mtls->start.array[0] != mtls->end.array[0]) ||
838                      (mtls->start.array[1] != mtls->end.array[1]) ||
839                      (mtls->start.array[2] != mtls->end.array[2]) ||
840                      (mtls->start.array[3] != mtls->end.array[3]);
841 
842     if ((mWorkers.mCount >= 1) && mtls->isThreadable && !mInKernel) {
843         const size_t targetByteChunk = 16 * 1024;
844         mInKernel = true;  // NOTE: The guard immediately above ensures this was !mInKernel
845 
846         if (outerDims) {
847             // No fancy logic for chunk size
848             mtls->mSliceSize = 1;
849             launchThreads(walk_general_foreach, mtls);
850         } else if (mtls->fep.dim.y > 1) {
851             uint32_t s1 = mtls->fep.dim.y / ((mWorkers.mCount + 1) * 4);
852             uint32_t s2 = 0;
853 
854             // This chooses our slice size to rate limit atomic ops to
855             // one per 16k bytes of reads/writes.
856             if ((mtls->aout[0] != nullptr) && mtls->aout[0]->mHal.drvState.lod[0].stride) {
857                 s2 = targetByteChunk / mtls->aout[0]->mHal.drvState.lod[0].stride;
858             } else if (mtls->ains[0]) {
859                 s2 = targetByteChunk / mtls->ains[0]->mHal.drvState.lod[0].stride;
860             } else {
861                 // Launch option only case
862                 // Use s1 based only on the dimensions
863                 s2 = s1;
864             }
865             mtls->mSliceSize = rsMin(s1, s2);
866 
867             if(mtls->mSliceSize < 1) {
868                 mtls->mSliceSize = 1;
869             }
870 
871             launchThreads(walk_2d_foreach, mtls);
872         } else {
873             uint32_t s1 = mtls->fep.dim.x / ((mWorkers.mCount + 1) * 4);
874             uint32_t s2 = 0;
875 
876             // This chooses our slice size to rate limit atomic ops to
877             // one per 16k bytes of reads/writes.
878             if ((mtls->aout[0] != nullptr) && mtls->aout[0]->getType()->getElementSizeBytes()) {
879                 s2 = targetByteChunk / mtls->aout[0]->getType()->getElementSizeBytes();
880             } else if (mtls->ains[0]) {
881                 s2 = targetByteChunk / mtls->ains[0]->getType()->getElementSizeBytes();
882             } else {
883                 // Launch option only case
884                 // Use s1 based only on the dimensions
885                 s2 = s1;
886             }
887             mtls->mSliceSize = rsMin(s1, s2);
888 
889             if (mtls->mSliceSize < 1) {
890                 mtls->mSliceSize = 1;
891             }
892 
893             launchThreads(walk_1d_foreach, mtls);
894         }
895         mInKernel = false;
896 
897     } else {
898         ForEachFunc_t fn = mtls->kernel;
899         uint32_t slice = 0;
900 
901 
902         while(SelectOuterSlice(mtls, &mtls->fep, slice++)) {
903             for (mtls->fep.current.y = mtls->start.y;
904                  mtls->fep.current.y < mtls->end.y;
905                  mtls->fep.current.y++) {
906 
907                 FepPtrSetup(mtls, &mtls->fep, mtls->start.x,
908                             mtls->fep.current.y, mtls->fep.current.z, mtls->fep.current.lod,
909                             (RsAllocationCubemapFace) mtls->fep.current.face,
910                             mtls->fep.current.array[0], mtls->fep.current.array[1],
911                             mtls->fep.current.array[2], mtls->fep.current.array[3]);
912 
913                 fn(&mtls->fep, mtls->start.x, mtls->end.x, mtls->fep.outStride[0]);
914             }
915         }
916     }
917 }
918 
setTLS(RsdCpuScriptImpl * sc)919 RsdCpuScriptImpl * RsdCpuReferenceImpl::setTLS(RsdCpuScriptImpl *sc) {
920     //ALOGE("setTls %p", sc);
921     ScriptTLSStruct * tls = (ScriptTLSStruct *)pthread_getspecific(gThreadTLSKey);
922     rsAssert(tls);
923     RsdCpuScriptImpl *old = tls->mImpl;
924     tls->mImpl = sc;
925     tls->mContext = mRSC;
926     if (sc) {
927         tls->mScript = sc->getScript();
928     } else {
929         tls->mScript = nullptr;
930     }
931     return old;
932 }
933 
symLookup(const char * name)934 const RsdCpuReference::CpuSymbol * RsdCpuReferenceImpl::symLookup(const char *name) {
935     return mSymLookupFn(mRSC, name);
936 }
937 
938 
createScript(const ScriptC * s,char const * resName,char const * cacheDir,uint8_t const * bitcode,size_t bitcodeSize,uint32_t flags)939 RsdCpuReference::CpuScript * RsdCpuReferenceImpl::createScript(const ScriptC *s,
940                                     char const *resName, char const *cacheDir,
941                                     uint8_t const *bitcode, size_t bitcodeSize,
942                                     uint32_t flags) {
943 
944     RsdCpuScriptImpl *i = new RsdCpuScriptImpl(this, s);
945     if (!i->init(resName, cacheDir, bitcode, bitcodeSize, flags
946         , getBccPluginName()
947         )) {
948         delete i;
949         return nullptr;
950     }
951     return i;
952 }
953 
954 extern RsdCpuScriptImpl * rsdIntrinsic_3DLUT(RsdCpuReferenceImpl *ctx,
955                                              const Script *s, const Element *e);
956 extern RsdCpuScriptImpl * rsdIntrinsic_Convolve3x3(RsdCpuReferenceImpl *ctx,
957                                                    const Script *s, const Element *e);
958 extern RsdCpuScriptImpl * rsdIntrinsic_ColorMatrix(RsdCpuReferenceImpl *ctx,
959                                                    const Script *s, const Element *e);
960 extern RsdCpuScriptImpl * rsdIntrinsic_LUT(RsdCpuReferenceImpl *ctx,
961                                            const Script *s, const Element *e);
962 extern RsdCpuScriptImpl * rsdIntrinsic_Convolve5x5(RsdCpuReferenceImpl *ctx,
963                                                    const Script *s, const Element *e);
964 extern RsdCpuScriptImpl * rsdIntrinsic_Blur(RsdCpuReferenceImpl *ctx,
965                                             const Script *s, const Element *e);
966 extern RsdCpuScriptImpl * rsdIntrinsic_YuvToRGB(RsdCpuReferenceImpl *ctx,
967                                                 const Script *s, const Element *e);
968 extern RsdCpuScriptImpl * rsdIntrinsic_Blend(RsdCpuReferenceImpl *ctx,
969                                              const Script *s, const Element *e);
970 extern RsdCpuScriptImpl * rsdIntrinsic_Histogram(RsdCpuReferenceImpl *ctx,
971                                                  const Script *s, const Element *e);
972 extern RsdCpuScriptImpl * rsdIntrinsic_Resize(RsdCpuReferenceImpl *ctx,
973                                               const Script *s, const Element *e);
974 extern RsdCpuScriptImpl * rsdIntrinsic_BLAS(RsdCpuReferenceImpl *ctx,
975                                               const Script *s, const Element *e);
976 
createIntrinsic(const Script * s,RsScriptIntrinsicID iid,Element * e)977 RsdCpuReference::CpuScript * RsdCpuReferenceImpl::createIntrinsic(const Script *s,
978                                     RsScriptIntrinsicID iid, Element *e) {
979 
980     RsdCpuScriptImpl *i = nullptr;
981     switch (iid) {
982     case RS_SCRIPT_INTRINSIC_ID_3DLUT:
983         i = rsdIntrinsic_3DLUT(this, s, e);
984         break;
985     case RS_SCRIPT_INTRINSIC_ID_CONVOLVE_3x3:
986         i = rsdIntrinsic_Convolve3x3(this, s, e);
987         break;
988     case RS_SCRIPT_INTRINSIC_ID_COLOR_MATRIX:
989         i = rsdIntrinsic_ColorMatrix(this, s, e);
990         break;
991     case RS_SCRIPT_INTRINSIC_ID_LUT:
992         i = rsdIntrinsic_LUT(this, s, e);
993         break;
994     case RS_SCRIPT_INTRINSIC_ID_CONVOLVE_5x5:
995         i = rsdIntrinsic_Convolve5x5(this, s, e);
996         break;
997     case RS_SCRIPT_INTRINSIC_ID_BLUR:
998         i = rsdIntrinsic_Blur(this, s, e);
999         break;
1000     case RS_SCRIPT_INTRINSIC_ID_YUV_TO_RGB:
1001         i = rsdIntrinsic_YuvToRGB(this, s, e);
1002         break;
1003     case RS_SCRIPT_INTRINSIC_ID_BLEND:
1004         i = rsdIntrinsic_Blend(this, s, e);
1005         break;
1006     case RS_SCRIPT_INTRINSIC_ID_HISTOGRAM:
1007         i = rsdIntrinsic_Histogram(this, s, e);
1008         break;
1009     case RS_SCRIPT_INTRINSIC_ID_RESIZE:
1010         i = rsdIntrinsic_Resize(this, s, e);
1011         break;
1012     case RS_SCRIPT_INTRINSIC_ID_BLAS:
1013         i = rsdIntrinsic_BLAS(this, s, e);
1014         break;
1015 
1016     default:
1017         rsAssert(0);
1018     }
1019 
1020     return i;
1021 }
1022 
createScriptGroup(const ScriptGroupBase * sg)1023 void* RsdCpuReferenceImpl::createScriptGroup(const ScriptGroupBase *sg) {
1024   switch (sg->getApiVersion()) {
1025     case ScriptGroupBase::SG_V1: {
1026       CpuScriptGroupImpl *sgi = new CpuScriptGroupImpl(this, sg);
1027       if (!sgi->init()) {
1028         delete sgi;
1029         return nullptr;
1030       }
1031       return sgi;
1032     }
1033     case ScriptGroupBase::SG_V2: {
1034       return new CpuScriptGroup2Impl(this, sg);
1035     }
1036   }
1037   return nullptr;
1038 }
1039 
1040 } // namespace renderscript
1041 } // namespace android
1042