| /* |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "tuningfork/tuningfork.h" |
| #include "tuningfork/tuningfork_extra.h" |
| #include "protobuf_util.h" |
| #include "swappy/swappyGL.h" |
| #include "swappy/swappyGL_extra.h" |
| #include "lite/tuningfork.pb.h" |
| #include "lite/dev_tuningfork.pb.h" |
| #include <sstream> |
| #include <condition_variable> |
| #include <jni.h> |
| #include <unistd.h> |
| #include <android/native_window_jni.h> |
| |
| #define LOG_TAG "insightsdemo" |
| #include "Log.h" |
| #include "Renderer.h" |
| |
| #include <random> |
| |
| using ::com::google::tuningfork::FidelityParams; |
| using ::com::google::tuningfork::Settings; |
| using ::com::google::tuningfork::Annotation; |
| |
| namespace proto_tf = com::google::tuningfork; |
| namespace tf = tuningfork; |
| using namespace samples; |
| |
| bool swappy_enabled = false; |
| |
| namespace { |
| |
| constexpr TuningFork_InstrumentKey TFTICK_CHOREOGRAPHER = TFTICK_USERDEFINED_BASE; |
| |
| std::string ReplaceReturns(const std::string& s) { |
| std::string r = s; |
| for (int i=0; i<r.length(); ++i) { |
| if (r[i]=='\n') r[i] = ','; |
| if (r[i]=='\r') r[i] = ' '; |
| } |
| return r; |
| } |
| |
| void SplitAndLog(const std::string& s) { |
| std::stringstream to_log; |
| const int max_line_len = 300; |
| int nparts = s.length()/max_line_len+1; |
| for (int i=0;i<nparts;++i) { |
| std::stringstream msg; |
| msg << "(TGE" << (i+1) << '/' << nparts << ')'; |
| msg << s.substr(i*max_line_len, max_line_len); |
| ALOGI("%s", msg.str().c_str()); |
| } |
| } |
| |
| void UploadCallback(const char *tuningfork_log_event, size_t n) { |
| if(tuningfork_log_event) { |
| std::string evt(tuningfork_log_event, n); |
| SplitAndLog(evt); |
| } |
| } |
| |
| static bool sLoading = false; |
| static int sLevel = proto_tf::LEVEL_1; |
| extern "C" |
| void SetAnnotations() { |
| if(proto_tf::Level_IsValid(sLevel)) { |
| Annotation a; |
| a.set_level((proto_tf::Level)sLevel); |
| auto ser = tf::TuningFork_CProtobufSerialization_Alloc(a); |
| if (TuningFork_setCurrentAnnotation(&ser)!=TUNINGFORK_ERROR_OK) { |
| ALOGW("Bad annotation"); |
| } |
| TuningFork_CProtobufSerialization_free(&ser); |
| } |
| } |
| |
| std::mutex mutex; |
| std::condition_variable cv; |
| bool setFPs = false; |
| extern "C" void FidelityParamsCallback(const TuningFork_CProtobufSerialization* params) { |
| FidelityParams p; |
| // Set default values |
| p.set_num_spheres(10); |
| p.set_tesselation_percent(30); |
| std::vector<uint8_t> params_ser(params->bytes, params->bytes + params->size); |
| tf::Deserialize(params_ser, p); |
| std::string s = p.DebugString(); |
| ALOGI("Using FidelityParams: %s", ReplaceReturns(s).c_str()); |
| int nSpheres = p.num_spheres(); |
| int tesselation = p.tesselation_percent(); |
| Renderer::getInstance()->setQuality(nSpheres, tesselation); |
| setFPs = true; |
| cv.notify_one(); |
| } |
| |
| void WaitForFidelityParams() { |
| std::unique_lock<std::mutex> lock(mutex); |
| cv.wait(lock, []{ return setFPs;}); |
| } |
| |
| std::thread tf_thread; |
| jobject tf_activity; |
| |
| void InitTf(JNIEnv* env, jobject activity) { |
| SwappyGL_init(env, activity); |
| swappy_enabled = SwappyGL_isEnabled(); |
| TuningFork_Settings settings {}; |
| if (swappy_enabled) { |
| settings.swappy_tracer_fn = &SwappyGL_injectTracer; |
| settings.swappy_version = Swappy_version(); |
| } |
| settings.fidelity_params_callback = FidelityParamsCallback; |
| #ifndef NDEBUG |
| settings.endpoint_uri_override = "http://localhost:9000"; |
| #endif |
| // This overrides the value in default_fidelity_parameters_filename |
| // in tuningfork_settings, if it is there. |
| TuningFork_CProtobufSerialization fps = {}; |
| const char* filename = "dev_tuningfork_fidelityparams_3.bin"; |
| if (TuningFork_findFidelityParamsInApk(env, activity, filename, &fps) |
| == TUNINGFORK_ERROR_OK) |
| settings.training_fidelity_params = &fps; |
| else |
| ALOGE("Couldn't load fidelity params from %s", filename); |
| |
| TuningFork_ErrorCode err = TuningFork_init(&settings, env, activity); |
| if (err==TUNINGFORK_ERROR_OK) { |
| TuningFork_reportLifecycleEvent(TUNINGFORK_STATE_ONCREATE); |
| TuningFork_setUploadCallback(UploadCallback); |
| SetAnnotations(); |
| TuningFork_enableMemoryRecording(true); |
| } else { |
| ALOGW("Error initializing TuningFork: %d", err); |
| } |
| // Free any fidelity params we got from the APK |
| TuningFork_CProtobufSerialization_free(&fps); |
| |
| // If we don't wait for fidelity params here, the download thread will set them after we |
| // have already started rendering with a different set of parameters. |
| // In a real game, we'd initialize all the other assets before waiting. |
| WaitForFidelityParams(); |
| |
| } |
| |
| void InitTfFromNewThread(JavaVM* vm) { |
| JNIEnv *env; |
| int status = vm->AttachCurrentThread(&env, NULL); |
| InitTf(env, tf_activity); |
| vm->DetachCurrentThread(); |
| } |
| } // anonymous namespace |
| |
| extern "C" { |
| |
| // initFromNewThread parameter is for testing |
| JNIEXPORT void JNICALL |
| Java_com_tuningfork_insightsdemo_TFTestActivity_initTuningFork( |
| JNIEnv *env, jobject activity, jboolean initFromNewThread) { |
| if(initFromNewThread) { |
| tf_activity = env->NewGlobalRef(activity); |
| JavaVM* vm; |
| env->GetJavaVM(&vm); |
| tf_thread = std::thread(InitTfFromNewThread, vm); |
| } else { |
| InitTf(env, activity); |
| } |
| } |
| |
| static TuningFork_LoadingEventHandle inter_level_loading_handle; |
| static TuningFork_LoadingTimeMetadata inter_level_loading_metadata; |
| JNIEXPORT void JNICALL |
| Java_com_tuningfork_insightsdemo_TFTestActivity_onChoreographer(JNIEnv */*env*/, jclass clz, |
| jlong /*frameTimeNanos*/) { |
| TuningFork_frameTick(TFTICK_CHOREOGRAPHER); |
| // Switch levels and loading state according to the number of ticks we've had. |
| constexpr int COUNT_NEXT_LEVEL_START_LOADING = 80; |
| constexpr int COUNT_NEXT_LEVEL_STOP_LOADING = 90; |
| static int tick_count = 0; |
| ++tick_count; |
| if(tick_count>=COUNT_NEXT_LEVEL_START_LOADING) { |
| if(tick_count>=COUNT_NEXT_LEVEL_STOP_LOADING) { |
| // Loading finished |
| TuningFork_stopRecordingLoadingTime(inter_level_loading_handle); |
| sLoading = false; |
| tick_count = 0; |
| } |
| else { |
| if (!sLoading) { |
| // Loading next level |
| sLoading = true; |
| Annotation a; |
| ++sLevel; |
| if (sLevel > proto_tf::Level_MAX) sLevel = proto_tf::LEVEL_1; |
| a.set_level((proto_tf::Level) sLevel); |
| auto ser = tf::TuningFork_CProtobufSerialization_Alloc(a); |
| inter_level_loading_metadata.state = |
| TuningFork_LoadingTimeMetadata::LoadingState::INTER_LEVEL; |
| inter_level_loading_metadata.network_latency_ns = 1234567; |
| TuningFork_startRecordingLoadingTime(&inter_level_loading_metadata, |
| sizeof(TuningFork_LoadingTimeMetadata), |
| &ser, |
| &inter_level_loading_handle); |
| TuningFork_CProtobufSerialization_free(&ser); |
| } |
| } |
| SetAnnotations(); |
| } |
| } |
| JNIEXPORT void JNICALL |
| Java_com_tuningfork_insightsdemo_TFTestActivity_resize(JNIEnv *env, jclass /*clz*/, jobject surface, |
| jint width, jint height) { |
| ANativeWindow *window = ANativeWindow_fromSurface(env, surface); |
| Renderer::getInstance()->setWindow(window, |
| static_cast<int32_t>(width), |
| static_cast<int32_t>(height)); |
| } |
| JNIEXPORT void JNICALL |
| Java_com_tuningfork_insightsdemo_TFTestActivity_clearSurface(JNIEnv */*env*/, jclass /*clz*/ ) { |
| Renderer::getInstance()->setWindow(nullptr, 0, 0); |
| } |
| JNIEXPORT void JNICALL |
| Java_com_tuningfork_insightsdemo_TFTestActivity_start(JNIEnv */*env*/, jclass /*clz*/ ) { |
| TuningFork_reportLifecycleEvent(TUNINGFORK_STATE_ONSTART); |
| Renderer::getInstance()->start(); |
| } |
| JNIEXPORT void JNICALL |
| Java_com_tuningfork_insightsdemo_TFTestActivity_stop(JNIEnv */*env*/, jclass /*clz*/ ) { |
| TuningFork_reportLifecycleEvent(TUNINGFORK_STATE_ONSTOP); |
| Renderer::getInstance()->stop(); |
| // Call flush here to upload any histograms when the app goes to the background. |
| auto ret = TuningFork_flush(); |
| ALOGI("TuningFork_flush returned %d", ret); |
| } |
| |
| JNIEXPORT void JNICALL |
| Java_com_tuningfork_insightsdemo_TFTestActivity_destroy(JNIEnv */*env*/, jclass /*clz*/ ) { |
| TuningFork_reportLifecycleEvent(TUNINGFORK_STATE_ONDESTROY); |
| TuningFork_destroy(); |
| } |
| |
| JNIEXPORT void JNICALL |
| Java_com_tuningfork_insightsdemo_TFTestActivity_raiseSignal(JNIEnv * env, jclass clz, jint signal) { |
| std::stringstream ss; |
| ss << std::this_thread::get_id(); |
| ALOGI("raiseSignal %d: [pid: %d], [tid: %d], [thread_id: %s])", |
| signal, getpid(), gettid(), ss.str().c_str()); |
| raise(signal); |
| } |
| |
| JNIEXPORT void JNICALL |
| Java_com_tuningfork_insightsdemo_TFTestActivity_setFidelityParameters(JNIEnv * env, jclass clz) { |
| // Simulate the user changing quality settings in the game |
| static std::mt19937 gen; |
| static std::uniform_int_distribution<> dis(1,10); |
| static std::uniform_int_distribution<> dis2(1,30); |
| FidelityParams p; |
| p.set_num_spheres(dis(gen)); |
| p.set_tesselation_percent(dis2(gen)); |
| auto params = tf::TuningFork_CProtobufSerialization_Alloc(p); |
| TuningFork_setFidelityParameters(¶ms); |
| FidelityParamsCallback(¶ms); |
| TuningFork_CProtobufSerialization_free(¶ms); |
| } |
| |
| JNIEXPORT jlong JNICALL |
| Java_com_tuningfork_insightsdemo_TFTestActivity_nGetTuningforkVersion(JNIEnv *env, jclass /*clz*/) { |
| return TUNINGFORK_MAJOR_VERSION*10000L |
| + TUNINGFORK_MINOR_VERSION*100L |
| + TUNINGFORK_BUGFIX_VERSION; |
| } |
| |
| } |