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 28using android::RSC::RS; 29using android::RSC::RSError; 30 31bool RS::gInitialized = false; 32bool RS::usingNative = false; 33pthread_mutex_t RS::gInitMutex = PTHREAD_MUTEX_INITIALIZER; 34dispatchTable* RS::dispatch = nullptr; 35static int gInitError = 0; 36 37RS::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 49RS::~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 66bool 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. 72static 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 86static 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 96bool 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 130bool 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 190void 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 199RSError RS::getError() { 200 return mCurrentError; 201} 202 203 204void * 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 void *tmpBuf = realloc(rbuf, rbuf_size); 223 if (tmpBuf) { 224 rbuf = tmpBuf; 225 } else { 226 free(rbuf); 227 rbuf = NULL; 228 } 229 } 230 if (!rbuf) { 231 ALOGE("RS::message handler realloc error %zu", rbuf_size); 232 // No clean way to recover now? 233 } 234 RS::dispatch->ContextGetMessage(rs->mContext, rbuf, rbuf_size, &receiveLen, sizeof(receiveLen), 235 &subID, sizeof(subID)); 236 237 switch(r) { 238 case RS_MESSAGE_TO_CLIENT_ERROR: 239 ALOGE("RS Error %s", (const char *)rbuf); 240 rs->throwError(RS_ERROR_RUNTIME_ERROR, "Error returned from runtime"); 241 if(rs->mMessageFunc != nullptr) { 242 rs->mErrorFunc(usrID, (const char *)rbuf); 243 } 244 break; 245 case RS_MESSAGE_TO_CLIENT_NONE: 246 case RS_MESSAGE_TO_CLIENT_EXCEPTION: 247 case RS_MESSAGE_TO_CLIENT_RESIZE: 248 /* 249 * Teardown. We want to avoid starving other threads during 250 * teardown by yielding until the next line in the destructor can 251 * execute to set mRun = false. Note that the FIFO sends an 252 * empty NONE message when it reaches its destructor. 253 */ 254 usleep(1000); 255 break; 256 case RS_MESSAGE_TO_CLIENT_USER: 257 if(rs->mMessageFunc != nullptr) { 258 rs->mMessageFunc(usrID, rbuf, receiveLen); 259 } else { 260 ALOGE("Received a message from the script with no message handler installed."); 261 } 262 break; 263 264 default: 265 ALOGE("RS unknown message type %i", r); 266 } 267 } 268 269 if (rbuf) { 270 free(rbuf); 271 } 272 ALOGV("RS Message thread exiting."); 273 return nullptr; 274} 275 276void RS::setErrorHandler(ErrorHandlerFunc_t func) { 277 mErrorFunc = func; 278} 279 280void RS::setMessageHandler(MessageHandlerFunc_t func) { 281 mMessageFunc = func; 282} 283 284void RS::finish() { 285 RS::dispatch->ContextFinish(mContext); 286} 287