1 2 #include "tuningfork/tuningfork_extra.h" 3 #include "tuningfork/protobuf_util.h" 4 #include "tuningfork_internal.h" 5 #include "tuningfork_utils.h" 6 7 #include <cinttypes> 8 #include <dlfcn.h> 9 #include <memory> 10 #include <vector> 11 #include <cstdlib> 12 #include <sstream> 13 #include <thread> 14 #include <fstream> 15 #include <mutex> 16 17 #define LOG_TAG "TuningFork" 18 #include "Log.h" 19 #include "swappy/swappy_extra.h" 20 21 #include <android/asset_manager_jni.h> 22 #include <jni.h> 23 24 using namespace tuningfork; 25 26 namespace { 27 28 using PFN_Swappy_initTracer = void (*)(const SwappyTracer* tracer); 29 30 constexpr TFInstrumentKey TFTICK_WAIT_TIME = 2; 31 constexpr TFInstrumentKey TFTICK_SWAP_TIME = 3; 32 33 class DynamicSwappy { 34 typedef void* Handle; 35 Handle lib_; 36 PFN_Swappy_initTracer inject_tracer_; 37 public: 38 DynamicSwappy(const char* libraryName) { 39 static char defaultLibNames[][20] = {"libgamesdk.so", "libswappy.so", "libunity.so"}; 40 std::vector<const char*> libNames = { 41 libraryName, NULL, defaultLibNames[0], defaultLibNames[1], defaultLibNames[2]}; 42 for(auto libName: libNames) { 43 lib_ = dlopen(libName, RTLD_NOW); 44 if( lib_ ) { 45 inject_tracer_ = (PFN_Swappy_initTracer)dlsym(lib_, "Swappy_injectTracer"); 46 if(inject_tracer_) { 47 return; 48 } else { 49 dlclose(lib_); 50 } 51 } 52 } 53 ALOGW("Couldn't find Swappy_injectTracer"); 54 lib_ = nullptr; 55 } 56 ~DynamicSwappy() { 57 if(lib_) dlclose(lib_); 58 } 59 void injectTracer(const SwappyTracer* tracer) const { 60 if(inject_tracer_) 61 inject_tracer_(tracer); 62 } 63 bool valid() const { return lib_ != nullptr; } 64 }; 65 66 class SwappyTuningFork { 67 DynamicSwappy swappy_; 68 SwappyTracer trace_; 69 VoidCallback frame_callback_; 70 TFTraceHandle waitTraceHandle_ = 0; 71 TFTraceHandle swapTraceHandle_ = 0; 72 public: 73 SwappyTuningFork(const CProtobufSerialization& settings_ser, JNIEnv* env, jobject activity, 74 VoidCallback cbk, const char* libName) 75 : swappy_(libName), trace_({}), frame_callback_(cbk) { 76 trace_.startFrame = swappyStartFrameCallback; 77 trace_.preWait = swappyPreWaitCallback; 78 trace_.postWait = swappyPostWaitCallback; 79 trace_.preSwapBuffers = swappyPreSwapBuffersCallback; 80 trace_.postSwapBuffers = swappyPostSwapBuffersCallback; 81 trace_.userData = this; 82 if(swappy_.valid()) { 83 TuningFork_init(&settings_ser, env, activity); 84 swappy_.injectTracer(&trace_); 85 } 86 } 87 bool valid() const { return swappy_.valid(); } 88 89 // Swappy trace callbacks 90 static void swappyStartFrameCallback(void* userPtr, int /*currentFrame*/, 91 long /*currentFrameTimeStampMs*/) { 92 SwappyTuningFork* _this = (SwappyTuningFork*)userPtr; 93 _this->frame_callback_(); 94 TuningFork_frameTick(TFTICK_SYSCPU); 95 } 96 static void swappyPreWaitCallback(void* userPtr) { 97 SwappyTuningFork* _this = (SwappyTuningFork*)userPtr; 98 _this->waitTraceHandle_ = TuningFork_startTrace(TFTICK_WAIT_TIME); 99 } 100 static void swappyPostWaitCallback(void* userPtr) { 101 SwappyTuningFork *_this = (SwappyTuningFork *) userPtr; 102 if (_this->waitTraceHandle_) { 103 TuningFork_endTrace(_this->waitTraceHandle_); 104 _this->waitTraceHandle_ = 0; 105 } 106 TuningFork_frameTick(TFTICK_SYSGPU); 107 } 108 static void swappyPreSwapBuffersCallback(void* userPtr) { 109 SwappyTuningFork* _this = (SwappyTuningFork*)userPtr; 110 _this->swapTraceHandle_ = TuningFork_startTrace(TFTICK_SWAP_TIME); 111 } 112 static void swappyPostSwapBuffersCallback(void* userPtr, long /*desiredPresentationTimeMs*/) { 113 SwappyTuningFork *_this = (SwappyTuningFork *) userPtr; 114 if (_this->swapTraceHandle_) { 115 TuningFork_endTrace(_this->swapTraceHandle_); 116 _this->swapTraceHandle_ = 0; 117 } 118 } 119 // Static methods 120 static std::unique_ptr<SwappyTuningFork> s_instance_; 121 122 static bool Init(const CProtobufSerialization* settings, JNIEnv* env, 123 jobject activity, const char* libName, void (*frame_callback)()) { 124 s_instance_ = std::unique_ptr<SwappyTuningFork>( 125 new SwappyTuningFork(*settings, env, activity, frame_callback, libName)); 126 return s_instance_->valid(); 127 } 128 }; 129 130 std::unique_ptr<SwappyTuningFork> SwappyTuningFork::s_instance_; 131 132 // Gets the serialized settings from the APK. 133 // Returns false if there was an error. 134 bool GetSettingsSerialization(JNIEnv* env, jobject activity, 135 CProtobufSerialization& settings_ser) { 136 auto asset = apk_utils::GetAsset(env, activity, "tuningfork/tuningfork_settings.bin"); 137 if (asset == nullptr ) 138 return false; 139 ALOGI("Got settings from tuningfork/tuningfork_settings.bin"); 140 // Get serialized settings from assets 141 uint64_t size = AAsset_getLength64(asset); 142 settings_ser.bytes = (uint8_t*)::malloc(size); 143 memcpy(settings_ser.bytes, AAsset_getBuffer(asset), size); 144 settings_ser.size = size; 145 settings_ser.dealloc = ::free; 146 AAsset_close(asset); 147 return true; 148 } 149 150 // Gets the serialized fidelity params from the APK. 151 // Call this function once with fps_ser=NULL to get the count of files present, 152 // then allocate an array of CProtobufSerializations and pass this as fps_ser 153 // to a second call. 154 void GetFidelityParamsSerialization(JNIEnv* env, jobject activity, 155 CProtobufSerialization* fps_ser, 156 int* fp_count) { 157 std::vector<AAsset*> fps; 158 for( int i=1; i<16; ++i ) { 159 std::stringstream name; 160 name << "tuningfork/dev_tuningfork_fidelityparams_" << i << ".bin"; 161 auto fp = apk_utils::GetAsset(env, activity, name.str().c_str()); 162 if ( fp == nullptr ) break; 163 fps.push_back(fp); 164 } 165 *fp_count = fps.size(); 166 if( fps_ser==nullptr ) 167 return; 168 for(int i=0; i<*fp_count; ++i) { 169 // Get serialized FidelityParams from assets 170 AAsset* asset = fps[i]; 171 CProtobufSerialization& fp_ser = fps_ser[i]; 172 uint64_t size = AAsset_getLength64(asset); 173 fp_ser.bytes = (uint8_t*)::malloc(size); 174 memcpy(fp_ser.bytes, AAsset_getBuffer(asset), size); 175 fp_ser.size = size; 176 fp_ser.dealloc = ::free; 177 AAsset_close(asset); 178 } 179 } 180 181 // Get the name of the tuning fork save file. Returns true if the directory 182 // for the file exists and false on error. 183 bool GetSavedFileName(JNIEnv* env, jobject activity, std::string& name) { 184 185 // Create tuningfork/version folder if it doesn't exist 186 std::stringstream tf_path_str; 187 tf_path_str << file_utils::GetAppCacheDir(env, activity) << "/tuningfork"; 188 if (!file_utils::CheckAndCreateDir(tf_path_str.str())) { 189 return false; 190 } 191 tf_path_str << "/V" << apk_utils::GetVersionCode(env, activity); 192 if (!file_utils::CheckAndCreateDir(tf_path_str.str())) { 193 return false; 194 } 195 tf_path_str << "/saved_fp.bin"; 196 name = tf_path_str.str(); 197 return true; 198 } 199 200 // Get a previously save fidelity param serialization. 201 bool GetSavedFidelityParams(JNIEnv* env, jobject activity, CProtobufSerialization* params) { 202 std::string save_filename; 203 if (GetSavedFileName(env, activity, save_filename)) { 204 std::ifstream save_file(save_filename, std::ios::binary); 205 if (save_file.good()) { 206 save_file.seekg(0, std::ios::end); 207 params->size = save_file.tellg(); 208 params->bytes = (uint8_t*)::malloc(params->size); 209 params->dealloc = ::free; 210 save_file.seekg(0, std::ios::beg); 211 save_file.read((char*)params->bytes, params->size); 212 ALOGI("Loaded fps from %s (%zu bytes)", save_filename.c_str(), params->size); 213 return true; 214 } 215 ALOGI("Couldn't load fps from %s", save_filename.c_str()); 216 } 217 return false; 218 } 219 220 // Save fidelity params to the save file. 221 bool SaveFidelityParams(JNIEnv* env, jobject activity, const CProtobufSerialization* params) { 222 std::string save_filename; 223 if (GetSavedFileName(env, activity, save_filename)) { 224 std::ofstream save_file(save_filename, std::ios::binary); 225 if (save_file.good()) { 226 save_file.write((const char*)params->bytes, params->size); 227 ALOGI("Saved fps to %s (%zu bytes)", save_filename.c_str(), params->size); 228 return true; 229 } 230 ALOGI("Couldn't save fps to %s", save_filename.c_str()); 231 } 232 return false; 233 } 234 235 // Check if we have saved fidelity params. 236 bool SavedFidelityParamsFileExists(JNIEnv* env, jobject activity) { 237 std::string save_filename; 238 if (GetSavedFileName(env, activity, save_filename)) { 239 return file_utils::FileExists(save_filename); 240 } 241 return false; 242 } 243 244 // Download FPs on a separate thread 245 void StartFidelityParamDownloadThread(JNIEnv* env, jobject activity, 246 const CProtobufSerialization& defaultParams, 247 ProtoCallback fidelity_params_callback, 248 int initialTimeoutMs, int ultimateTimeoutMs) { 249 static std::mutex threadMutex; 250 std::lock_guard<std::mutex> lock(threadMutex); 251 static std::thread fpThread; 252 if (fpThread.joinable()) { 253 ALOGW("Fidelity param download thread already started"); 254 return; 255 } 256 JavaVM *vm; 257 env->GetJavaVM(&vm); 258 auto newActivity = env->NewGlobalRef(activity); 259 fpThread = std::thread([=](CProtobufSerialization defaultParams) { 260 CProtobufSerialization params = {}; 261 int waitTimeMs = initialTimeoutMs; 262 bool first_time = true; 263 JNIEnv *newEnv; 264 if (vm->AttachCurrentThread(&newEnv, NULL) == 0) { 265 while (true) { 266 if (TuningFork_getFidelityParameters(&defaultParams, 267 ¶ms, waitTimeMs)) { 268 ALOGI("Got fidelity params from server"); 269 SaveFidelityParams(newEnv, newActivity, ¶ms); 270 CProtobufSerialization_Free(&defaultParams); 271 fidelity_params_callback(¶ms); 272 CProtobufSerialization_Free(¶ms); 273 break; 274 } else { 275 ALOGI("Could not get fidelity params from server"); 276 if (first_time) { 277 fidelity_params_callback(&defaultParams); 278 first_time = false; 279 } 280 if (waitTimeMs > ultimateTimeoutMs) { 281 ALOGW("Not waiting any longer for fidelity params"); 282 CProtobufSerialization_Free(&defaultParams); 283 break; 284 } 285 waitTimeMs *= 2; // back off 286 } 287 } 288 newEnv->DeleteGlobalRef(newActivity); 289 vm->DetachCurrentThread(); 290 } 291 }, defaultParams); 292 } 293 294 } // anonymous namespace 295 296 extern "C" { 297 298 bool TuningFork_findSettingsInAPK(JNIEnv* env, jobject activity, 299 CProtobufSerialization* settings_ser) { 300 if(settings_ser) { 301 return GetSettingsSerialization(env, activity, *settings_ser); 302 } else { 303 return false; 304 } 305 } 306 void TuningFork_findFidelityParamsInAPK(JNIEnv* env, jobject activity, 307 CProtobufSerialization* fps, int* fp_count) { 308 GetFidelityParamsSerialization(env, activity, fps, fp_count); 309 } 310 311 bool TuningFork_initWithSwappy(const CProtobufSerialization* settings, JNIEnv* env, 312 jobject activity, const char* libraryName, 313 VoidCallback frame_callback) { 314 return SwappyTuningFork::Init(settings, env, activity, libraryName, frame_callback); 315 } 316 317 void TuningFork_setUploadCallback(void(*cbk)(const CProtobufSerialization*)) { 318 tuningfork::SetUploadCallback(cbk); 319 } 320 321 TFErrorCode TuningFork_initFromAssetsWithSwappy(JNIEnv* env, jobject activity, 322 const char* libraryName, 323 VoidCallback frame_callback, 324 int fpFileNum, 325 ProtoCallback fidelity_params_callback, 326 int initialTimeoutMs, int ultimateTimeoutMs) { 327 CProtobufSerialization ser; 328 if (!TuningFork_findSettingsInAPK(env, activity, &ser)) 329 return TFERROR_NO_SETTINGS; 330 if (!TuningFork_initWithSwappy(&ser, env, activity, libraryName, frame_callback)) 331 return TFERROR_NO_SWAPPY; 332 CProtobufSerialization defaultParams = {}; 333 // Special meaning for negative fpFileNum: don't load saved params, overwrite them instead 334 bool resetSavedFPs = fpFileNum<0; 335 fpFileNum = abs(fpFileNum); 336 // Use the saved params as default, if they exist 337 if (!resetSavedFPs && SavedFidelityParamsFileExists(env, activity)) { 338 GetSavedFidelityParams(env, activity, &defaultParams); 339 } else { 340 int nfps=0; 341 TuningFork_findFidelityParamsInAPK(env, activity, NULL, &nfps); 342 if (nfps>0) { 343 std::vector<CProtobufSerialization> fps(nfps); 344 TuningFork_findFidelityParamsInAPK(env, activity, fps.data(), &nfps); 345 int chosen = fpFileNum - 1; // File indices start at 1 346 for (int i=0;i<nfps;++i) { 347 if (i==chosen) { 348 defaultParams = fps[i]; 349 } else { 350 CProtobufSerialization_Free(&fps[i]); 351 } 352 } 353 if (chosen>=0 && chosen<nfps) { 354 ALOGI("Using params from dev_tuningfork_fidelityparams_%d.bin as default", 355 fpFileNum); 356 } else { 357 return TFERROR_INVALID_DEFAULT_FIDELITY_PARAMS; 358 } 359 } else { 360 return TFERROR_NO_FIDELITY_PARAMS; 361 } 362 // Save the default params 363 SaveFidelityParams(env, activity, &defaultParams); 364 } 365 StartFidelityParamDownloadThread(env, activity, defaultParams, fidelity_params_callback, 366 initialTimeoutMs, ultimateTimeoutMs); 367 return TFERROR_OK; 368 } 369 370 } // extern "C" 371