Home | History | Annotate | Download | only in rs
      1 /*
      2  * Copyright (C) 2009-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 "rsContext.h"
     18 #include "rsScriptC.h"
     19 
     20 #if !defined(RS_COMPATIBILITY_LIB) && !defined(ANDROID_RS_SERIALIZE)
     21 #include <bcinfo/BitcodeTranslator.h>
     22 #include <bcinfo/BitcodeWrapper.h>
     23 #endif
     24 
     25 #if !defined(RS_SERVER) && !defined(RS_COMPATIBILITY_LIB)
     26 #include "utils/Timers.h"
     27 #include "cutils/trace.h"
     28 #endif
     29 
     30 #include <sys/stat.h>
     31 
     32 #include <sstream>
     33 #include <string>
     34 
     35 #ifdef USE_MINGW
     36 /* Define the default path separator for the platform. */
     37 #define OS_PATH_SEPARATOR     '\\'
     38 #define OS_PATH_SEPARATOR_STR "\\"
     39 
     40 #else /* not USE_MINGW */
     41 
     42 /* Define the default path separator for the platform. */
     43 #define OS_PATH_SEPARATOR     '/'
     44 #define OS_PATH_SEPARATOR_STR "/"
     45 
     46 #endif
     47 
     48 using namespace android;
     49 using namespace android::renderscript;
     50 
     51 #define GET_TLS()  Context::ScriptTLSStruct * tls = \
     52     (Context::ScriptTLSStruct *)pthread_getspecific(Context::gThreadTLSKey); \
     53     Context * rsc = tls->mContext; \
     54     ScriptC * sc = (ScriptC *) tls->mScript
     55 
     56 ScriptC::ScriptC(Context *rsc) : Script(rsc) {
     57 }
     58 
     59 ScriptC::~ScriptC() {
     60     if (mInitialized) {
     61         mRSC->mHal.funcs.script.invokeFreeChildren(mRSC, this);
     62         mRSC->mHal.funcs.script.destroy(mRSC, this);
     63     }
     64 }
     65 
     66 #ifndef RS_COMPATIBILITY_LIB
     67 bool ScriptC::createCacheDir(const char *cacheDir) {
     68     std::string currentDir;
     69     const std::string cacheDirString(cacheDir);
     70 
     71     struct stat statBuf;
     72     int statReturn = stat(cacheDir, &statBuf);
     73     if (!statReturn) {
     74         return true;
     75     }
     76 
     77     // Start from the beginning of the cacheDirString.
     78     int currPos = 0;
     79 
     80     // Reserve space in currentDir for the entire cacheDir path.
     81     currentDir.reserve(cacheDirString.length());
     82 
     83     while (currPos >= 0) {
     84         /*
     85          * The character at currPos should be a path separator.  We need to look
     86          * for the next one.
     87          */
     88         int nextPos = cacheDirString.find(OS_PATH_SEPARATOR_STR, currPos + 1);
     89 
     90         if (nextPos > 0) {
     91             // A new path separator has been found.
     92             currentDir += cacheDirString.substr(currPos, nextPos - currPos);
     93         } else {
     94             // There are no more path separators.
     95             currentDir += cacheDirString.substr(currPos);
     96         }
     97 
     98         currPos = nextPos;
     99 
    100         statReturn = stat(currentDir.c_str(), &statBuf);
    101 
    102         if (statReturn) {
    103             if (errno == ENOENT) {
    104                 if (mkdir(currentDir.c_str(), S_IRUSR | S_IWUSR | S_IXUSR)) {
    105                     ALOGE("Couldn't create cache directory: %s",
    106                           currentDir.c_str());
    107                     ALOGE("Error: %s", strerror(errno));
    108                     return false;
    109                 }
    110             } else {
    111                 ALOGE("Stat error: %s", strerror(errno));
    112                 return false;
    113             }
    114         }
    115     }
    116     return true;
    117 }
    118 #endif
    119 
    120 void ScriptC::setupScript(Context *rsc) {
    121 #ifndef RS_SERVER
    122     mEnviroment.mStartTimeMillis
    123                 = nanoseconds_to_milliseconds(systemTime(SYSTEM_TIME_MONOTONIC));
    124 #endif
    125 
    126     for (uint32_t ct=0; ct < mHal.info.exportedVariableCount; ct++) {
    127         if (mSlots[ct].get() && !mTypes[ct].get()) {
    128             mTypes[ct].set(mSlots[ct]->getType());
    129         }
    130 
    131         if (!mTypes[ct].get())
    132             continue;
    133         rsc->mHal.funcs.script.setGlobalBind(rsc, this, ct, mSlots[ct].get());
    134     }
    135 }
    136 
    137 void ScriptC::setupGLState(Context *rsc) {
    138 #ifndef RS_COMPATIBILITY_LIB
    139     if (mEnviroment.mFragmentStore.get()) {
    140         rsc->setProgramStore(mEnviroment.mFragmentStore.get());
    141     }
    142     if (mEnviroment.mFragment.get()) {
    143         rsc->setProgramFragment(mEnviroment.mFragment.get());
    144     }
    145     if (mEnviroment.mVertex.get()) {
    146         rsc->setProgramVertex(mEnviroment.mVertex.get());
    147     }
    148     if (mEnviroment.mRaster.get()) {
    149         rsc->setProgramRaster(mEnviroment.mRaster.get());
    150     }
    151 #endif
    152 }
    153 
    154 uint32_t ScriptC::run(Context *rsc) {
    155     if (mHal.info.root == nullptr) {
    156         rsc->setError(RS_ERROR_BAD_SCRIPT, "Attempted to run bad script");
    157         return 0;
    158     }
    159 
    160     setupGLState(rsc);
    161     setupScript(rsc);
    162 
    163     uint32_t ret = 0;
    164 
    165     if (rsc->props.mLogScripts) {
    166         ALOGV("%p ScriptC::run invoking root,  ptr %p", rsc, mHal.info.root);
    167     }
    168 
    169     ret = rsc->mHal.funcs.script.invokeRoot(rsc, this);
    170 
    171     if (rsc->props.mLogScripts) {
    172         ALOGV("%p ScriptC::run invoking complete, ret=%i", rsc, ret);
    173     }
    174 
    175     return ret;
    176 }
    177 
    178 
    179 void ScriptC::runForEach(Context *rsc,
    180                          uint32_t slot,
    181                          const Allocation ** ains,
    182                          size_t inLen,
    183                          Allocation * aout,
    184                          const void * usr,
    185                          size_t usrBytes,
    186                          const RsScriptCall *sc) {
    187     // Make a copy of RsScriptCall and zero out extra fields that are absent
    188     // in API levels below 23.
    189     RsScriptCall sc_copy;
    190     if (sc != nullptr && getApiLevel() < 23) {
    191         memset(&sc_copy, 0, sizeof(sc_copy));
    192         memcpy(&sc_copy, sc, 7*4);
    193         sc = &sc_copy;
    194     }
    195 
    196     if (slot >= mHal.info.exportedForEachCount) {
    197         rsc->setError(RS_ERROR_BAD_SCRIPT,
    198                       "The forEach kernel index is out of bounds");
    199         return;
    200     }
    201 
    202     // Trace this function call.
    203     // To avoid overhead we only build the string if tracing is actually
    204     // enabled.
    205     std::stringstream ss;
    206     if (ATRACE_ENABLED()) {
    207         ss << "runForEach slot[" << slot << "]";
    208     }
    209     ATRACE_NAME(ss.str().c_str());
    210 
    211     if (mRSC->hadFatalError()) return;
    212 
    213     Context::PushState ps(rsc);
    214 
    215     setupGLState(rsc);
    216     setupScript(rsc);
    217 
    218     if (rsc->props.mLogScripts) {
    219         ALOGV("%p ScriptC::runForEach invoking slot %i, ptr %p", rsc, slot, this);
    220     }
    221 
    222     if (rsc->mHal.funcs.script.invokeForEachMulti != nullptr) {
    223         rsc->mHal.funcs.script.invokeForEachMulti(rsc, this, slot, ains, inLen,
    224                                                   aout, usr, usrBytes, sc);
    225 
    226     } else if (inLen == 1) {
    227         rsc->mHal.funcs.script.invokeForEach(rsc, this, slot, ains[0], aout,
    228                                              usr, usrBytes, sc);
    229 
    230     } else {
    231         rsc->setError(RS_ERROR_FATAL_DRIVER,
    232                       "Driver support for multi-input not present");
    233     }
    234 }
    235 
    236 void ScriptC::runReduce(Context *rsc, uint32_t slot,
    237                         const Allocation ** ains, size_t inLen,
    238                         Allocation *aout, const RsScriptCall *sc) {
    239   // TODO: Record the name of the kernel in the tracing information.
    240   ATRACE_CALL();
    241 
    242   if (slot >= mHal.info.exportedReduceCount) {
    243       rsc->setError(RS_ERROR_BAD_SCRIPT, "The general reduce kernel index is out of bounds");
    244       return;
    245   }
    246   if (mRSC->hadFatalError()) return;
    247 
    248   setupScript(rsc);
    249 
    250   if (rsc->props.mLogScripts) {
    251       ALOGV("%p ScriptC::runReduce invoking slot %i, ptr %p", rsc, slot, this);
    252   }
    253 
    254   rsc->mHal.funcs.script.invokeReduce(rsc, this, slot, ains, inLen, aout, sc);
    255 }
    256 
    257 void ScriptC::Invoke(Context *rsc, uint32_t slot, const void *data, size_t len) {
    258     ATRACE_CALL();
    259 
    260     if (slot >= mHal.info.exportedFunctionCount) {
    261         rsc->setError(RS_ERROR_BAD_SCRIPT, "The invokable index is out of bounds");
    262         return;
    263     }
    264     if (mRSC->hadFatalError()) return;
    265 
    266     setupScript(rsc);
    267 
    268     if (rsc->props.mLogScripts) {
    269         ALOGV("%p ScriptC::Invoke invoking slot %i,  ptr %p", rsc, slot, this);
    270     }
    271     rsc->mHal.funcs.script.invokeFunction(rsc, this, slot, data, len);
    272 }
    273 
    274 static const bool kDebugBitcode = false;
    275 
    276 #ifndef RS_COMPATIBILITY_LIB
    277 #ifndef ANDROID_RS_SERIALIZE
    278 
    279 static bool dumpBitcodeFile(const char *cacheDir, const char *resName,
    280                             const char *suffix, const uint8_t *bitcode,
    281                             size_t bitcodeLen) {
    282     std::string f(cacheDir);
    283     f.append("/");
    284     f.append(resName);
    285     f.append("#");
    286     f.append(suffix);
    287     f.append(".bc");
    288 
    289     if (!ScriptC::createCacheDir(cacheDir)) {
    290         return false;
    291     }
    292 
    293     FILE *fp = fopen(f.c_str(), "w");
    294     if (!fp) {
    295         ALOGE("Could not open %s", f.c_str());
    296         return false;
    297     }
    298 
    299     size_t nWritten = fwrite(bitcode, 1, bitcodeLen, fp);
    300     fclose(fp);
    301     if (nWritten != bitcodeLen) {
    302         ALOGE("Could not write %s", f.c_str());
    303         return false;
    304     }
    305     return true;
    306 }
    307 
    308 #endif  // !ANDROID_RS_SERIALIZE
    309 #endif  // !RS_COMPATIBILITY_LIB
    310 
    311 
    312 bool ScriptC::runCompiler(Context *rsc,
    313                           const char *resName,
    314                           const char *cacheDir,
    315                           const uint8_t *bitcode,
    316                           size_t bitcodeLen) {
    317     ATRACE_CALL();
    318     //ALOGE("runCompiler %p %p %p %p %p %i", rsc, this, resName, cacheDir, bitcode, bitcodeLen);
    319 #ifndef RS_COMPATIBILITY_LIB
    320 #ifndef ANDROID_RS_SERIALIZE
    321     uint32_t sdkVersion = 0;
    322     bcinfo::BitcodeWrapper bcWrapper((const char *)bitcode, bitcodeLen);
    323     if (!bcWrapper.unwrap()) {
    324         ALOGE("Bitcode is not in proper container format (raw or wrapper)");
    325         return false;
    326     }
    327 
    328     if (bcWrapper.getBCFileType() == bcinfo::BC_WRAPPER) {
    329         sdkVersion = bcWrapper.getTargetAPI();
    330     }
    331 
    332     if (sdkVersion == 0) {
    333         // This signals that we didn't have a wrapper containing information
    334         // about the bitcode.
    335         sdkVersion = rsc->getTargetSdkVersion();
    336     }
    337 
    338     // Save off the sdkVersion, so that we can handle broken cases later.
    339     // Bug 19734267
    340     mApiLevel = sdkVersion;
    341 
    342     bcinfo::BitcodeTranslator BT((const char *)bitcode, bitcodeLen,
    343                                  sdkVersion);
    344     if (!BT.translate()) {
    345         ALOGE("Failed to translate bitcode from version: %u", sdkVersion);
    346         return false;
    347     }
    348     bitcode = (const uint8_t *) BT.getTranslatedBitcode();
    349     bitcodeLen = BT.getTranslatedBitcodeSize();
    350 
    351     if (kDebugBitcode) {
    352         if (!dumpBitcodeFile(cacheDir, resName, "after", bitcode, bitcodeLen)) {
    353             return false;
    354         }
    355     }
    356 
    357 
    358     // Set the optimization level of bcc to be the same as the
    359     // optimization level used to compile the bitcode.
    360     rsc->setOptLevel(bcWrapper.getOptimizationLevel());
    361 
    362 #endif
    363     if (!cacheDir) {
    364         // MUST BE FIXED BEFORE ANYTHING USING C++ API IS RELEASED
    365         cacheDir = getenv("EXTERNAL_STORAGE");
    366         ALOGV("Cache dir changed to %s", cacheDir);
    367     }
    368 
    369     // ensure that cache dir exists
    370     if (cacheDir && !createCacheDir(cacheDir)) {
    371       return false;
    372     }
    373 #endif
    374 
    375     if (!rsc->mHal.funcs.script.init(rsc, this, resName, cacheDir, bitcode, bitcodeLen, 0)) {
    376         return false;
    377     }
    378 
    379     mInitialized = true;
    380 #ifndef RS_COMPATIBILITY_LIB
    381     mEnviroment.mFragment.set(rsc->getDefaultProgramFragment());
    382     mEnviroment.mVertex.set(rsc->getDefaultProgramVertex());
    383     mEnviroment.mFragmentStore.set(rsc->getDefaultProgramStore());
    384     mEnviroment.mRaster.set(rsc->getDefaultProgramRaster());
    385 #endif
    386 
    387     rsc->mHal.funcs.script.invokeInit(rsc, this);
    388 
    389     for (size_t i=0; i < mHal.info.exportedPragmaCount; ++i) {
    390         const char * key = mHal.info.exportedPragmaKeyList[i];
    391         const char * value = mHal.info.exportedPragmaValueList[i];
    392         //ALOGE("pragma %s %s", keys[i], values[i]);
    393         if (!strcmp(key, "version")) {
    394             if (!strcmp(value, "1")) {
    395                 continue;
    396             }
    397             ALOGE("Invalid version pragma value: %s\n", value);
    398             return false;
    399         }
    400 
    401 #ifndef RS_COMPATIBILITY_LIB
    402         if (!strcmp(key, "stateVertex")) {
    403             if (!strcmp(value, "default")) {
    404                 continue;
    405             }
    406             if (!strcmp(value, "parent")) {
    407                 mEnviroment.mVertex.clear();
    408                 continue;
    409             }
    410             ALOGE("Unrecognized value %s passed to stateVertex", value);
    411             return false;
    412         }
    413 
    414         if (!strcmp(key, "stateRaster")) {
    415             if (!strcmp(value, "default")) {
    416                 continue;
    417             }
    418             if (!strcmp(value, "parent")) {
    419                 mEnviroment.mRaster.clear();
    420                 continue;
    421             }
    422             ALOGE("Unrecognized value %s passed to stateRaster", value);
    423             return false;
    424         }
    425 
    426         if (!strcmp(key, "stateFragment")) {
    427             if (!strcmp(value, "default")) {
    428                 continue;
    429             }
    430             if (!strcmp(value, "parent")) {
    431                 mEnviroment.mFragment.clear();
    432                 continue;
    433             }
    434             ALOGE("Unrecognized value %s passed to stateFragment", value);
    435             return false;
    436         }
    437 
    438         if (!strcmp(key, "stateStore")) {
    439             if (!strcmp(value, "default")) {
    440                 continue;
    441             }
    442             if (!strcmp(value, "parent")) {
    443                 mEnviroment.mFragmentStore.clear();
    444                 continue;
    445             }
    446             ALOGE("Unrecognized value %s passed to stateStore", value);
    447             return false;
    448         }
    449 #endif
    450 
    451     }
    452 
    453     mSlots = new ObjectBaseRef<Allocation>[mHal.info.exportedVariableCount];
    454     mTypes = new ObjectBaseRef<const Type>[mHal.info.exportedVariableCount];
    455 
    456     return true;
    457 }
    458 
    459 namespace android {
    460 namespace renderscript {
    461 
    462 RsScript rsi_ScriptCCreate(Context *rsc,
    463                            const char *resName, size_t resName_length,
    464                            const char *cacheDir, size_t cacheDir_length,
    465                            const char *text, size_t text_length)
    466 {
    467     ScriptC *s = new ScriptC(rsc);
    468 
    469     if (!s->runCompiler(rsc, resName, cacheDir, (uint8_t *)text, text_length)) {
    470         // Error during compile, destroy s and return null.
    471         ObjectBase::checkDelete(s);
    472         return nullptr;
    473     }
    474 
    475     s->incUserRef();
    476     return s;
    477 }
    478 
    479 }
    480 }
    481