1 /* 2 * Copyright (C) 2013 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 <malloc.h> 18 #include <string.h> 19 #include <pthread.h> 20 21 #include "RenderScript.h" 22 #include "rsCppStructs.h" 23 #include "rsCppInternal.h" 24 25 #include <dlfcn.h> 26 #include <unistd.h> 27 28 using android::RSC::RS; 29 using android::RSC::RSError; 30 31 bool RS::gInitialized = false; 32 bool RS::usingNative = false; 33 pthread_mutex_t RS::gInitMutex = PTHREAD_MUTEX_INITIALIZER; 34 dispatchTable* RS::dispatch = nullptr; 35 static int gInitError = 0; 36 37 RS::RS() { 38 mContext = nullptr; 39 mErrorFunc = nullptr; 40 mMessageFunc = nullptr; 41 mMessageRun = false; 42 mInit = false; 43 mCurrentError = RS_SUCCESS; 44 45 memset(&mElements, 0, sizeof(mElements)); 46 memset(&mSamplers, 0, sizeof(mSamplers)); 47 } 48 49 RS::~RS() { 50 if (mInit == true) { 51 mMessageRun = false; 52 53 if (mContext) { 54 finish(); 55 RS::dispatch->ContextDeinitToClient(mContext); 56 57 void *res = nullptr; 58 pthread_join(mMessageThreadId, &res); 59 60 RS::dispatch->ContextDestroy(mContext); 61 mContext = nullptr; 62 } 63 } 64 } 65 66 bool RS::init(const char * name, uint32_t flags) { 67 return RS::init(name, flags, 0); 68 } 69 70 // This will only open API 19+ libRS, because that's when 71 // we changed libRS to extern "C" entry points. 72 static bool loadSO(const char* filename, int targetApi) { 73 void* handle = dlopen(filename, RTLD_LAZY | RTLD_LOCAL); 74 if (handle == nullptr) { 75 ALOGV("couldn't dlopen %s, %s", filename, dlerror()); 76 return false; 77 } 78 79 if (loadSymbols(handle, *RS::dispatch, targetApi) == false) { 80 ALOGV("%s init failed!", filename); 81 return false; 82 } 83 return true; 84 } 85 86 static uint32_t getProp(const char *str) { 87 #if !defined(__LP64__) && defined(__ANDROID__) 88 char buf[256]; 89 android::renderscript::property_get(str, buf, "0"); 90 return atoi(buf); 91 #else 92 return 0; 93 #endif 94 } 95 96 bool RS::initDispatch(int targetApi) { 97 pthread_mutex_lock(&gInitMutex); 98 if (gInitError) { 99 goto error; 100 } else if (gInitialized) { 101 pthread_mutex_unlock(&gInitMutex); 102 return true; 103 } 104 105 RS::dispatch = new dispatchTable; 106 107 // Attempt to load libRS, load libRSSupport on failure. 108 // If property is set, proceed directly to libRSSupport. 109 if (getProp("debug.rs.forcecompat") == 0) { 110 usingNative = loadSO("libRS.so", targetApi); 111 } 112 if (usingNative == false) { 113 if (loadSO("libRSSupport.so", targetApi) == false) { 114 ALOGE("Failed to load libRS.so and libRSSupport.so"); 115 goto error; 116 } 117 } 118 119 gInitialized = true; 120 121 pthread_mutex_unlock(&gInitMutex); 122 return true; 123 124 error: 125 gInitError = 1; 126 pthread_mutex_unlock(&gInitMutex); 127 return false; 128 } 129 130 bool RS::init(const char * name, uint32_t flags, int targetApi) { 131 if (mInit) { 132 return true; 133 } 134 // When using default value 0, set targetApi to RS_VERSION, 135 // to preserve the behavior of existing apps. 136 if (targetApi == 0) { 137 targetApi = RS_VERSION; 138 } 139 140 if (initDispatch(targetApi) == false) { 141 ALOGE("Couldn't initialize dispatch table"); 142 return false; 143 } 144 145 uint32_t nameLen = strlen(name); 146 if (nameLen > PATH_MAX) { 147 ALOGE("The path to the cache directory is too long"); 148 return false; 149 } 150 memcpy(mCacheDir, name, nameLen); 151 // Add the null character even if the user does not. 152 mCacheDir[nameLen] = 0; 153 mCacheDirLen = nameLen + 1; 154 155 RsDevice device = RS::dispatch->DeviceCreate(); 156 if (device == 0) { 157 ALOGE("Device creation failed"); 158 return false; 159 } 160 161 if (flags & ~(RS_CONTEXT_SYNCHRONOUS | RS_CONTEXT_LOW_LATENCY | 162 RS_CONTEXT_LOW_POWER | RS_CONTEXT_WAIT_FOR_ATTACH)) { 163 ALOGE("Invalid flags passed"); 164 return false; 165 } 166 167 mContext = RS::dispatch->ContextCreate(device, 0, targetApi, RS_CONTEXT_TYPE_NORMAL, flags); 168 if (mContext == 0) { 169 ALOGE("Context creation failed"); 170 return false; 171 } 172 173 pid_t mNativeMessageThreadId; 174 175 int status = pthread_create(&mMessageThreadId, nullptr, threadProc, this); 176 if (status) { 177 ALOGE("Failed to start RS message thread."); 178 return false; 179 } 180 // Wait for the message thread to be active. 181 while (!mMessageRun) { 182 usleep(1000); 183 } 184 185 mInit = true; 186 187 return true; 188 } 189 190 void RS::throwError(RSError error, const char *errMsg) { 191 if (mCurrentError == RS_SUCCESS) { 192 mCurrentError = error; 193 ALOGE("RS CPP error: %s", errMsg); 194 } else { 195 ALOGE("RS CPP error (masked by previous error): %s", errMsg); 196 } 197 } 198 199 RSError RS::getError() { 200 return mCurrentError; 201 } 202 203 204 void * RS::threadProc(void *vrsc) { 205 RS *rs = static_cast<RS *>(vrsc); 206 size_t rbuf_size = 256; 207 void * rbuf = malloc(rbuf_size); 208 209 RS::dispatch->ContextInitToClient(rs->mContext); 210 rs->mMessageRun = true; 211 212 while (rs->mMessageRun) { 213 size_t receiveLen = 0; 214 uint32_t usrID = 0; 215 uint32_t subID = 0; 216 RsMessageToClientType r = RS::dispatch->ContextPeekMessage(rs->mContext, 217 &receiveLen, sizeof(receiveLen), 218 &usrID, sizeof(usrID)); 219 220 if (receiveLen >= rbuf_size) { 221 rbuf_size = receiveLen + 32; 222 rbuf = realloc(rbuf, rbuf_size); 223 } 224 if (!rbuf) { 225 ALOGE("RS::message handler realloc error %zu", rbuf_size); 226 // No clean way to recover now? 227 } 228 RS::dispatch->ContextGetMessage(rs->mContext, rbuf, rbuf_size, &receiveLen, sizeof(receiveLen), 229 &subID, sizeof(subID)); 230 231 switch(r) { 232 case RS_MESSAGE_TO_CLIENT_ERROR: 233 ALOGE("RS Error %s", (const char *)rbuf); 234 rs->throwError(RS_ERROR_RUNTIME_ERROR, "Error returned from runtime"); 235 if(rs->mMessageFunc != nullptr) { 236 rs->mErrorFunc(usrID, (const char *)rbuf); 237 } 238 break; 239 case RS_MESSAGE_TO_CLIENT_NONE: 240 case RS_MESSAGE_TO_CLIENT_EXCEPTION: 241 case RS_MESSAGE_TO_CLIENT_RESIZE: 242 /* 243 * Teardown. We want to avoid starving other threads during 244 * teardown by yielding until the next line in the destructor can 245 * execute to set mRun = false. Note that the FIFO sends an 246 * empty NONE message when it reaches its destructor. 247 */ 248 usleep(1000); 249 break; 250 case RS_MESSAGE_TO_CLIENT_USER: 251 if(rs->mMessageFunc != nullptr) { 252 rs->mMessageFunc(usrID, rbuf, receiveLen); 253 } else { 254 ALOGE("Received a message from the script with no message handler installed."); 255 } 256 break; 257 258 default: 259 ALOGE("RS unknown message type %i", r); 260 } 261 } 262 263 if (rbuf) { 264 free(rbuf); 265 } 266 ALOGV("RS Message thread exiting."); 267 return nullptr; 268 } 269 270 void RS::setErrorHandler(ErrorHandlerFunc_t func) { 271 mErrorFunc = func; 272 } 273 274 void RS::setMessageHandler(MessageHandlerFunc_t func) { 275 mMessageFunc = func; 276 } 277 278 void RS::finish() { 279 RS::dispatch->ContextFinish(mContext); 280 } 281