• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
DynamicSwappy(const char * libraryName)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     }
~DynamicSwappy()56     ~DynamicSwappy() {
57         if(lib_) dlclose(lib_);
58     }
injectTracer(const SwappyTracer * tracer) const59     void injectTracer(const SwappyTracer* tracer) const {
60         if(inject_tracer_)
61             inject_tracer_(tracer);
62     }
valid() const63     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:
SwappyTuningFork(const CProtobufSerialization & settings_ser,JNIEnv * env,jobject activity,VoidCallback cbk,const char * libName)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     }
valid() const87     bool valid() const { return swappy_.valid(); }
88 
89     // Swappy trace callbacks
swappyStartFrameCallback(void * userPtr,int,long)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     }
swappyPreWaitCallback(void * userPtr)96     static void swappyPreWaitCallback(void* userPtr) {
97         SwappyTuningFork* _this = (SwappyTuningFork*)userPtr;
98         _this->waitTraceHandle_ = TuningFork_startTrace(TFTICK_WAIT_TIME);
99     }
swappyPostWaitCallback(void * userPtr)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     }
swappyPreSwapBuffersCallback(void * userPtr)108     static void swappyPreSwapBuffersCallback(void* userPtr) {
109         SwappyTuningFork* _this = (SwappyTuningFork*)userPtr;
110         _this->swapTraceHandle_ = TuningFork_startTrace(TFTICK_SWAP_TIME);
111     }
swappyPostSwapBuffersCallback(void * userPtr,long)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 
Init(const CProtobufSerialization * settings,JNIEnv * env,jobject activity,const char * libName,void (* frame_callback)())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.
GetSettingsSerialization(JNIEnv * env,jobject activity,CProtobufSerialization & settings_ser)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.
GetFidelityParamsSerialization(JNIEnv * env,jobject activity,CProtobufSerialization * fps_ser,int * fp_count)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.
GetSavedFileName(JNIEnv * env,jobject activity,std::string & name)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.
GetSavedFidelityParams(JNIEnv * env,jobject activity,CProtobufSerialization * params)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.
SaveFidelityParams(JNIEnv * env,jobject activity,const CProtobufSerialization * params)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.
SavedFidelityParamsFileExists(JNIEnv * env,jobject activity)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
StartFidelityParamDownloadThread(JNIEnv * env,jobject activity,const CProtobufSerialization & defaultParams,ProtoCallback fidelity_params_callback,int initialTimeoutMs,int ultimateTimeoutMs)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                                                      &params, waitTimeMs)) {
268                     ALOGI("Got fidelity params from server");
269                     SaveFidelityParams(newEnv, newActivity, &params);
270                     CProtobufSerialization_Free(&defaultParams);
271                     fidelity_params_callback(&params);
272                     CProtobufSerialization_Free(&params);
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 
TuningFork_findSettingsInAPK(JNIEnv * env,jobject activity,CProtobufSerialization * settings_ser)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 }
TuningFork_findFidelityParamsInAPK(JNIEnv * env,jobject activity,CProtobufSerialization * fps,int * fp_count)306 void TuningFork_findFidelityParamsInAPK(JNIEnv* env, jobject activity,
307                                         CProtobufSerialization* fps, int* fp_count) {
308     GetFidelityParamsSerialization(env, activity, fps, fp_count);
309 }
310 
TuningFork_initWithSwappy(const CProtobufSerialization * settings,JNIEnv * env,jobject activity,const char * libraryName,VoidCallback frame_callback)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 
TuningFork_setUploadCallback(void (* cbk)(const CProtobufSerialization *))317 void TuningFork_setUploadCallback(void(*cbk)(const CProtobufSerialization*)) {
318     tuningfork::SetUploadCallback(cbk);
319 }
320 
TuningFork_initFromAssetsWithSwappy(JNIEnv * env,jobject activity,const char * libraryName,VoidCallback frame_callback,int fpFileNum,ProtoCallback fidelity_params_callback,int initialTimeoutMs,int ultimateTimeoutMs)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